LCOV - code coverage report
Current view: top level - bgp - bgp_update_monitor.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 119 300 39.7 %
Date: 2026-09-21 02:11:39 Functions: 17 27 63.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "bgp/bgp_update_monitor.h"
       6             : 
       7             : #include "base/task_annotations.h"
       8             : #include "bgp/bgp_ribout_updates.h"
       9             : #include "bgp/bgp_table.h"
      10             : #include "bgp/bgp_update_queue.h"
      11             : 
      12         852 : RouteUpdatePtr::RouteUpdatePtr(RouteUpdate *rt_update) : rt_update_(rt_update) {
      13         852 : }
      14             : 
      15        1988 : RouteUpdatePtr::~RouteUpdatePtr() {
      16        1988 : }
      17             : 
      18         576 : RibUpdateMonitor::RibUpdateMonitor(RibOut *ribout, QueueVec *queue_vec)
      19         576 :     : ribout_(ribout), queue_vec_(queue_vec) {
      20         576 : }
      21             : 
      22             : //
      23             : // Helper routine for GetRouteStateAndDequeue to handle a RouteUpdate. Do
      24             : // nothing if it's a duplicate.
      25             : //
      26             : // Return a pointer to the DBState and update duplicate as appropriate.
      27             : //
      28           0 : DBState *RibUpdateMonitor::GetRouteUpdateAndDequeue(DBEntryBase *db_entry,
      29             :         RouteUpdate *rt_update, UpdateCmp cmp, bool *duplicate) {
      30           0 :     CHECK_CONCURRENCY("db::DBTable");
      31             : 
      32             :     // Compare desired state with current update. If the desired state
      33             :     // is a NOP just return without modifying the queue.
      34           0 :     if (rt_update->queue_id() == RibOutUpdates::QUPDATE) {
      35           0 :         *duplicate = cmp(rt_update);
      36           0 :         if (*duplicate) {
      37           0 :             return NULL;
      38             :         }
      39             :     }
      40             : 
      41             :     // Set the state on the DBEntryBase and remove the RouteUpdate from
      42             :     // the queue. Change the queue to QUPDATE since that's where we will
      43             :     // re-enqueue the RouteUpdate, if we do it.
      44           0 :     db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
      45           0 :     UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
      46           0 :     queue->Dequeue(rt_update);
      47           0 :     rt_update->set_queue_id(RibOutUpdates::QUPDATE);
      48           0 :     return rt_update;
      49             : }
      50             : 
      51             : //
      52             : // Helper routine for GetRouteStateAndDequeue to handle a UpdateList.
      53             : //
      54             : // Since an update operation overrides any join/refresh state, get rid of
      55             : // RouteUpdates that are not for QUPDATE.
      56             : //
      57             : // Note that we should NOT check for duplicate state in QUPDATE and bail.
      58             : // Doing so could result in a failure to remove pending RouteUpdates from
      59             : // QBULK which are no longer required.
      60             : //
      61             : // Return a pointer to the RouteUpdate for QUPDATE if it exists, else
      62             : // Return a pointer to new RouteState if the UpdateList has history, else
      63             : // Return NULL.
      64             : //
      65           0 : DBState *RibUpdateMonitor::GetUpdateListAndDequeue(DBEntryBase *db_entry,
      66             :         UpdateList *uplist) {
      67           0 :     CHECK_CONCURRENCY("db::DBTable");
      68             : 
      69             :     DBState *dbstate;
      70           0 :     RouteUpdate *rt_update = uplist->FindUpdate(RibOutUpdates::QUPDATE);
      71           0 :     if (rt_update) {
      72             :         // There's a RouteUpdate for QUPDATE. Remove it from the UpdateList
      73             :         // and move the history to it.  The entry will be reused as if that
      74             :         // was the DBState instead of the UpdateList.
      75           0 :         uplist->RemoveUpdate(rt_update);
      76           0 :         queue_vec_->at(rt_update->queue_id())->Dequeue(rt_update);
      77           0 :         uplist->MoveHistory(rt_update);
      78           0 :         dbstate = rt_update;
      79           0 :     } else if (!uplist->History()->empty()) {
      80             :         // There's no RouteUpdate for QUPDATE, but the history is not empty.
      81             :         // Move the history to a new RouteState.
      82           0 :         RouteState *rstate = new RouteState();
      83           0 :         uplist->MoveHistory(rstate);
      84           0 :         dbstate = rstate;
      85             :     } else {
      86             :         // There's no RouteUpdate for QUPDATE and the history is empty. We
      87             :         // can simply pretend that there was no DBState after we get rid
      88             :         // of the UpdateList.
      89           0 :         dbstate = NULL;
      90             :     }
      91             : 
      92             :     // Get rid of all other RouteUpdates and the UpdateList itself.  Note
      93             :     // that the RouteUpdate for QUPDATE, if any, has already been removed
      94             :     // from the UpdateList at this point.
      95           0 :     UpdateList::List *list = uplist->GetList();
      96           0 :     for (UpdateList::List::iterator iter = list->begin();
      97           0 :          iter != list->end(); iter++) {
      98           0 :         RouteUpdate *temp_rt_update = *iter;
      99           0 :         queue_vec_->at(temp_rt_update->queue_id())->Dequeue(temp_rt_update);
     100           0 :         delete temp_rt_update;
     101             :     }
     102             : 
     103             :     // Update the DBState and return it.
     104           0 :     if (dbstate) {
     105           0 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
     106             :     } else {
     107           0 :         db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     108             :     }
     109           0 :     return dbstate;
     110             : }
     111             : 
     112             : //
     113             : // Get the DBState for the DBEntryBase and dequeue it from an UpdateQueue
     114             : // if required.  The DBState could be a RouteState, a RouteUpdate or an
     115             : // UpdateList.  If it's a RouteState, it won't be on an UpdateQueue.  If
     116             : // it's a RouteUpdate or an UpdateList, see if it's a duplicate i.e. the
     117             : // desired state is the same as the currently advertised state.
     118             : //
     119             : // This routine is used by the export module to obtain exclusive access to
     120             : // the RouteUpdate or UpdateList. The access is exclusive by definition if
     121             : // there's no DBState or it's a RouteState.
     122             : //
     123             : // Return a pointer to the DBState and update duplicate as appropriate.
     124             : //
     125         648 : DBState *RibUpdateMonitor::GetDBStateAndDequeue(DBEntryBase *db_entry,
     126             :         UpdateCmp cmp, bool *duplicate) {
     127         648 :     CHECK_CONCURRENCY("db::DBTable");
     128             : 
     129         648 :     *duplicate = false;
     130             : 
     131             :     // Don't need to bother going through the monitor if there's no DBState.
     132             :     DBState *dbstate =
     133         648 :         db_entry->GetState(ribout_->table(), ribout_->listener_id());
     134         648 :     if (dbstate == NULL) {
     135         466 :         return dbstate;
     136             :     }
     137             : 
     138             :     // Go through the monitor and handle the DBState as appropriate.  Note
     139             :     // that we still need to check for the DBState being NULL as things may
     140             :     // have changed after the check made above.
     141             :     while (true) {
     142             :         // Get the DBState; bail if there's no existing state.
     143             :         DBState *dbstate =
     144         182 :             db_entry->GetState(ribout_->table(), ribout_->listener_id());
     145         182 :         if (dbstate == NULL) {
     146           0 :             return dbstate;
     147             :         }
     148             : 
     149             :         // Return if it's a RouteState.
     150         182 :         RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
     151         182 :         if (rstate != NULL) {
     152         182 :             return rstate;
     153             :         }
     154             : 
     155             :         // Handle the case where it's a RouteUpdate.
     156           0 :         RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     157           0 :         if (rt_update != NULL) {
     158           0 :             return GetRouteUpdateAndDequeue(db_entry, rt_update, cmp,
     159           0 :                     duplicate);
     160             :         }
     161             : 
     162             :         // Handle the case where it's a UpdateList.
     163           0 :         UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
     164             :         DBState *db_state;
     165           0 :         assert(uplist);
     166           0 :         db_state = GetUpdateListAndDequeue(db_entry, uplist);
     167           0 :         delete uplist;
     168           0 :         return db_state;
     169             :     }
     170             : 
     171             :     assert(false);
     172             :     return NULL;
     173             : }
     174             : 
     175             : //
     176             : // Helper routine for GetPeerSetCurrentAndScheduled to handle a RouteState.
     177             : // Go through all the AdvertiseInfo to build the bitset.
     178             : //
     179         136 : static void RouteStateCurrent(const RouteState *rstate,
     180             :         RibPeerSet *mcurrent) {
     181         136 :     CHECK_CONCURRENCY("db::DBTable");
     182             : 
     183         136 :     const AdvertiseSList &adv_slist = rstate->Advertised();
     184         136 :     for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
     185         544 :          iter != adv_slist->end(); ++iter) {
     186         272 :         mcurrent->Set(iter->bitset);
     187             :     }
     188         136 : }
     189             : 
     190             : //
     191             : // Helper routine for GetPeerSetCurrentAndScheduled to handle a RouteUpdate.
     192             : // Go through all the AdvertiseInfo and UpdateInfo to build the bitset.
     193             : //
     194             : // Do not consider the UpdateInfos if the given queue_id does not match the
     195             : // one in the RouteUpdate.  Note that QCOUNT is considered to be a wildcard.
     196             : //
     197          96 : static void RouteUpdateCurrentAndScheduled(const RouteUpdate *rt_update,
     198             :         int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
     199          96 :     CHECK_CONCURRENCY("db::DBTable");
     200             : 
     201          96 :     const AdvertiseSList &adv_slist = rt_update->History();
     202          96 :     for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
     203         192 :          iter != adv_slist->end(); ++iter) {
     204           0 :         mcurrent->Set(iter->bitset);
     205             :     }
     206             : 
     207          96 :     if (queue_id != RibOutUpdates::QCOUNT && queue_id != rt_update->queue_id())
     208           0 :         return;
     209             : 
     210          96 :     const UpdateInfoSList &uinfo_slist = rt_update->Updates();
     211          96 :     for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
     212         384 :          iter != uinfo_slist->end(); ++iter) {
     213         192 :         mscheduled->Set(iter->target);
     214             :     }
     215             : }
     216             : 
     217             : //
     218             : // Helper routine for GetPeerSetCurrentAndScheduled to handle a UpdateList.
     219             : // Go through all AdvertiseInfo in the UpdateList and then each RouteUpdate
     220             : // in the UpdateList to build the bitset.
     221             : //
     222           0 : static void UpdateListCurrentAndScheduled(const UpdateList *uplist,
     223             :         int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
     224           0 :     CHECK_CONCURRENCY("db::DBTable");
     225             : 
     226           0 :     const AdvertiseSList &adv_slist = uplist->History();
     227           0 :     for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
     228           0 :          iter != adv_slist->end(); ++iter) {
     229           0 :         mcurrent->Set(iter->bitset);
     230             :     }
     231             : 
     232           0 :     const UpdateList::List *list = uplist->GetList();
     233           0 :     for (UpdateList::List::const_iterator iter = list->begin();
     234           0 :          iter != list->end(); ++iter) {
     235           0 :         RouteUpdateCurrentAndScheduled(*iter, queue_id, mcurrent, mscheduled);
     236             :     }
     237           0 : }
     238             : 
     239             : //
     240             : // Build the RibPeerSet of peers that are currently advertising the given
     241             : // DBEntryBase or are scheduled to advertise it. The mcurrent parameter is
     242             : // filled with the contents of the advertised bitmask (history) while the
     243             : // mscheduled parameter is filled with the bitmask of updates in the queue.
     244             : //
     245             : // Return false if there's no associated DBState or if it's a RouteState
     246             : // i.e. there's no pending state to be advertised.
     247             : //
     248         478 : bool RibUpdateMonitor::GetPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
     249             :         int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
     250         478 :     CHECK_CONCURRENCY("db::DBTable");
     251             : 
     252             :     // Don't need to bother going through the monitor if there's no DBState.
     253             :     DBState *dbstate =
     254         478 :         db_entry->GetState(ribout_->table(), ribout_->listener_id());
     255         478 :     if (dbstate == NULL) {
     256         246 :         return false;
     257             :     }
     258             : 
     259             :     // Go through the monitor and handle the DBState as appropriate.  Note
     260             :     // that we still need to check for the DBState being NULL as things may
     261             :     // have changed after the check made above.
     262             :     while (true) {
     263             :         // Get the DBState; bail if there's no existing state.
     264         232 :         DBState *dbstate = db_entry->GetState(ribout_->table(),
     265         232 :                                          ribout_->listener_id());
     266         232 :         if (dbstate == NULL) {
     267           0 :             return false;
     268             :         }
     269             : 
     270             :         // Handle the case where it's a RouteState.
     271         232 :         RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
     272         232 :         if (rstate != NULL) {
     273         136 :             RouteStateCurrent(rstate, mcurrent);
     274         136 :             return false;
     275             :         }
     276             : 
     277             :         // Handle the case where it's a RouteUpdate.
     278          96 :         RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     279          96 :         if (rt_update != NULL) {
     280          96 :             RouteUpdateCurrentAndScheduled(rt_update, queue_id,
     281             :                     mcurrent, mscheduled);
     282          96 :             break;
     283             :         }
     284             : 
     285             :         // Handle the case where it's a UpdateList.
     286           0 :         UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
     287           0 :         if (uplist != NULL) {
     288           0 :             UpdateListCurrentAndScheduled(uplist, queue_id,
     289             :                     mcurrent, mscheduled);
     290           0 :             break;
     291             :         }
     292             : 
     293             :         // Unknown DBState.
     294           0 :         assert(false);
     295             :     }
     296             : 
     297          96 :     return true;
     298             : }
     299             : 
     300             : //
     301             : // Helper routine for MergeUpdate to handle case where the current DBState
     302             : // is a RouteState.
     303             : //
     304             : // Move the previous history from the RouteState to the RouteUpdate and
     305             : // enqueue it.
     306             : //
     307             : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
     308             : // the (RibOut, QueueId).
     309             : //
     310           0 : bool RibUpdateMonitor::RouteStateMergeUpdate(DBEntryBase *db_entry,
     311             :         RouteUpdate *rt_update, RouteState *rstate) {
     312           0 :     CHECK_CONCURRENCY("db::DBTable");
     313             : 
     314           0 :     rstate->MoveHistory(rt_update);
     315           0 :     delete rstate;
     316           0 :     return EnqueueUpdate(db_entry, rt_update);
     317             : }
     318             : 
     319             : //
     320             : // Helper routine for MergeUpdate to handle case where the current DBState
     321             : // is a RouteUpdate.
     322             : //
     323             : // If the new RouteUpdate is for the same queue as the current RouteUpdate,
     324             : // dequeue the current, merge in the UpdateInfo from the new and enqueue it
     325             : // again. Otherwise, build an UpdateList containing both RouteUpdates and
     326             : // enqueue the new RouteUpdate.
     327             : //
     328             : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
     329             : // the (RibOut, QueueId).
     330             : //
     331           0 : bool RibUpdateMonitor::RouteUpdateMergeUpdate(DBEntryBase *db_entry,
     332             :         RouteUpdate *rt_update, RouteUpdate *current_rt_update) {
     333           0 :     CHECK_CONCURRENCY("db::DBTable");
     334             : 
     335           0 :     if (current_rt_update->queue_id() == rt_update->queue_id()) {
     336           0 :         UpdateQueue *queue = queue_vec_->at(current_rt_update->queue_id());
     337           0 :         queue->Dequeue(current_rt_update);
     338           0 :         current_rt_update->MergeUpdateInfo(rt_update->Updates());
     339           0 :         assert(rt_update->Updates()->empty());
     340           0 :         delete rt_update;
     341           0 :         return EnqueueUpdate(db_entry, current_rt_update);
     342             :     } else {
     343           0 :         UpdateList *uplist = current_rt_update->MakeUpdateList();
     344           0 :         uplist->AddUpdate(rt_update);
     345           0 :         return EnqueueUpdate(db_entry, rt_update, uplist);
     346             :     }
     347             : }
     348             : 
     349             : //
     350             : // Helper routine for MergeUpdate to handle case where the current DBState
     351             : // is a UpdateList.
     352             : //
     353             : // Figure out if the UpdateList already has a RouteUpdate for this queue.
     354             : // If so, dequeue the existing RouteUpdate, merge the UpdateInfo from the
     355             : // new one and enqueue the existing RouteUpdate again. Otherwise, add the
     356             : // new RouteUpdate to the UpdateList and enqueue the RouteUpdate.
     357             : //
     358             : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
     359             : // the (RibOut, QueueId).
     360             : //
     361           0 : bool RibUpdateMonitor::UpdateListMergeUpdate(DBEntryBase *db_entry,
     362             :         RouteUpdate *rt_update, UpdateList *uplist) {
     363           0 :     CHECK_CONCURRENCY("db::DBTable");
     364             : 
     365           0 :     RouteUpdate *current_rt_update = uplist->FindUpdate(rt_update->queue_id());
     366           0 :     if (current_rt_update) {
     367           0 :         UpdateQueue *queue = queue_vec_->at(current_rt_update->queue_id());
     368           0 :         queue->Dequeue(current_rt_update);
     369           0 :         current_rt_update->MergeUpdateInfo(rt_update->Updates());
     370           0 :         assert(rt_update->Updates()->empty());
     371           0 :         delete rt_update;
     372           0 :         return EnqueueUpdate(db_entry, current_rt_update, uplist);
     373             :     } else {
     374           0 :         uplist->AddUpdate(rt_update);
     375           0 :         return EnqueueUpdate(db_entry, rt_update, uplist);
     376             :     }
     377             : }
     378             : 
     379             : //
     380             : // Take the desired state as represented by the RouteUpdate and merge it with
     381             : // any previously advertised state for the DBEntryBase. Note that the previous
     382             : // state could be for a different queue and/or other peers in the RibOut.
     383             : //
     384             : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
     385             : // the (RibOut, QueueId).
     386             : //
     387          96 : bool RibUpdateMonitor::MergeUpdate(DBEntryBase *db_entry,
     388             :         RouteUpdate *rt_update) {
     389          96 :     CHECK_CONCURRENCY("db::DBTable");
     390             : 
     391             :     // Go through the monitor and handle the DBState as necessary. Need
     392             :     // to use the monitor even if there's no DBState in order to protect
     393             :     // against race conditions which could result in an incorrect return
     394             :     // value.
     395             :     while (true) {
     396             :         DBState *dbstate =
     397          96 :             db_entry->GetState(ribout_->table(), ribout_->listener_id());
     398             : 
     399             :         // Handle the case where there's no DBState.  Simply add the new
     400             :         // RouteUpdate to the queue.
     401          96 :         if (dbstate == NULL) {
     402          96 :             return EnqueueUpdate(db_entry, rt_update);
     403             :         }
     404             : 
     405             :         // Handle the case where it's a RouteState.
     406           0 :         RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
     407           0 :         if (rstate != NULL) {
     408           0 :             return RouteStateMergeUpdate(db_entry, rt_update, rstate);
     409             :         }
     410             : 
     411             :         // Handle the case where it's a RouteUpdate.
     412           0 :         RouteUpdate *current_rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     413           0 :         if (current_rt_update != NULL) {
     414           0 :             return RouteUpdateMergeUpdate(db_entry, rt_update,
     415           0 :                     current_rt_update);
     416             :         }
     417             : 
     418             :         // Handle the case where it's a UpdateList.
     419           0 :         UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
     420           0 :         if (uplist != NULL) {
     421           0 :             return UpdateListMergeUpdate(db_entry, rt_update, uplist);
     422             :         }
     423             : 
     424             :         // Unknown DBState.
     425           0 :         assert(false);
     426             :     }
     427             : 
     428             :     assert(false);
     429             :     return false;
     430             : }
     431             : 
     432             : //
     433             : // Traipse through all the AdvertiseInfo elements in the list and clear the
     434             : // RibPeerSet. If the RibPeerSet in an element becomes empty, remove it from
     435             : // the list container and get rid of the element.
     436             : //
     437          68 : void RibUpdateMonitor::AdvertiseSListClearBits(AdvertiseSList &adv_slist,
     438             :         const RibPeerSet &clear) {
     439          68 :     CHECK_CONCURRENCY("db::DBTable");
     440             : 
     441          68 :     for (AdvertiseSList::List::iterator iter = adv_slist->begin();
     442         272 :          iter != adv_slist->end(); ) {
     443          68 :         iter->bitset.Reset(clear);
     444          68 :         if (iter->bitset.empty()) {
     445         204 :             iter = adv_slist->erase_and_dispose(iter, AdvertiseInfoDisposer());
     446             :         } else {
     447           0 :             iter++;
     448             :         }
     449             :     }
     450          68 : }
     451             : 
     452             : //
     453             : // Traipse through all the UpdateInfo elements in the list and clear the
     454             : // RibPeerSet. If the RibPeerSet in an element becomes empty, remove it
     455             : // from the set and list containers and get rid of the element.
     456             : //
     457           0 : void RibUpdateMonitor::UpdateInfoSListClearBits(UpdateInfoSList &uinfo_slist,
     458             :         const RibPeerSet &clear) {
     459           0 :     CHECK_CONCURRENCY("db::DBTable");
     460             : 
     461           0 :     for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin();
     462           0 :          iter != uinfo_slist->end(); ) {
     463           0 :         iter->target.Reset(clear);
     464           0 :         if (iter->target.empty()) {
     465           0 :             RouteUpdate *rt_update = iter->update;
     466           0 :             UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
     467           0 :             queue->AttrDequeue(iter.operator->());
     468           0 :             iter = uinfo_slist->erase_and_dispose(iter, UpdateInfoDisposer());
     469             :         } else {
     470           0 :             iter++;
     471             :         }
     472             :     }
     473           0 : }
     474             : 
     475             : //
     476             : // Helper routine for ClearPeerSetCurrentAndScheduled to handle case where
     477             : // the current DBState is a RouteState.
     478             : //
     479             : // Clean up the AdvertiseInfos corresponding to the peers in RibPeerSet.
     480             : //
     481          68 : void RibUpdateMonitor::RouteStateClearPeerSet(DBEntryBase *db_entry,
     482             :         RouteState *rstate, const RibPeerSet &mleave) {
     483          68 :     CHECK_CONCURRENCY("db::DBTable");
     484             : 
     485             :     // Clear the bits for each element in the AdvertiseSList.
     486          68 :     AdvertiseSListClearBits(rstate->Advertised(), mleave);
     487             : 
     488             :     // Get rid of the RouteState itself if it's empty.
     489          68 :     if (rstate->Advertised()->empty()) {
     490          68 :         db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     491          68 :         delete rstate;
     492             :     }
     493          68 : }
     494             : 
     495             : //
     496             : // Helper routine for ClearPeerSetCurrentAndScheduled to handle case where
     497             : // the current DBState is a RouteUpdate.
     498             : //
     499             : // Clean up the UpdateInfos corresponding to the peers in RibPeerSet.
     500             : //
     501             : // Return true if rt_update has to be deleted, false otherwise
     502             : //
     503           0 : bool RibUpdateMonitor::RouteUpdateClearPeerSet(DBEntryBase *db_entry,
     504             :             RouteUpdate *rt_update, const RibPeerSet &mleave) {
     505           0 :     CHECK_CONCURRENCY("db::DBTable");
     506             : 
     507             :     // Clear the bits for each element in the AdvertiseSList.
     508           0 :     AdvertiseSListClearBits(rt_update->History(), mleave);
     509             : 
     510             :     // Clear the bits for each element in the UpdateInfoSList.
     511           0 :     UpdateInfoSListClearBits(rt_update->Updates(), mleave);
     512             : 
     513             :     // Update the DBstate for the DBentry as appropriate.
     514           0 :     if (!rt_update->Updates()->empty()) {
     515             :         // There are more scheduled updates, do nothing.
     516           0 :         return false;
     517             :     }
     518             : 
     519           0 :     if (!rt_update->History()->empty()) {
     520             :         // No more scheduled updates but there are current updates. Dequeue
     521             :         // the RouteUpdate, move the history to a new RouteState and get rid
     522             :         // of the RouteUpdate.
     523           0 :         DequeueUpdate(rt_update);
     524           0 :         RouteState *rstate = new RouteState;
     525           0 :         rt_update->MoveHistory(rstate);
     526           0 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), rstate);
     527             :     } else {
     528             :         // No more scheduled or current updates.  Dequeue the RouteUpdate,
     529             :         // clear the state on the DBEntry and get rid of the RouteUpdate.
     530           0 :         DequeueUpdate(rt_update);
     531           0 :         db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     532             :     }
     533             : 
     534           0 :     return true;
     535             : }
     536             : 
     537             : //
     538             : // Helper routine for ClearPeerSetCurrentAndScheduled to handle case where
     539             : // the current DBState is a UpdateList.
     540             : //
     541             : // Return true if uplist has to be deleted, false otherwise
     542             : //
     543           0 : bool RibUpdateMonitor::UpdateListClearPeerSet(DBEntryBase *db_entry,
     544             :         UpdateList *uplist, const RibPeerSet &mleave) {
     545           0 :     CHECK_CONCURRENCY("db::DBTable");
     546             : 
     547           0 :     UpdateList::List *list = uplist->GetList();
     548             : 
     549             :     // Clear the bits for each element in the AdvertiseSList.
     550           0 :     AdvertiseSListClearBits(uplist->History(), mleave);
     551             : 
     552             :     // Clear the bits for each element in the UpdateInfoSList for each
     553             :     // RouteUpdate. If a RouteUpdate becomes empty as a result, get rid
     554             :     // of it.
     555           0 :     for (UpdateList::List::iterator iter = list->begin();
     556           0 :          iter != list->end(); ) {
     557           0 :         RouteUpdate *rt_update = *iter++;
     558           0 :         UpdateInfoSListClearBits(rt_update->Updates(), mleave);
     559           0 :         if (rt_update->Updates()->empty()) {
     560           0 :             uplist->RemoveUpdate(rt_update);
     561           0 :             DequeueUpdate(rt_update);
     562           0 :             delete rt_update;
     563             :         }
     564             :     }
     565             : 
     566             :     // Update the DBstate for the DBentry as appropriate.
     567           0 :     if (list->size() > 1) {
     568             :         // There's multiple RouteUpdates on the UpdateList, do nothing.
     569           0 :         return false;
     570           0 :     } else if (list->size() == 1) {
     571             :         // There's exactly 1 RouteUpdate on the UpdateList. Downgrade
     572             :         // the UpdateList to a RouteUpdate and get rid of UpdateList.
     573             :         // Note that the history will be moved to the RouteUpdate as
     574             :         // part of the downgrade.
     575           0 :         RouteUpdate *rt_update = uplist->MakeRouteUpdate();
     576           0 :         assert(rt_update);
     577           0 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
     578           0 :     } else if (!uplist->History()->empty()) {
     579             :         // There's no RouteUpdates on the UpdateList, but the history
     580             :         // is not empty.  Move the history to a new RouteState and get
     581             :         // rid of the UpdateList.
     582           0 :         RouteState *rstate = new RouteState;
     583           0 :         uplist->MoveHistory(rstate);
     584           0 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), rstate);
     585             :     } else {
     586             :         // There's no RouteUpdates on the UpdateList and the history is
     587             :         // empty. Clear state on the DBEntry and get rid of UpdateList.
     588           0 :         db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     589             :     }
     590             : 
     591           0 :     return true;
     592             : }
     593             : 
     594             : //
     595             : // Cancel all scheduled updates and clean up AdvertiseInfo corresponding
     596             : // to any current updates for the peers in the RibPeerSet.
     597             : //
     598          68 : void RibUpdateMonitor::ClearPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
     599             :         const RibPeerSet &mleave) {
     600          68 :     CHECK_CONCURRENCY("db::DBTable");
     601             : 
     602             :     // Don't need to bother going through the monitor if there's no DBState.
     603             :     DBState *dbstate =
     604          68 :         db_entry->GetState(ribout_->table(), ribout_->listener_id());
     605          68 :     if (dbstate == NULL) {
     606           0 :         return;
     607             :     }
     608             : 
     609             :     // Go through the monitor and handle the DBState as appropriate.  Note
     610             :     // that we still need to check for the DBState being NULL as things may
     611             :     // have changed after the check made above.
     612             :     while (true) {
     613             :         DBState *dbstate =
     614          68 :             db_entry->GetState(ribout_->table(), ribout_->listener_id());
     615             : 
     616             :         // Handle the case where there's no DBState.
     617          68 :         if (dbstate == NULL) {
     618           0 :             return;
     619             :         }
     620             : 
     621             :         // Handle the case where it's a RouteState.
     622          68 :         RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
     623          68 :         if (rstate != NULL) {
     624          68 :             RouteStateClearPeerSet(db_entry, rstate, mleave);
     625          68 :             return;
     626             :         }
     627             : 
     628             :         // Handle the case where it's a RouteUpdate.
     629           0 :         RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     630           0 :         if (rt_update != NULL) {
     631             :             bool delete_rt_update =
     632           0 :                 RouteUpdateClearPeerSet(db_entry, rt_update, mleave);
     633           0 :             if (delete_rt_update) {
     634           0 :                 delete rt_update;
     635             :             }
     636           0 :             return;
     637             :         }
     638             : 
     639             :         // Handle the case where it's a UpdateList.
     640           0 :         UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
     641             :         bool delete_uplist;
     642             : 
     643           0 :         assert(uplist);
     644           0 :         delete_uplist = UpdateListClearPeerSet(db_entry, uplist, mleave);
     645           0 :         if (delete_uplist) {
     646           0 :             delete uplist;
     647             :         }
     648           0 :         return;
     649             :     }
     650             : 
     651             :     assert(false);
     652             : }
     653             : 
     654             : //
     655             : // Enqueue the specified RouteUpdate to the UpdateQueue and set the listener
     656             : // state for the for the DBEntry to point to the RouteUpdate.
     657             : // Set the listener state to the UpdateList if it is non-NULL, else set it to
     658             : // the RouteUpdate.
     659             : //
     660             : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
     661             : // the (RibOut, QueueId).
     662             : //
     663         284 : bool RibUpdateMonitor::EnqueueUpdate(DBEntryBase *db_entry,
     664             :         RouteUpdate *rt_update, UpdateList *uplist) {
     665         284 :     CHECK_CONCURRENCY("db::DBTable");
     666             : 
     667         284 :     UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
     668         284 :     if (uplist) {
     669           0 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), uplist);
     670             :     } else {
     671         284 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
     672             :     }
     673         284 :     return queue->Enqueue(rt_update);
     674             : }
     675             : 
     676             : //
     677             : // Dequeue the specified RouteUpdate from it's UpdateQueue.
     678             : //
     679         284 : void RibUpdateMonitor::DequeueUpdate(RouteUpdate *rt_update) {
     680         284 :     CHECK_CONCURRENCY("db::DBTable", "bgp::SendUpdate");
     681             : 
     682         284 :     UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
     683         284 :     queue->Dequeue(rt_update);
     684         284 : }
     685             : 
     686             : //
     687             : // Get the next RouteUpdate after the provided UpdateEntry and return
     688             : // the RouteUpdatePtr encapsulator for it.
     689             : //
     690             : // If the next RouteUpdate is NULL, move the tail marker to be after
     691             : // the UpdateEntry.  The tail marker move must be done must be done
     692             : // atomically with returning a NULL RouteUpdatePtr. This ensures that
     693             : // Enqueue can correctly detect the need to trigger a tail dequeue.
     694             : //
     695         568 : RouteUpdatePtr RibUpdateMonitor::GetNextUpdate(int queue_id,
     696             :         UpdateEntry *upentry) {
     697         568 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     698             : 
     699         568 :     UpdateQueue *queue = queue_vec_->at(queue_id);
     700         568 :     RouteUpdate *next_rt_update = queue->NextUpdate(upentry);
     701         568 :     RouteUpdatePtr update(next_rt_update);
     702         568 :     if (!next_rt_update && upentry->IsUpdate()) {
     703         284 :         RouteUpdate *rt_update = static_cast<RouteUpdate *>(upentry);
     704         284 :         queue->MoveMarker(queue->tail_marker(), rt_update);
     705             :     }
     706         568 :     return update;
     707           0 : }
     708             : 
     709             : //
     710             : // Get the next UpdateEntry after the one provided and return it via
     711             : // the output parameter.
     712             : //
     713             : // Return the RouteUpdatePtr encapsulator for the next UpdateEntry
     714             : // if it's a RouteUpdate.  If it's not, return an encapsulator for
     715             : // a NULL RouteUpdate.
     716             : //
     717           0 : RouteUpdatePtr RibUpdateMonitor::GetNextEntry(int queue_id,
     718             :         UpdateEntry *upentry, UpdateEntry **next_upentry_p) {
     719           0 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     720             : 
     721           0 :     UpdateQueue *queue = queue_vec_->at(queue_id);
     722           0 :     UpdateEntry *next_upentry = *next_upentry_p = queue->NextEntry(upentry);
     723           0 :     if (next_upentry != NULL && next_upentry->IsUpdate()) {
     724           0 :         RouteUpdate *rt_update = static_cast<RouteUpdate *>(next_upentry);
     725           0 :         RouteUpdatePtr update(rt_update);
     726           0 :         return update;
     727           0 :     }
     728           0 :     return RouteUpdatePtr();
     729             : }
     730             : 
     731             : //
     732             : // Get the next UpdateInfo after the one provided and return it via
     733             : // the output parameter.
     734             : //
     735             : // Return the RouteUpdatePtr encapsulator for the RouteUpdate that
     736             : // corresponds to the next UpdateInfo. If there's no next UpdateInfo
     737             : // return an encapsulator for a NULL RouteUpdate.
     738             : //
     739         284 : RouteUpdatePtr RibUpdateMonitor::GetAttrNext(int queue_id,
     740             :         UpdateInfo *current_uinfo, UpdateInfo **next_uinfo_p) {
     741         284 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     742             : 
     743         284 :     UpdateQueue *queue = queue_vec_->at(queue_id);
     744         284 :     UpdateInfo *next_uinfo = queue->AttrNext(current_uinfo);
     745         284 :     RouteUpdate *rt_update = NULL;
     746         284 :     if (next_uinfo) {
     747           0 :         rt_update = next_uinfo->update;
     748             :     }
     749         284 :     RouteUpdatePtr update(rt_update);
     750         284 :     *next_uinfo_p = next_uinfo;
     751         284 :     return update;
     752             : }
     753             : 
     754             : //
     755             : // Set the listener state for the DBEntryBase to be the DBState.
     756             : //
     757         186 : void RibUpdateMonitor::SetEntryState(DBEntryBase *db_entry, DBState *dbstate) {
     758         186 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     759             : 
     760         186 :     db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
     761         186 : }
     762             : 
     763             : //
     764             : // Clear the listener state for the DBEntryBase.
     765             : //
     766          98 : void RibUpdateMonitor::ClearEntryState(DBEntryBase *db_entry) {
     767          98 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     768             : 
     769          98 :     db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     770          98 : }

Generated by: LCOV version 1.14