LCOV - code coverage report
Current view: top level - bgp - bgp_update_sender.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 457 495 92.3 %
Date: 2026-11-09 02:10:49 Functions: 93 97 95.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include "bgp/bgp_update_sender.h"
       7             : 
       8             : #include <boost/bind/bind.hpp>
       9             : #include <boost/foreach.hpp>
      10             : 
      11             : #include <map>
      12             : #include <string>
      13             : #include <atomic>
      14             : 
      15             : #include "base/task_annotations.h"
      16             : #include "bgp/ipeer.h"
      17             : #include "bgp/bgp_ribout.h"
      18             : #include "bgp/bgp_ribout_updates.h"
      19             : #include "db/db.h"
      20             : 
      21             : using std::unique_ptr;
      22             : using std::make_pair;
      23             : using std::map;
      24             : using std::string;
      25             : using std::vector;
      26             : using namespace boost::placeholders;
      27             : 
      28             : //
      29             : // This struct represents RibOut specific state for a PeerState.  There's one
      30             : // instance of this for each RibOut that an IPeerUpdate has joined.
      31             : //
      32             : // The PeerRibState contains a bit mask to keep track of the QueueIds that are
      33             : // currently active for the RibOut for the IPeerUpdate.
      34             : //
      35             : struct BgpSenderPartition::PeerRibState {
      36      367821 :     PeerRibState() : qactive(0) { }
      37             :     uint8_t qactive;
      38             : };
      39             : 
      40             : //
      41             : // This nested class represents IPeerUpdate related state that's specific to
      42             : // the BgpSenderPartition.
      43             : //
      44             : // A PeerState contains a Map of the index for a RibState to a PeerRibState.
      45             : // Each entry in the map logically represents the state of the peer for the
      46             : // ribout.
      47             : //
      48             : // The Map is used in conjunction with the RibStateMap in BgpSenderPartition
      49             : // to implement regular and circular iterator classes that are used to walk
      50             : // through all the RibState entries for a peer.
      51             : //
      52             : // A PeerState maintains the in_sync and send_ready state for the IPeerUpdate.
      53             : //
      54             : // The PeerState is considered to be send_ready when the underlying socket is
      55             : // is writable.  Note that the send_ready state in the PeerState may be out of
      56             : // date with the actual socket state because the socket could have got blocked
      57             : // when writing from another partition. Hence IPeerUpdate::send_ready() is the
      58             : // more authoritative source.
      59             : //
      60             : // The PeerState is considered to be in_sync if it's send_ready and the marker
      61             : // IPeerUpdate the peer has merged with the tail marker for all QueueIds in
      62             : // all RiBOuts for which the IPeerUpdate is subscribed.
      63             : //
      64             : // The PeerState keeps count of the number of active RibOuts for each QueueId.
      65             : // A (RibOut, QueueId) pair is considered to be active if the PeerState isn't
      66             : // send_ready and there's RouteUpdates for the pair.
      67             : //
      68             : class BgpSenderPartition::PeerState {
      69             : public:
      70             :     typedef map<size_t, PeerRibState> Map;
      71             : 
      72             :     class iterator : public boost::iterator_facade<
      73             :         iterator, RibOut, boost::forward_traversal_tag> {
      74             :     public:
      75       33584 :         explicit iterator(const RibStateMap &indexmap, Map *map, size_t index)
      76       33584 :             : indexmap_(indexmap), map_(map), index_(index) {
      77       33584 :         }
      78             :         size_t index() const { return index_; }
      79       11436 :         RibState *rib_state() { return indexmap_.At(index_); }
      80             :         const PeerRibState &peer_rib_state() const { return (*map_)[index_]; }
      81             : 
      82             :     private:
      83             :         friend class boost::iterator_core_access;
      84       11436 :         void increment() {
      85       11436 :             Map::const_iterator loc = map_->upper_bound(index_);
      86       11436 :             if (loc == map_->end()) {
      87       11074 :                 index_ = -1;
      88             :             } else {
      89         362 :                 index_ = loc->first;
      90             :             }
      91       11436 :         }
      92       22510 :         bool equal(const iterator &rhs) const {
      93       22510 :             return index_ == rhs.index_;
      94             :         }
      95             :         RibOut &dereference() const;
      96             : 
      97             :         const RibStateMap &indexmap_;
      98             :         Map *map_;
      99             :         size_t index_;
     100             :     };
     101             : 
     102             :     class circular_iterator : public boost::iterator_facade<
     103             :         circular_iterator, RibOut, boost::forward_traversal_tag> {
     104             :     public:
     105        1203 :         explicit circular_iterator(const RibStateMap &indexmap, Map *map,
     106             :                                    int start, bool is_valid)
     107        1203 :         : indexmap_(indexmap), map_(map), index_(-1), match_(true) {
     108        1203 :             if (map_->empty()) {
     109           0 :                 return;
     110             :             }
     111        1203 :             Map::const_iterator loc = map_->lower_bound(start);
     112        1203 :             if (loc == map_->end()) {
     113        1060 :                 loc = map_->begin();
     114             :             }
     115        1203 :             index_ = loc->first;
     116        1203 :             if (is_valid) match_ = false;
     117             :         }
     118          37 :         int index() const { return index_; }
     119         447 :         RibState *rib_state() { return indexmap_.At(index_); }
     120         490 :         const PeerRibState &peer_rib_state() const { return (*map_)[index_]; }
     121             : 
     122             :     private:
     123             :         friend class boost::iterator_core_access;
     124         453 :         void increment() {
     125         453 :             match_ = true;
     126         453 :             assert(!map_->empty());
     127         453 :             Map::const_iterator loc = map_->upper_bound(index_);
     128         453 :             if (loc == map_->end()) {
     129         338 :                 loc = map_->begin();
     130             :             }
     131         453 :             index_ = loc->first;
     132         453 :         }
     133         828 :         bool equal(const circular_iterator &rhs) const {
     134         828 :             return ((match_ == rhs.match_) && (index_ == rhs.index_));
     135             :         }
     136             :         RibOut &dereference() const;
     137             : 
     138             :         const RibStateMap &indexmap_;
     139             :         Map *map_;
     140             :         int index_;
     141             :         bool match_;
     142             :     };
     143             : 
     144       81397 :     explicit PeerState(IPeerUpdate *peer)
     145       81397 :         : key_(peer), index_(-1),
     146       81397 :         qactive_cnt_(RibOutUpdates::QCOUNT),
     147       81397 :         in_sync_(true), rib_iterator_(BitSet::npos) {
     148       81397 :         send_ready_ = true;
     149      244191 :         for (int i = 0; i < RibOutUpdates::QCOUNT; i++) {
     150      162794 :             qactive_cnt_[i] = 0;
     151             :         }
     152       81397 :     }
     153             : 
     154             :     void Add(RibState *rs);
     155             : 
     156             :     void Remove(RibState *rs);
     157             : 
     158         576 :     bool IsMember(size_t index) const {
     159         576 :         return rib_bitset_.test(index);
     160             :     }
     161             : 
     162       11074 :     iterator begin(const RibStateMap &indexmap) {
     163       11074 :         Map::const_iterator it = rib_set_.begin();
     164       11074 :         size_t index = (it != rib_set_.end() ? it->first : -1);
     165       11074 :         return iterator(indexmap, &rib_set_, index);
     166             :     }
     167       22510 :     iterator end(const RibStateMap &indexmap) {
     168       22510 :         return iterator(indexmap, &rib_set_, -1);
     169             :     }
     170             : 
     171         375 :     circular_iterator circular_begin(const RibStateMap &indexmap) {
     172         375 :         return circular_iterator(indexmap, &rib_set_, rib_iterator_, true);
     173             :     }
     174         828 :     circular_iterator circular_end(const RibStateMap &indexmap) {
     175         828 :         return circular_iterator(indexmap, &rib_set_, rib_iterator_, false);
     176             :     }
     177             : 
     178          37 :     void SetIteratorStart(size_t start) { rib_iterator_ = start; }
     179             : 
     180       24407 :     void SetQueueActive(size_t rib_index, int queue_id) {
     181       24407 :         CHECK_CONCURRENCY("bgp::SendUpdate");
     182       24413 :         Map::iterator loc = rib_set_.find(rib_index);
     183       24399 :         assert(loc != rib_set_.end());
     184       24398 :         if (!BitIsSet(loc->second.qactive, queue_id)) {
     185       21752 :             SetBit(loc->second.qactive, queue_id);
     186       21750 :             qactive_cnt_[queue_id]++;
     187             :         }
     188       24399 :     }
     189             : 
     190      736052 :     void SetQueueInactive(size_t rib_index, int queue_id) {
     191      736052 :         CHECK_CONCURRENCY("bgp::SendUpdate", "bgp::PeerMembership");
     192      736052 :         Map::iterator loc = rib_set_.find(rib_index);
     193      736052 :         assert(loc != rib_set_.end());
     194      736052 :         if (BitIsSet(loc->second.qactive, queue_id)) {
     195       21786 :             ClearBit(loc->second.qactive, queue_id);
     196       21786 :             qactive_cnt_[queue_id]--;
     197             :         }
     198      736052 :     }
     199             : 
     200           0 :     bool IsQueueActive(size_t rib_index, int queue_id) {
     201           0 :         CHECK_CONCURRENCY("bgp::SendUpdate");
     202           0 :         Map::iterator loc = rib_set_.find(rib_index);
     203           0 :         assert(loc != rib_set_.end());
     204           0 :         return BitIsSet(loc->second.qactive, queue_id);
     205             :     }
     206             : 
     207       10703 :     int QueueCount(int queue_id) { return qactive_cnt_[queue_id]; }
     208             : 
     209     3782584 :     IPeerUpdate *peer() const { return key_; }
     210       81397 :     void set_index(size_t index) { index_ = index; }
     211      817459 :     size_t index() const { return index_; }
     212             : 
     213     1937988 :     bool in_sync() const { return in_sync_; }
     214        4188 :     void clear_sync() { in_sync_ = false; }
     215             :     void SetSync();
     216             : 
     217       19232 :     bool send_ready() const { return send_ready_; }
     218        4578 :     void set_send_ready(bool toggle) { send_ready_ = toggle; }
     219             : 
     220      367821 :     bool empty() const { return rib_set_.empty(); }
     221             : 
     222         420 :     bool CheckInvariants() const {
     223        1260 :         for (int i = 0; i < RibOutUpdates::QCOUNT; i++) {
     224         840 :             CHECK_INVARIANT(qactive_cnt_[i] <= (int) rib_set_.size());
     225             :         }
     226         420 :         return true;
     227             :     }
     228             : 
     229             : private:
     230             :     IPeerUpdate *key_;
     231             :     size_t index_;          // assigned from PeerStateMap
     232             :     Map rib_set_;           // list of RibOuts advertised by the peer.
     233             :     BitSet rib_bitset_;     // bitset of RibOuts advertised by the peer
     234             :     vector<int> qactive_cnt_;
     235             :     bool in_sync_;          // whether the peer may dequeue tail markers.
     236             :     std::atomic<bool> send_ready_;    // whether the peer may send updates.
     237             :     size_t rib_iterator_;   // index of last processed rib.
     238             : 
     239             :     DISALLOW_COPY_AND_ASSIGN(PeerState);
     240             : };
     241             : 
     242             : //
     243             : // This nested class represents RibOut related state that's specific to the
     244             : // BgpSenderPartition.
     245             : //
     246             : // A RibState contains a BitSet of all the peers that are advertising the
     247             : // RibOut associated with the RibState.
     248             : //
     249             : // The BitSet is used in conjunction with PeerStateMap in BgpSenderPartition
     250             : // to implement an iterator that is used to walk through all the PeerState
     251             : // entries for the ribout.
     252             : //
     253             : class BgpSenderPartition::RibState {
     254             : public:
     255             :     class iterator : public boost::iterator_facade<
     256             :         iterator, PeerState, boost::forward_traversal_tag> {
     257             :     public:
     258     3155932 :         explicit iterator(const PeerStateMap &indexmap,
     259             :                           const BitSet &set, size_t index)
     260     3155932 :             : indexmap_(indexmap), set_(set), index_(index) {
     261     3155932 :         }
     262             :         size_t index() const { return index_; }
     263             : 
     264             :     private:
     265             :         friend class boost::iterator_core_access;
     266     1931469 :         void increment() {
     267     1931469 :             index_ = set_.find_next(index_);
     268     1931040 :         }
     269     2543432 :         bool equal(const iterator &rhs) const {
     270     2543432 :             return index_ == rhs.index_;
     271             :         }
     272     1931543 :         PeerState &dereference() const {
     273     1931543 :             return *indexmap_.At(index_);
     274             :         }
     275             :         const PeerStateMap &indexmap_;
     276             :         const BitSet &set_;
     277             :         size_t index_;
     278             :     };
     279             : 
     280      135724 :     explicit RibState(RibOut *ribout)
     281      135724 :         : key_(ribout), index_(-1), in_sync_(RibOutUpdates::QCOUNT, true) {
     282      135724 :     }
     283             : 
     284             :     void Add(PeerState *ps);
     285             :     void Remove(PeerState *ps);
     286             : 
     287             :     bool QueueSync(int queue_id);
     288             :     void SetQueueSync(int queue_id);
     289             :     void SetQueueUnsync(int queue_id);
     290             : 
     291         520 :     RibOut *ribout() { return key_; }
     292             : 
     293      612652 :     iterator begin(const PeerStateMap &indexmap) {
     294      612652 :         return iterator(indexmap, peer_set_, peer_set_.find_first());
     295             :     }
     296             : 
     297     2543487 :     iterator end(const PeerStateMap &indexmap) {
     298     2543487 :         return iterator(indexmap, peer_set_, BitSet::npos);
     299             :     }
     300             : 
     301      135724 :     void set_index(size_t index) { index_ = index; }
     302     2367610 :     size_t index() const { return index_; }
     303             : 
     304      367821 :     bool empty() const { return peer_set_.none(); }
     305             : 
     306         576 :     const BitSet &peer_set() const { return peer_set_; }
     307             : 
     308             : private:
     309             :     RibOut *key_;
     310             :     size_t index_;
     311             :     BitSet peer_set_;
     312             :     vector<bool> in_sync_;
     313             : 
     314             :     DISALLOW_COPY_AND_ASSIGN(RibState);
     315             : };
     316             : 
     317             : 
     318      367821 : void BgpSenderPartition::RibState::Add(BgpSenderPartition::PeerState *ps) {
     319      367821 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     320      367821 :     peer_set_.set(ps->index());
     321      367821 : }
     322             : 
     323      367821 : void BgpSenderPartition::RibState::Remove(BgpSenderPartition::PeerState *ps) {
     324      367821 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     325      367821 :     peer_set_.reset(ps->index());
     326      367821 : }
     327             : 
     328       10860 : bool BgpSenderPartition::RibState::QueueSync(int queue_id) {
     329       10860 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     330       10860 :     return (in_sync_[queue_id]);
     331             : }
     332             : 
     333          73 : void BgpSenderPartition::RibState::SetQueueSync(int queue_id) {
     334          73 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     335          73 :     in_sync_[queue_id] = true;
     336          73 : }
     337             : 
     338        6451 : void BgpSenderPartition::RibState::SetQueueUnsync(int queue_id) {
     339        6451 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     340        6450 :     in_sync_[queue_id] = false;
     341        6454 : }
     342             : 
     343      367821 : void BgpSenderPartition::PeerState::Add(RibState *rs) {
     344      367821 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     345      367821 :     PeerRibState init;
     346      367821 :     rib_set_.insert(make_pair(rs->index(), init));
     347      367821 :     rib_bitset_.set(rs->index());
     348      367821 : }
     349             : 
     350      367821 : void BgpSenderPartition::PeerState::Remove(RibState *rs) {
     351      367821 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     352     1103463 :     for (int queue_id = 0; queue_id < RibOutUpdates::QCOUNT; queue_id++) {
     353      735642 :         SetQueueInactive(rs->index(), queue_id);
     354             :     }
     355      367821 :     rib_set_.erase(rs->index());
     356      367821 :     rib_bitset_.reset(rs->index());
     357      367821 : }
     358             : 
     359        5327 : void BgpSenderPartition::PeerState::SetSync() {
     360        5327 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     361       10757 :     for (Map::iterator it = rib_set_.begin(); it != rib_set_.end(); ++it) {
     362        5430 :         assert(it->second.qactive == 0);
     363             :     }
     364       15981 :     for (int i = 0; i < RibOutUpdates::QCOUNT; i++) {
     365       10654 :         assert(qactive_cnt_[i] == 0);
     366             :     }
     367        5327 :     in_sync_ = true;
     368        5327 : }
     369             : 
     370          73 : RibOut &BgpSenderPartition::PeerState::iterator::dereference() const {
     371          73 :     return *indexmap_.At(index_)->ribout();
     372             : }
     373             : 
     374         447 : RibOut &BgpSenderPartition::PeerState::circular_iterator::dereference() const {
     375         447 :     return *indexmap_.At(index_)->ribout();
     376             : }
     377             : 
     378             : class BgpSenderPartition::Worker : public Task {
     379             : public:
     380      218543 :     explicit Worker(BgpSenderPartition *partition)
     381      218543 :         : Task(partition->task_id(), partition->index()),
     382      218543 :           partition_(partition) {
     383      218532 :     }
     384             : 
     385      218634 :     virtual bool Run() {
     386      218634 :         CHECK_CONCURRENCY("bgp::SendUpdate");
     387             : 
     388             :         while (true) {
     389      831266 :             unique_ptr<WorkBase> wentry = partition_->WorkDequeue();
     390      831101 :             if (!wentry.get())
     391      218598 :                 break;
     392      612503 :             if (!wentry->valid)
     393           1 :                 continue;
     394      612521 :             switch (wentry->type) {
     395      612129 :             case WorkBase::WRibOut: {
     396      612129 :                 WorkRibOut *workrib = static_cast<WorkRibOut *>(wentry.get());
     397      612102 :                 partition_->UpdateRibOut(workrib->ribout, workrib->queue_id);
     398      612218 :                 break;
     399             :             }
     400         337 :             case WorkBase::WPeer: {
     401         337 :                 WorkPeer *workpeer = static_cast<WorkPeer *>(wentry.get());
     402         390 :                 partition_->UpdatePeer(workpeer->peer);
     403         390 :                 break;
     404             :             }
     405             :             }
     406     1443895 :         }
     407             : 
     408      218601 :         return true;
     409             :     }
     410           0 :     string Description() const { return "BgpSenderPartition::Worker"; }
     411             : 
     412             : private:
     413             :     BgpSenderPartition *partition_;
     414             : };
     415             : 
     416       38483 : BgpSenderPartition::BgpSenderPartition(BgpUpdateSender *sender, int index)
     417       38483 :     : sender_(sender),
     418       38483 :       index_(index),
     419       38483 :       running_(false),
     420       38483 :       disabled_(false),
     421       38483 :       worker_task_(NULL) {
     422       38483 : }
     423             : 
     424       38483 : BgpSenderPartition::~BgpSenderPartition() {
     425       38483 :     if (worker_task_) {
     426           0 :         TaskScheduler *scheduler = TaskScheduler::GetInstance();
     427           0 :         scheduler->Cancel(worker_task_);
     428             :     }
     429       38483 :     assert(peer_state_imap_.empty());
     430       38483 :     assert(rib_state_imap_.empty());
     431       38483 : }
     432             : 
     433      218525 : int BgpSenderPartition::task_id() const {
     434      218525 :     return sender_->task_id();
     435             : }
     436             : 
     437             : //
     438             : // Add the (RibOut, IPeerUpdate) combo to the BgpSenderPartition.
     439             : // Find or create the corresponding RibState and PeerState and sets up the
     440             : // cross-linkage.
     441             : //
     442      367821 : void BgpSenderPartition::Add(RibOut *ribout, IPeerUpdate *peer) {
     443      367821 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     444             : 
     445      367821 :     RibState *rs = rib_state_imap_.Locate(ribout);
     446      367821 :     PeerState *ps = peer_state_imap_.Locate(peer);
     447      367821 :     rs->Add(ps);
     448      367821 :     ps->Add(rs);
     449      367821 : }
     450             : 
     451             : //
     452             : // Remove the (RibOut, IPeerUpdate) combo from the BgpSenderPartition.
     453             : // Decouple cross linkage between the corresponding RibState and PeerState and
     454             : // get rid of the RibState and/or PeerState if they are no longer needed.
     455             : //
     456      367821 : void BgpSenderPartition::Remove(RibOut *ribout, IPeerUpdate *peer) {
     457      367821 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     458             : 
     459      367821 :     RibState *rs = rib_state_imap_.Find(ribout);
     460      367821 :     PeerState *ps = peer_state_imap_.Find(peer);
     461      367821 :     assert(rs != NULL);
     462      367821 :     assert(ps != NULL);
     463      367821 :     rs->Remove(ps);
     464      367821 :     ps->Remove(rs);
     465      367821 :     if (rs->empty()) {
     466      135724 :         WorkRibOutInvalidate(ribout);
     467      135724 :         rib_state_imap_.Remove(ribout, rs->index());
     468             :     }
     469      367821 :     if (ps->empty())  {
     470       81397 :         WorkPeerInvalidate(peer);
     471       81397 :         peer_state_imap_.Remove(peer, ps->index());
     472             :     }
     473      367821 : }
     474             : 
     475             : //
     476             : // Create and enqueue new WorkRibOut entry since the RibOut is now
     477             : // active.
     478             : //
     479      611982 : void BgpSenderPartition::RibOutActive(RibOut *ribout, int queue_id) {
     480      611982 :     CHECK_CONCURRENCY("db::DBTable", "bgp::SendUpdate", "bgp::PeerMembership");
     481             : 
     482      612094 :     WorkRibOutEnqueue(ribout, queue_id);
     483      612124 : }
     484             : 
     485             : //
     486             : // Mark an IPeerUpdate to be send ready.
     487             : //
     488        5147 : void BgpSenderPartition::PeerSendReady(IPeerUpdate *peer) {
     489        5147 :     CHECK_CONCURRENCY("bgp::SendReadyTask");
     490             : 
     491             :     // The IPeerUpdate may not be registered if it has not reached Established
     492             :     // state or it may already have been unregistered by the time we get around
     493             :     // to processing the notification.
     494        5147 :     PeerState *ps = peer_state_imap_.Find(peer);
     495        5147 :     if (!ps)
     496           0 :         return;
     497             : 
     498             :     // Nothing to do if the IPeerUpdate's already in that state.
     499        5147 :     if (ps->send_ready())
     500        4757 :         return;
     501             : 
     502             :     // Create and enqueue new WorkPeer entry.
     503         390 :     ps->set_send_ready(true);
     504         390 :     WorkPeerEnqueue(peer);
     505             : }
     506             : 
     507             : //
     508             : // Return true if the IPeer is send ready.
     509             : //
     510        2905 : bool BgpSenderPartition::PeerIsSendReady(IPeerUpdate *peer) const {
     511        2905 :     CHECK_CONCURRENCY("bgp::PeerMembership", "bgp::ShowCommand");
     512             : 
     513        2905 :     PeerState *ps = peer_state_imap_.Find(peer);
     514        2905 :     return ps->send_ready();
     515             : }
     516             : 
     517             : //
     518             : // Return true if the IPeer is registered.
     519             : //
     520        9329 : bool BgpSenderPartition::PeerIsRegistered(IPeerUpdate *peer) const {
     521        9329 :     CHECK_CONCURRENCY("bgp::PeerMembership", "bgp::ShowCommand");
     522             : 
     523        9329 :     return (peer_state_imap_.Find(peer) != NULL);
     524             : }
     525             : 
     526             : //
     527             : // Return true if the IPeer is in sync.
     528             : //
     529        7122 : bool BgpSenderPartition::PeerInSync(IPeerUpdate *peer) const {
     530        7122 :     CHECK_CONCURRENCY("bgp::PeerMembership", "bgp::ShowCommand");
     531             : 
     532        7122 :     PeerState *ps = peer_state_imap_.Find(peer);
     533        7122 :     return (ps ? ps->in_sync() : false);
     534             : }
     535             : 
     536             : //
     537             : // Create a Worker if warranted and enqueue it to the TaskScheduler.
     538             : // Assumes that the caller holds the BgpSenderPartition mutex.
     539             : //
     540      612291 : void BgpSenderPartition::MaybeStartWorker() {
     541      612291 :     if (!running_ && !disabled_) {
     542      218529 :         worker_task_ = new Worker(this);
     543      218531 :         TaskScheduler *scheduler = TaskScheduler::GetInstance();
     544      218534 :         scheduler->Enqueue(worker_task_);
     545      218640 :         running_ = true;
     546             :     }
     547      612402 : }
     548             : 
     549             : //
     550             : // Dequeue the first WorkBase item from the work queue and return an
     551             : // unique_ptr to it.  Clear out Worker related state if the work queue
     552             : // is empty.
     553             : //
     554      831235 : unique_ptr<BgpSenderPartition::WorkBase> BgpSenderPartition::WorkDequeue() {
     555      831235 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     556             : 
     557      831161 :     unique_ptr<WorkBase> wentry;
     558      831161 :     if (work_queue_.empty()) {
     559      218599 :         worker_task_ = NULL;
     560      218599 :         running_ = false;
     561             :     } else {
     562      612555 :         wentry.reset(work_queue_.pop_front().release());
     563             :     }
     564      831121 :     return wentry;
     565           0 : }
     566             : 
     567             : //
     568             : // Enqueue a WorkBase entry into the the work queue and start a new Worker
     569             : // task if required.
     570             : //
     571      612474 : void BgpSenderPartition::WorkEnqueue(WorkBase *wentry) {
     572      612474 :     CHECK_CONCURRENCY("db::DBTable", "bgp::SendUpdate", "bgp::SendReadyTask",
     573             :         "bgp::PeerMembership");
     574             : 
     575      612510 :     work_queue_.push_back(wentry);
     576      612285 :     MaybeStartWorker();
     577      612518 : }
     578             : 
     579             : //
     580             : // Disable or enable the worker.
     581             : // For unit testing.
     582             : //
     583          34 : void BgpSenderPartition::set_disabled(bool disabled) {
     584          34 :     disabled_ = disabled;
     585          34 :     MaybeStartWorker();
     586          34 : }
     587             : 
     588             : //
     589             : // Enqueue a WorkPeer to the work queue.
     590             : //
     591         390 : void BgpSenderPartition::WorkPeerEnqueue(IPeerUpdate *peer) {
     592         390 :     CHECK_CONCURRENCY("bgp::SendReadyTask");
     593             : 
     594         390 :     WorkBase *wentry = new WorkPeer(peer);
     595         390 :     WorkEnqueue(wentry);
     596         390 : }
     597             : 
     598             : //
     599             : // Invalidate all WorkBases for the given IPeerUpdate.
     600             : // Used when a IPeerUpdate is removed.
     601             : //
     602       81397 : void BgpSenderPartition::WorkPeerInvalidate(IPeerUpdate *peer) {
     603       81397 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     604             : 
     605       81397 :     for (WorkQueue::iterator it = work_queue_.begin();
     606       81397 :          it != work_queue_.end(); ++it) {
     607           0 :         WorkBase *wentry = it.operator->();
     608           0 :         if (wentry->type != WorkBase::WPeer)
     609           0 :             continue;
     610           0 :         WorkPeer *wpeer = static_cast<WorkPeer *>(wentry);
     611           0 :         if (wpeer->peer != peer)
     612           0 :             continue;
     613           0 :         wpeer->valid = false;
     614             :     }
     615       81397 : }
     616             : 
     617             : //
     618             : // Enqueue a WorkRibOut to the work queue.
     619             : //
     620      612066 : void BgpSenderPartition::WorkRibOutEnqueue(RibOut *ribout, int queue_id) {
     621      612066 :     CHECK_CONCURRENCY("db::DBTable", "bgp::SendUpdate", "bgp::PeerMembership");
     622             : 
     623      612088 :     WorkBase *wentry = new WorkRibOut(ribout, queue_id);
     624      612099 :     WorkEnqueue(wentry);
     625      612128 : }
     626             : 
     627             : //
     628             : // Invalidate all WorkBases for the given RibOut.
     629             : // Used when a RibOut is removed.
     630             : //
     631      135724 : void BgpSenderPartition::WorkRibOutInvalidate(RibOut *ribout) {
     632      135724 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     633             : 
     634      135724 :     for (WorkQueue::iterator it = work_queue_.begin();
     635      135957 :          it != work_queue_.end(); ++it) {
     636         233 :         WorkBase *wentry = it.operator->();
     637         233 :         if (wentry->type != WorkBase::WRibOut)
     638           0 :             continue;
     639         233 :         WorkRibOut *wribout = static_cast<WorkRibOut *>(wentry);
     640         233 :         if (wribout->ribout != ribout)
     641         232 :             continue;
     642           1 :         wribout->valid = false;
     643             :     }
     644      135724 : }
     645             : 
     646             : //
     647             : // Build the RibPeerSet of IPeers for the RibOut that are in sync. Note that
     648             : // we need to use bit indices that are specific to the RibOut, not the ones
     649             : // from the BgpSenderPartition.
     650             : //
     651      612431 : void BgpSenderPartition::BuildSyncBitSet(const RibOut *ribout, RibState *rs,
     652             :     RibPeerSet *msync) {
     653      612431 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     654             : 
     655      612528 :     for (RibState::iterator it = rs->begin(peer_state_imap_);
     656     2543417 :          it != rs->end(peer_state_imap_); ++it) {
     657     1931042 :         PeerState *ps = it.operator->();
     658             : 
     659             :         // If the PeerState is in sync but the IPeerUpdate is not send ready
     660             :         // then update the sync and send ready state in the PeerState.  Note
     661             :         // that the RibOut queue for the PeerState will get marked active via
     662             :         // the call the SetQueueActive from UpdateRibOut.
     663     1930894 :         if (ps->in_sync()) {
     664     1891700 :             if (ps->peer()->send_ready()) {
     665     1891630 :                 int rix = ribout->GetPeerIndex(ps->peer());
     666     1891247 :                 msync->set(rix);
     667             :             } else {
     668          53 :                 ps->clear_sync();
     669          53 :                 ps->set_send_ready(false);
     670             :             }
     671             :         }
     672             :     }
     673      612400 : }
     674             : 
     675             : //
     676             : // Take the RibPeerSet of blocked IPeers and update the relevant PeerStates.
     677             : // Note that bit indices in the RibPeerSet and are specific to the RibOut.
     678             : //
     679      613060 : void BgpSenderPartition::SetSendBlocked(const RibOut *ribout, RibState *rs,
     680             :     int queue_id, const RibPeerSet &blocked) {
     681      613060 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     682             : 
     683      617133 :     for (size_t bit = blocked.find_first(); bit != RibPeerSet::npos;
     684        4138 :          bit = blocked.find_next(bit)) {
     685        4138 :         IPeerUpdate *peer = ribout->GetPeer(bit);
     686        4138 :         PeerState *ps = peer_state_imap_.Find(peer);
     687        4135 :         ps->SetQueueActive(rs->index(), queue_id);
     688        4135 :         ps->clear_sync();
     689        4135 :         ps->set_send_ready(false);
     690             :     }
     691      612989 : }
     692             : 
     693             : //
     694             : // For unit testing only.
     695             : // Take the RibPeerSet of blocked IPeers and update the relevant PeerStates.
     696             : //
     697           8 : void BgpSenderPartition::SetSendBlocked(RibOut *ribout,
     698             :     int queue_id, const RibPeerSet &blocked) {
     699           8 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     700             : 
     701           8 :     RibState *rs = rib_state_imap_.Find(ribout);
     702           8 :     assert(rs);
     703           8 :     SetSendBlocked(ribout, rs, queue_id, blocked);
     704           8 : }
     705             : 
     706             : //
     707             : // Concurrency: called from bgp send task.
     708             : //
     709             : // Take the RibPeerSet of unsync IPeers and update the relevant PeerStates.
     710             : // Note that bit indices in the RibPeerSet and are specific to the RibOut.
     711             : //
     712      612536 : void BgpSenderPartition::SetQueueActive(const RibOut *ribout, RibState *rs,
     713             :     int queue_id, const RibPeerSet &munsync) {
     714      612536 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     715             : 
     716      632822 :     for (size_t bit = munsync.find_first(); bit != RibPeerSet::npos;
     717       20276 :          bit = munsync.find_next(bit)) {
     718       20284 :         IPeerUpdate *peer = ribout->GetPeer(bit);
     719       20282 :         PeerState *ps = peer_state_imap_.Find(peer);
     720       20275 :         ps->SetQueueActive(rs->index(), queue_id);
     721             :     }
     722      612534 : }
     723             : 
     724             : //
     725             : // Concurrency: called from bgp send task.
     726             : //
     727             : // Mark the PeerRibState corresponding to the given IPeerUpdate and RibOut
     728             : // as active.
     729             : //
     730             : // Used by unit test code.
     731             : //
     732           0 : void BgpSenderPartition::SetQueueActive(RibOut *ribout, int queue_id,
     733             :     IPeerUpdate *peer) {
     734           0 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     735             : 
     736           0 :     PeerState *ps = peer_state_imap_.Find(peer);
     737           0 :     RibState *rs = rib_state_imap_.Find(ribout);
     738           0 :     ps->SetQueueActive(rs->index(), queue_id);
     739           0 : }
     740             : 
     741             : //
     742             : // Concurrency: called from bgp send task.
     743             : //
     744             : // Check if the queue corresponding to IPeerUpdate, Ribout and queue id is
     745             : // active.
     746             : //
     747             : // Used by unit test code.
     748             : //
     749           0 : bool BgpSenderPartition::IsQueueActive(RibOut *ribout, int queue_id,
     750             :     IPeerUpdate *peer) {
     751           0 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     752             : 
     753           0 :     PeerState *ps = peer_state_imap_.Find(peer);
     754           0 :     RibState *rs = rib_state_imap_.Find(ribout);
     755           0 :     return ps->IsQueueActive(rs->index(), queue_id);
     756             : }
     757             : 
     758             : //
     759             : // Concurrency: called from bgp send task.
     760             : //
     761             : // Mark all the RibStates for the given peer and queue id as being in sync
     762             : // and trigger a tail dequeue.
     763             : //
     764       10654 : void BgpSenderPartition::SetQueueSync(PeerState *ps, int queue_id) {
     765       10654 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     766             : 
     767       10654 :     for (PeerState::iterator it = ps->begin(rib_state_imap_);
     768       21514 :          it != ps->end(rib_state_imap_); ++it) {
     769       10860 :          RibState *rs = it.rib_state();
     770       10860 :          if (!rs->QueueSync(queue_id)) {
     771          73 :              RibOut *ribout = it.operator->();
     772          73 :              RibOutActive(ribout, queue_id);
     773          73 :              rs->SetQueueSync(queue_id);
     774             :          }
     775             :     }
     776       10654 : }
     777             : 
     778             : //
     779             : // Drain the queue until there are no more updates or all the members become
     780             : // blocked.
     781             : //
     782      612515 : void BgpSenderPartition::UpdateRibOut(RibOut *ribout, int queue_id) {
     783      612515 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     784             : 
     785      612577 :     RibOutUpdates *updates = ribout->updates(index_);
     786      612584 :     RibState *rs = rib_state_imap_.Find(ribout);
     787      612439 :     RibPeerSet msync;
     788             : 
     789             :     // Convert group in-sync list to rib specific bitset.
     790      612457 :     BuildSyncBitSet(ribout, rs, &msync);
     791             : 
     792             :     // Drain the queue till we can do no more.
     793      612404 :     RibPeerSet blocked, munsync;
     794      612409 :     bool done = updates->TailDequeue(queue_id, msync, &blocked, &munsync);
     795      612542 :     assert(msync.Contains(blocked));
     796             : 
     797             :     // Mark peers as send blocked.
     798      612628 :     SetSendBlocked(ribout, rs, queue_id, blocked);
     799             : 
     800             :     // Set the queue to be active for any unsync peers. If we don't do this,
     801             :     // we will forget to mark the (RibOut,QueueId) as active for these peers
     802             :     // since the blocked RibPeerSet does not contain peers that are already
     803             :     // out of sync.  Note that the unsync peers would have been split from
     804             :     // the tail marker in TailDequeue.
     805      612557 :     SetQueueActive(ribout, rs, queue_id, munsync);
     806             : 
     807             :     // If all peers are blocked, mark the queue as unsync in the RibState. We
     808             :     // will trigger tail dequeue for the (RibOut,QueueId) when any peer that
     809             :     // is interested in the RibOut becomes in sync.
     810      612542 :     if (!done)
     811        6451 :         rs->SetQueueUnsync(queue_id);
     812      612545 : }
     813             : 
     814             : //
     815             : // Go through all RibOuts for the IPeerUpdate and drain the given queue till it
     816             : // is up-to date or it becomes blocked. If it's blocked, select the next RibOut
     817             : // to be processed when the IPeerUpdate becomes send ready.
     818             : //
     819             : // Return false if the IPeerUpdate got blocked.
     820             : //
     821         375 : bool BgpSenderPartition::UpdatePeerQueue(IPeerUpdate *peer, PeerState *ps,
     822             :     int queue_id) {
     823         375 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     824             : 
     825         375 :     for (PeerState::circular_iterator it = ps->circular_begin(rib_state_imap_);
     826         828 :          it != ps->circular_end(rib_state_imap_); ++it) {
     827             :         // Skip if this queue is not active in the PeerRibState.
     828         490 :         if (!BitIsSet(it.peer_rib_state().qactive, queue_id))
     829          43 :             continue;
     830             : 
     831             :         // Drain the queue till we can do no more.
     832         447 :         RibOut *ribout = it.operator->();
     833         447 :         RibOutUpdates *updates = ribout->updates(index_);
     834         447 :         RibPeerSet blocked;
     835         447 :         bool done = updates->PeerDequeue(queue_id, peer, &blocked);
     836             : 
     837             :         // Process blocked mask.
     838         447 :         RibState *rs = it.rib_state();
     839         447 :         SetSendBlocked(ribout, rs, queue_id, blocked);
     840             : 
     841             :         // If the peer is still send_ready, mark the queue as inactive for
     842             :         // the peer.  Need to check send_ready because the return value of
     843             :         // PeerDequeue only tells that *some* peer was merged with the tail
     844             :         // marker.
     845             :         // If the peer got blocked, remember where to start next time and
     846             :         // stop processing. We don't want to continue processing for other
     847             :         // merged peers if the lead peer is blocked.  Processing for other
     848             :         // peers will continue when their own WorkPeer items are processed.
     849         447 :         if (ps->send_ready()) {
     850         410 :             assert(done);
     851         410 :             ps->SetQueueInactive(rs->index(), queue_id);
     852             :         } else {
     853          37 :             ps->SetIteratorStart(it.index());
     854          37 :             return false;
     855             :         }
     856         447 :     }
     857             : 
     858         338 :     return true;
     859             : }
     860             : 
     861             : //
     862             : // Drain the queue of all updates for this IPeerUpdate, until it is up-to date
     863             : // or it becomes blocked.
     864             : //
     865        5369 : void BgpSenderPartition::UpdatePeer(IPeerUpdate *peer) {
     866        5369 :     CHECK_CONCURRENCY("bgp::SendUpdate");
     867             : 
     868             :     // Bail if the PeerState is not send ready.
     869        5369 :     PeerState *ps = peer_state_imap_.Find(peer);
     870        5369 :     if (!ps->send_ready()) {
     871           5 :         return;
     872             :     }
     873             : 
     874             :     // Update the PeerState and bail if the IPeerUpdate is not send ready.
     875             :     // This happens if the IPeerUpdate gets blocked while processing some
     876             :     // other partition.
     877        5364 :     if (!peer->send_ready()) {
     878           0 :         ps->set_send_ready(false);
     879           0 :         return;
     880             :     }
     881             : 
     882             :     // Go through all queues and drain them if there's anything on them.
     883       16030 :     for (int queue_id = RibOutUpdates::QCOUNT - 1; queue_id >= 0; --queue_id) {
     884       10703 :         if (ps->QueueCount(queue_id) == 0) {
     885       10328 :             continue;
     886             :         }
     887         375 :         if (!UpdatePeerQueue(peer, ps, queue_id)) {
     888          37 :             assert(!ps->send_ready());
     889          37 :             return;
     890             :         }
     891             :     }
     892             : 
     893             :     // Checking the return value of UpdatePeerQueue above is not sufficient as
     894             :     // that only tells us that *some* peer(s) got merged with the tail marker.
     895             :     // Need to make sure that the IPeerUpdate that we are processing is still
     896             :     // send ready.
     897        5327 :     if (!ps->send_ready()) {
     898           0 :         return;
     899             :     }
     900             : 
     901             :     // Mark the peer as being in sync across all tables.
     902        5327 :     ps->SetSync();
     903             : 
     904             :     // Mark all RibStates for the peer as being in sync. This triggers a tail
     905             :     // dequeue for the corresponding (RibOut, QueueId) if necessary. This in
     906             :     // turn ensures that we do not get stuck in the case where all peers get
     907             :     // blocked and then get back in sync.
     908       15981 :     for (int queue_id = RibOutUpdates::QCOUNT - 1; queue_id >= 0; --queue_id) {
     909       10654 :         SetQueueSync(ps, queue_id);
     910             :     }
     911             : }
     912             : 
     913             : //
     914             : // Check invariants for the BgpSenderPartition.
     915             : //
     916         249 : bool BgpSenderPartition::CheckInvariants() const {
     917         249 :     int grp_peer_count = 0;
     918         249 :     int peer_grp_count = 0;
     919         393 :     for (size_t i = 0; i < rib_state_imap_.size(); i++) {
     920         144 :         if (!rib_state_imap_.bits().test(i)) {
     921           0 :             continue;
     922             :         }
     923         144 :         RibState *rs = rib_state_imap_.At(i);
     924         144 :         assert(rs != NULL);
     925         144 :         CHECK_INVARIANT(rs->index() == i);
     926         144 :         for (RibState::iterator it = rs->begin(peer_state_imap_);
     927         720 :              it != rs->end(peer_state_imap_); ++it) {
     928         576 :             PeerState *ps = it.operator->();
     929         576 :             CHECK_INVARIANT(ps->IsMember(i));
     930         576 :             grp_peer_count++;
     931             :         }
     932             :     }
     933         669 :     for (size_t i = 0; i < peer_state_imap_.size(); i++) {
     934         420 :         if (!peer_state_imap_.bits().test(i)) {
     935           0 :             continue;
     936             :         }
     937         420 :         PeerState *ps = peer_state_imap_.At(i);
     938         420 :         assert(ps != NULL);
     939         420 :         CHECK_INVARIANT(ps->index() == i);
     940         420 :         if (!ps->CheckInvariants()) {
     941           0 :             return false;
     942             :         }
     943         420 :         for (PeerState::iterator it = ps->begin(rib_state_imap_);
     944         996 :              it != ps->end(rib_state_imap_); ++it) {
     945         576 :             RibState *rs = it.rib_state();
     946         576 :             CHECK_INVARIANT(rs->peer_set().test(i));
     947         576 :             peer_grp_count++;
     948             :         }
     949             :     }
     950             : 
     951         249 :     CHECK_INVARIANT(grp_peer_count == peer_grp_count);
     952         249 :     return true;
     953             : }
     954             : 
     955             : //
     956             : // Constructor for BgpUpdateSender.
     957             : // Initialize send ready WorkQueue and allocate BgpSenderPartitions.
     958             : //
     959        9752 : BgpUpdateSender::BgpUpdateSender(BgpServer *server)
     960        9752 :     : server_(server),
     961        9752 :       task_id_(TaskScheduler::GetInstance()->GetTaskId("bgp::SendUpdate")),
     962        9752 :       send_ready_queue_(
     963             :           TaskScheduler::GetInstance()->GetTaskId("bgp::SendReadyTask"), 0,
     964        9752 :           boost::bind(&BgpUpdateSender::SendReadyCallback, this, _1)) {
     965       48235 :     for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
     966       38483 :         partitions_.push_back(new BgpSenderPartition(this, idx));
     967             :     }
     968        9752 : }
     969             : 
     970             : //
     971             : // Destructor for BgpUpdateSender.
     972             : // Shutdown the WorkQueue and delete all BgpSenderPartitions.
     973             : //
     974        9752 : BgpUpdateSender::~BgpUpdateSender() {
     975        9752 :     send_ready_queue_.Shutdown(false);
     976        9752 :     STLDeleteValues(&partitions_);
     977        9752 : }
     978             : 
     979             : //
     980             : // Handle the join of an IPeerUpdate to a RibOut.
     981             : //
     982       93135 : void BgpUpdateSender::Join(RibOut *ribout, IPeerUpdate *peer) {
     983       93135 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     984             : 
     985      828777 :     BOOST_FOREACH(BgpSenderPartition *partition, partitions_) {
     986      367821 :         partition->Add(ribout, peer);
     987             :     }
     988       93135 : }
     989             : 
     990             : //
     991             : // Handle the leave of an IPeerUpdate from a RibOut.
     992             : //
     993       93135 : void BgpUpdateSender::Leave(RibOut *ribout, IPeerUpdate *peer) {
     994       93135 :     CHECK_CONCURRENCY("bgp::PeerMembership");
     995             : 
     996      828777 :     BOOST_FOREACH(BgpSenderPartition *partition, partitions_) {
     997      367821 :         partition->Remove(ribout, peer);
     998             :     }
     999       93135 : }
    1000             : 
    1001             : //
    1002             : // Inform the specified BgpSenderPartition that it needs to schedule a tail
    1003             : // dequeue for the given RibOut queue.
    1004             : //
    1005      611875 : void BgpUpdateSender::RibOutActive(int index, RibOut *ribout, int queue_id) {
    1006      611875 :     CHECK_CONCURRENCY("db::DBTable", "bgp::PeerMembership");
    1007             : 
    1008      611956 :     partitions_[index]->RibOutActive(ribout, queue_id);
    1009      612051 : }
    1010             : 
    1011             : //
    1012             : // Concurrency: called from arbitrary task.
    1013             : //
    1014             : // Enqueue the IPeerUpdate to the send ready processing work queue.
    1015             : // The callback is invoked in the context of bgp::SendReadyTask.
    1016             : //
    1017          14 : void BgpUpdateSender::PeerSendReady(IPeerUpdate *peer) {
    1018          14 :     send_ready_queue_.Enqueue(peer);
    1019          14 : }
    1020             : 
    1021             : //
    1022             : // Return true if the IPeer is registered.
    1023             : //
    1024         543 : bool BgpUpdateSender::PeerIsRegistered(IPeerUpdate *peer) const {
    1025         655 :     BOOST_FOREACH(BgpSenderPartition *partition, partitions_) {
    1026         585 :         if (partition->PeerIsRegistered(peer))
    1027         529 :             return true;
    1028             :     }
    1029          14 :     return false;
    1030             : }
    1031             : 
    1032             : //
    1033             : // Return true if the IPeer is in sync.
    1034             : //
    1035         529 : bool BgpUpdateSender::PeerInSync(IPeerUpdate *peer) const {
    1036        4761 :     BOOST_FOREACH(BgpSenderPartition *partition, partitions_) {
    1037        2116 :         if (!partition->PeerInSync(peer))
    1038           0 :             return false;
    1039             :     }
    1040         529 :     return true;
    1041             : }
    1042             : 
    1043             : //
    1044             : // Callback to handle send ready notification for IPeerUpdate.  Processing it
    1045             : // in the context of bgp::SendeReadyTask ensures that there are no concurrency
    1046             : // issues w.r.t. the BgpSenderPartition working on the IPeerUpdate while we are
    1047             : // processing the notification.
    1048             : //
    1049          14 : bool BgpUpdateSender::SendReadyCallback(IPeerUpdate *peer) {
    1050          14 :     CHECK_CONCURRENCY("bgp::SendReadyTask");
    1051             : 
    1052         114 :     BOOST_FOREACH(BgpSenderPartition *partition, partitions_) {
    1053          50 :         partition->PeerSendReady(peer);
    1054             :     }
    1055          14 :     return true;
    1056             : }
    1057             : 
    1058             : //
    1059             : // Check invariants for the BgpUpdateSender.
    1060             : //
    1061          59 : bool BgpUpdateSender::CheckInvariants() const {
    1062         531 :     BOOST_FOREACH(BgpSenderPartition *partition, partitions_) {
    1063         236 :         if (!partition->CheckInvariants())
    1064           0 :             return false;
    1065             :     }
    1066          59 :     return true;
    1067             : }
    1068             : 
    1069             : //
    1070             : // Disable all BgpSenderPartitions.
    1071             : //
    1072          17 : void BgpUpdateSender::DisableProcessing() {
    1073          51 :     BOOST_FOREACH(BgpSenderPartition *partition, partitions_) {
    1074          17 :         partition->set_disabled(true);
    1075             :     }
    1076          17 : }
    1077             : 
    1078             : //
    1079             : // Enable all BgpSenderPartitions.
    1080             : //
    1081          17 : void BgpUpdateSender::EnableProcessing() {
    1082          51 :     BOOST_FOREACH(BgpSenderPartition *partition, partitions_) {
    1083          17 :         partition->set_disabled(false);
    1084             :     }
    1085          17 : }

Generated by: LCOV version 1.14