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: 293 300 97.7 %
Date: 2026-06-11 01:56:02 Functions: 27 27 100.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     2172616 : RouteUpdatePtr::RouteUpdatePtr(RouteUpdate *rt_update) : rt_update_(rt_update) {
      13     2172616 : }
      14             : 
      15     5253592 : RouteUpdatePtr::~RouteUpdatePtr() {
      16     5253592 : }
      17             : 
      18      136016 : RibUpdateMonitor::RibUpdateMonitor(RibOut *ribout, QueueVec *queue_vec)
      19      136016 :     : ribout_(ribout), queue_vec_(queue_vec) {
      20      136016 : }
      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        1715 : DBState *RibUpdateMonitor::GetRouteUpdateAndDequeue(DBEntryBase *db_entry,
      29             :         RouteUpdate *rt_update, UpdateCmp cmp, bool *duplicate) {
      30        1715 :     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        1714 :     if (rt_update->queue_id() == RibOutUpdates::QUPDATE) {
      35        1615 :         *duplicate = cmp(rt_update);
      36        1616 :         if (*duplicate) {
      37         291 :             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        1424 :     db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
      45        1424 :     UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
      46        1424 :     queue->Dequeue(rt_update);
      47        1424 :     rt_update->set_queue_id(RibOutUpdates::QUPDATE);
      48        1424 :     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          60 : DBState *RibUpdateMonitor::GetUpdateListAndDequeue(DBEntryBase *db_entry,
      66             :         UpdateList *uplist) {
      67          60 :     CHECK_CONCURRENCY("db::DBTable");
      68             : 
      69             :     DBState *dbstate;
      70          60 :     RouteUpdate *rt_update = uplist->FindUpdate(RibOutUpdates::QUPDATE);
      71          60 :     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          30 :         uplist->RemoveUpdate(rt_update);
      76          30 :         queue_vec_->at(rt_update->queue_id())->Dequeue(rt_update);
      77          30 :         uplist->MoveHistory(rt_update);
      78          30 :         dbstate = rt_update;
      79          30 :     } 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          20 :         RouteState *rstate = new RouteState();
      83          20 :         uplist->MoveHistory(rstate);
      84          20 :         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          10 :         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          60 :     UpdateList::List *list = uplist->GetList();
      96          60 :     for (UpdateList::List::iterator iter = list->begin();
      97         120 :          iter != list->end(); iter++) {
      98          60 :         RouteUpdate *temp_rt_update = *iter;
      99          60 :         queue_vec_->at(temp_rt_update->queue_id())->Dequeue(temp_rt_update);
     100          60 :         delete temp_rt_update;
     101             :     }
     102             : 
     103             :     // Update the DBState and return it.
     104          60 :     if (dbstate) {
     105          50 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
     106             :     } else {
     107          10 :         db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     108             :     }
     109          60 :     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     1429765 : DBState *RibUpdateMonitor::GetDBStateAndDequeue(DBEntryBase *db_entry,
     126             :         UpdateCmp cmp, bool *duplicate) {
     127     1429765 :     CHECK_CONCURRENCY("db::DBTable");
     128             : 
     129     1429574 :     *duplicate = false;
     130             : 
     131             :     // Don't need to bother going through the monitor if there's no DBState.
     132             :     DBState *dbstate =
     133     1429574 :         db_entry->GetState(ribout_->table(), ribout_->listener_id());
     134     1430807 :     if (dbstate == NULL) {
     135      887635 :         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      543172 :             db_entry->GetState(ribout_->table(), ribout_->listener_id());
     145      543176 :         if (dbstate == NULL) {
     146           0 :             return dbstate;
     147             :         }
     148             : 
     149             :         // Return if it's a RouteState.
     150      543176 :         RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
     151      543176 :         if (rstate != NULL) {
     152      541406 :             return rstate;
     153             :         }
     154             : 
     155             :         // Handle the case where it's a RouteUpdate.
     156        1770 :         RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     157        1770 :         if (rt_update != NULL) {
     158        3430 :             return GetRouteUpdateAndDequeue(db_entry, rt_update, cmp,
     159        1715 :                     duplicate);
     160             :         }
     161             : 
     162             :         // Handle the case where it's a UpdateList.
     163          55 :         UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
     164             :         DBState *db_state;
     165          55 :         assert(uplist);
     166          55 :         db_state = GetUpdateListAndDequeue(db_entry, uplist);
     167          60 :         delete uplist;
     168          60 :         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      459837 : static void RouteStateCurrent(const RouteState *rstate,
     180             :         RibPeerSet *mcurrent) {
     181      459837 :     CHECK_CONCURRENCY("db::DBTable");
     182             : 
     183      459199 :     const AdvertiseSList &adv_slist = rstate->Advertised();
     184      459188 :     for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
     185     2087717 :          iter != adv_slist->end(); ++iter) {
     186     1169936 :         mcurrent->Set(iter->bitset);
     187             :     }
     188      458723 : }
     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      158204 : static void RouteUpdateCurrentAndScheduled(const RouteUpdate *rt_update,
     198             :         int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
     199      158204 :     CHECK_CONCURRENCY("db::DBTable");
     200             : 
     201      157963 :     const AdvertiseSList &adv_slist = rt_update->History();
     202      157960 :     for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
     203      679356 :          iter != adv_slist->end(); ++iter) {
     204      363606 :         mcurrent->Set(iter->bitset);
     205             :     }
     206             : 
     207      157806 :     if (queue_id != RibOutUpdates::QCOUNT && queue_id != rt_update->queue_id())
     208       12310 :         return;
     209             : 
     210      145494 :     const UpdateInfoSList &uinfo_slist = rt_update->Updates();
     211      145713 :     for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
     212      584306 :          iter != uinfo_slist->end(); ++iter) {
     213      292924 :         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         140 : static void UpdateListCurrentAndScheduled(const UpdateList *uplist,
     223             :         int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
     224         140 :     CHECK_CONCURRENCY("db::DBTable");
     225             : 
     226         140 :     const AdvertiseSList &adv_slist = uplist->History();
     227         140 :     for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
     228         650 :          iter != adv_slist->end(); ++iter) {
     229         370 :         mcurrent->Set(iter->bitset);
     230             :     }
     231             : 
     232         140 :     const UpdateList::List *list = uplist->GetList();
     233         140 :     for (UpdateList::List::const_iterator iter = list->begin();
     234         415 :          iter != list->end(); ++iter) {
     235         275 :         RouteUpdateCurrentAndScheduled(*iter, queue_id, mcurrent, mscheduled);
     236             :     }
     237         140 : }
     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      855859 : bool RibUpdateMonitor::GetPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
     249             :         int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
     250      855859 :     CHECK_CONCURRENCY("db::DBTable");
     251             : 
     252             :     // Don't need to bother going through the monitor if there's no DBState.
     253             :     DBState *dbstate =
     254      855487 :         db_entry->GetState(ribout_->table(), ribout_->listener_id());
     255      856789 :     if (dbstate == NULL) {
     256      238862 :         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      617851 :         DBState *dbstate = db_entry->GetState(ribout_->table(),
     265      617927 :                                          ribout_->listener_id());
     266      617947 :         if (dbstate == NULL) {
     267           0 :             return false;
     268             :         }
     269             : 
     270             :         // Handle the case where it's a RouteState.
     271      617947 :         RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
     272      617947 :         if (rstate != NULL) {
     273      459881 :             RouteStateCurrent(rstate, mcurrent);
     274      459110 :             return false;
     275             :         }
     276             : 
     277             :         // Handle the case where it's a RouteUpdate.
     278      158066 :         RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     279      158066 :         if (rt_update != NULL) {
     280      157943 :             RouteUpdateCurrentAndScheduled(rt_update, queue_id,
     281             :                     mcurrent, mscheduled);
     282      157753 :             break;
     283             :         }
     284             : 
     285             :         // Handle the case where it's a UpdateList.
     286         123 :         UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
     287         123 :         if (uplist != NULL) {
     288         140 :             UpdateListCurrentAndScheduled(uplist, queue_id,
     289             :                     mcurrent, mscheduled);
     290         140 :             break;
     291             :         }
     292             : 
     293             :         // Unknown DBState.
     294           0 :         assert(false);
     295             :     }
     296             : 
     297      157893 :     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       53274 : bool RibUpdateMonitor::RouteStateMergeUpdate(DBEntryBase *db_entry,
     311             :         RouteUpdate *rt_update, RouteState *rstate) {
     312       53274 :     CHECK_CONCURRENCY("db::DBTable");
     313             : 
     314       53250 :     rstate->MoveHistory(rt_update);
     315       53258 :     delete rstate;
     316       53252 :     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          47 : bool RibUpdateMonitor::RouteUpdateMergeUpdate(DBEntryBase *db_entry,
     332             :         RouteUpdate *rt_update, RouteUpdate *current_rt_update) {
     333          47 :     CHECK_CONCURRENCY("db::DBTable");
     334             : 
     335          47 :     if (current_rt_update->queue_id() == rt_update->queue_id()) {
     336          37 :         UpdateQueue *queue = queue_vec_->at(current_rt_update->queue_id());
     337          37 :         queue->Dequeue(current_rt_update);
     338          37 :         current_rt_update->MergeUpdateInfo(rt_update->Updates());
     339          37 :         assert(rt_update->Updates()->empty());
     340          37 :         delete rt_update;
     341          37 :         return EnqueueUpdate(db_entry, current_rt_update);
     342             :     } else {
     343          10 :         UpdateList *uplist = current_rt_update->MakeUpdateList();
     344          10 :         uplist->AddUpdate(rt_update);
     345          10 :         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          25 : bool RibUpdateMonitor::UpdateListMergeUpdate(DBEntryBase *db_entry,
     362             :         RouteUpdate *rt_update, UpdateList *uplist) {
     363          25 :     CHECK_CONCURRENCY("db::DBTable");
     364             : 
     365          25 :     RouteUpdate *current_rt_update = uplist->FindUpdate(rt_update->queue_id());
     366          25 :     if (current_rt_update) {
     367          20 :         UpdateQueue *queue = queue_vec_->at(current_rt_update->queue_id());
     368          20 :         queue->Dequeue(current_rt_update);
     369          20 :         current_rt_update->MergeUpdateInfo(rt_update->Updates());
     370          20 :         assert(rt_update->Updates()->empty());
     371          20 :         delete rt_update;
     372          20 :         return EnqueueUpdate(db_entry, current_rt_update, uplist);
     373             :     } else {
     374           5 :         uplist->AddUpdate(rt_update);
     375           5 :         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       79107 : bool RibUpdateMonitor::MergeUpdate(DBEntryBase *db_entry,
     388             :         RouteUpdate *rt_update) {
     389       79107 :     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       79076 :             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       79129 :         if (dbstate == NULL) {
     402       25770 :             return EnqueueUpdate(db_entry, rt_update);
     403             :         }
     404             : 
     405             :         // Handle the case where it's a RouteState.
     406       53359 :         RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
     407       53359 :         if (rstate != NULL) {
     408       53287 :             return RouteStateMergeUpdate(db_entry, rt_update, rstate);
     409             :         }
     410             : 
     411             :         // Handle the case where it's a RouteUpdate.
     412          72 :         RouteUpdate *current_rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     413          72 :         if (current_rt_update != NULL) {
     414          47 :             return RouteUpdateMergeUpdate(db_entry, rt_update,
     415          47 :                     current_rt_update);
     416             :         }
     417             : 
     418             :         // Handle the case where it's a UpdateList.
     419          25 :         UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
     420          25 :         if (uplist != NULL) {
     421          25 :             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      264695 : void RibUpdateMonitor::AdvertiseSListClearBits(AdvertiseSList &adv_slist,
     438             :         const RibPeerSet &clear) {
     439      264695 :     CHECK_CONCURRENCY("db::DBTable");
     440             : 
     441      264700 :     for (AdvertiseSList::List::iterator iter = adv_slist->begin();
     442     1190563 :          iter != adv_slist->end(); ) {
     443      330609 :         iter->bitset.Reset(clear);
     444      330484 :         if (iter->bitset.empty()) {
     445      552773 :             iter = adv_slist->erase_and_dispose(iter, AdvertiseInfoDisposer());
     446             :         } else {
     447      146306 :             iter++;
     448             :         }
     449             :     }
     450      264557 : }
     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       62904 : void RibUpdateMonitor::UpdateInfoSListClearBits(UpdateInfoSList &uinfo_slist,
     458             :         const RibPeerSet &clear) {
     459       62904 :     CHECK_CONCURRENCY("db::DBTable");
     460             : 
     461       62869 :     for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin();
     462      252516 :          iter != uinfo_slist->end(); ) {
     463       63335 :         iter->target.Reset(clear);
     464       63316 :         if (iter->target.empty()) {
     465       62004 :             RouteUpdate *rt_update = iter->update;
     466       62004 :             UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
     467      124008 :             queue->AttrDequeue(iter.operator->());
     468      186212 :             iter = uinfo_slist->erase_and_dispose(iter, UpdateInfoDisposer());
     469             :         } else {
     470        1316 :             iter++;
     471             :         }
     472             :     }
     473       62880 : }
     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      202070 : void RibUpdateMonitor::RouteStateClearPeerSet(DBEntryBase *db_entry,
     482             :         RouteState *rstate, const RibPeerSet &mleave) {
     483      202070 :     CHECK_CONCURRENCY("db::DBTable");
     484             : 
     485             :     // Clear the bits for each element in the AdvertiseSList.
     486      201922 :     AdvertiseSListClearBits(rstate->Advertised(), mleave);
     487             : 
     488             :     // Get rid of the RouteState itself if it's empty.
     489      201968 :     if (rstate->Advertised()->empty()) {
     490      105256 :         db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     491      105301 :         delete rstate;
     492             :     }
     493      201919 : }
     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       62835 : bool RibUpdateMonitor::RouteUpdateClearPeerSet(DBEntryBase *db_entry,
     504             :             RouteUpdate *rt_update, const RibPeerSet &mleave) {
     505       62835 :     CHECK_CONCURRENCY("db::DBTable");
     506             : 
     507             :     // Clear the bits for each element in the AdvertiseSList.
     508       62729 :     AdvertiseSListClearBits(rt_update->History(), mleave);
     509             : 
     510             :     // Clear the bits for each element in the UpdateInfoSList.
     511       62691 :     UpdateInfoSListClearBits(rt_update->Updates(), mleave);
     512             : 
     513             :     // Update the DBstate for the DBentry as appropriate.
     514       62771 :     if (!rt_update->Updates()->empty()) {
     515             :         // There are more scheduled updates, do nothing.
     516        1177 :         return false;
     517             :     }
     518             : 
     519       61577 :     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       15324 :         DequeueUpdate(rt_update);
     524       15321 :         RouteState *rstate = new RouteState;
     525       15307 :         rt_update->MoveHistory(rstate);
     526       15310 :         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       46242 :         DequeueUpdate(rt_update);
     531       46236 :         db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     532             :     }
     533             : 
     534       61663 :     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          80 : bool RibUpdateMonitor::UpdateListClearPeerSet(DBEntryBase *db_entry,
     544             :         UpdateList *uplist, const RibPeerSet &mleave) {
     545          80 :     CHECK_CONCURRENCY("db::DBTable");
     546             : 
     547          80 :     UpdateList::List *list = uplist->GetList();
     548             : 
     549             :     // Clear the bits for each element in the AdvertiseSList.
     550          80 :     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          80 :     for (UpdateList::List::iterator iter = list->begin();
     556         240 :          iter != list->end(); ) {
     557         160 :         RouteUpdate *rt_update = *iter++;
     558         160 :         UpdateInfoSListClearBits(rt_update->Updates(), mleave);
     559         160 :         if (rt_update->Updates()->empty()) {
     560          80 :             uplist->RemoveUpdate(rt_update);
     561          80 :             DequeueUpdate(rt_update);
     562          80 :             delete rt_update;
     563             :         }
     564             :     }
     565             : 
     566             :     // Update the DBstate for the DBentry as appropriate.
     567          80 :     if (list->size() > 1) {
     568             :         // There's multiple RouteUpdates on the UpdateList, do nothing.
     569          30 :         return false;
     570          50 :     } 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          20 :         RouteUpdate *rt_update = uplist->MakeRouteUpdate();
     576          20 :         assert(rt_update);
     577          20 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
     578          30 :     } 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          20 :         RouteState *rstate = new RouteState;
     583          20 :         uplist->MoveHistory(rstate);
     584          20 :         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          10 :         db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     589             :     }
     590             : 
     591          50 :     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      264640 : void RibUpdateMonitor::ClearPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
     599             :         const RibPeerSet &mleave) {
     600      264640 :     CHECK_CONCURRENCY("db::DBTable");
     601             : 
     602             :     // Don't need to bother going through the monitor if there's no DBState.
     603             :     DBState *dbstate =
     604      264631 :         db_entry->GetState(ribout_->table(), ribout_->listener_id());
     605      264999 :     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      264999 :             db_entry->GetState(ribout_->table(), ribout_->listener_id());
     615             : 
     616             :         // Handle the case where there's no DBState.
     617      265024 :         if (dbstate == NULL) {
     618           0 :             return;
     619             :         }
     620             : 
     621             :         // Handle the case where it's a RouteState.
     622      265024 :         RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
     623      265024 :         if (rstate != NULL) {
     624      202062 :             RouteStateClearPeerSet(db_entry, rstate, mleave);
     625      201915 :             return;
     626             :         }
     627             : 
     628             :         // Handle the case where it's a RouteUpdate.
     629       62962 :         RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     630       62962 :         if (rt_update != NULL) {
     631             :             bool delete_rt_update =
     632       62840 :                 RouteUpdateClearPeerSet(db_entry, rt_update, mleave);
     633       62839 :             if (delete_rt_update) {
     634       61662 :                 delete rt_update;
     635             :             }
     636       62823 :             return;
     637             :         }
     638             : 
     639             :         // Handle the case where it's a UpdateList.
     640         122 :         UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
     641             :         bool delete_uplist;
     642             : 
     643         122 :         assert(uplist);
     644         122 :         delete_uplist = UpdateListClearPeerSet(db_entry, uplist, mleave);
     645          80 :         if (delete_uplist) {
     646          50 :             delete uplist;
     647             :         }
     648          80 :         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      977219 : bool RibUpdateMonitor::EnqueueUpdate(DBEntryBase *db_entry,
     664             :         RouteUpdate *rt_update, UpdateList *uplist) {
     665      977219 :     CHECK_CONCURRENCY("db::DBTable");
     666             : 
     667      977326 :     UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
     668      977313 :     if (uplist) {
     669          35 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), uplist);
     670             :     } else {
     671      977278 :         db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
     672             :     }
     673      977906 :     return queue->Enqueue(rt_update);
     674             : }
     675             : 
     676             : //
     677             : // Dequeue the specified RouteUpdate from it's UpdateQueue.
     678             : //
     679      975996 : void RibUpdateMonitor::DequeueUpdate(RouteUpdate *rt_update) {
     680      975996 :     CHECK_CONCURRENCY("db::DBTable", "bgp::SendUpdate");
     681             : 
     682      975910 :     UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
     683      975887 :     queue->Dequeue(rt_update);
     684      976114 : }
     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     1247845 : RouteUpdatePtr RibUpdateMonitor::GetNextUpdate(int queue_id,
     696             :         UpdateEntry *upentry) {
     697     1247845 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     698             : 
     699     1247681 :     UpdateQueue *queue = queue_vec_->at(queue_id);
     700     1247683 :     RouteUpdate *next_rt_update = queue->NextUpdate(upentry);
     701     1247804 :     RouteUpdatePtr update(next_rt_update);
     702     1247993 :     if (!next_rt_update && upentry->IsUpdate()) {
     703      611116 :         RouteUpdate *rt_update = static_cast<RouteUpdate *>(upentry);
     704      611116 :         queue->MoveMarker(queue->tail_marker(), rt_update);
     705             :     }
     706     1247968 :     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         507 : RouteUpdatePtr RibUpdateMonitor::GetNextEntry(int queue_id,
     718             :         UpdateEntry *upentry, UpdateEntry **next_upentry_p) {
     719         507 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     720             : 
     721         507 :     UpdateQueue *queue = queue_vec_->at(queue_id);
     722         507 :     UpdateEntry *next_upentry = *next_upentry_p = queue->NextEntry(upentry);
     723         507 :     if (next_upentry != NULL && next_upentry->IsUpdate()) {
     724         419 :         RouteUpdate *rt_update = static_cast<RouteUpdate *>(next_upentry);
     725         419 :         RouteUpdatePtr update(rt_update);
     726         419 :         return update;
     727         419 :     }
     728          88 :     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      924355 : RouteUpdatePtr RibUpdateMonitor::GetAttrNext(int queue_id,
     740             :         UpdateInfo *current_uinfo, UpdateInfo **next_uinfo_p) {
     741      924355 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     742             : 
     743      924289 :     UpdateQueue *queue = queue_vec_->at(queue_id);
     744      924310 :     UpdateInfo *next_uinfo = queue->AttrNext(current_uinfo);
     745      924488 :     RouteUpdate *rt_update = NULL;
     746      924488 :     if (next_uinfo) {
     747      305303 :         rt_update = next_uinfo->update;
     748             :     }
     749      924488 :     RouteUpdatePtr update(rt_update);
     750      924486 :     *next_uinfo_p = next_uinfo;
     751      924486 :     return update;
     752             : }
     753             : 
     754             : //
     755             : // Set the listener state for the DBEntryBase to be the DBState.
     756             : //
     757      583190 : void RibUpdateMonitor::SetEntryState(DBEntryBase *db_entry, DBState *dbstate) {
     758      583190 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     759             : 
     760      583221 :     db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
     761      583399 : }
     762             : 
     763             : //
     764             : // Clear the listener state for the DBEntryBase.
     765             : //
     766      331097 : void RibUpdateMonitor::ClearEntryState(DBEntryBase *db_entry) {
     767      331097 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     768             : 
     769      331085 :     db_entry->ClearState(ribout_->table(), ribout_->listener_id());
     770      331273 : }

Generated by: LCOV version 1.14