Line data Source code
1 : /* 2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. 3 : */ 4 : 5 : #include "bgp/bgp_update_queue.h" 6 : 7 : #include <boost/tuple/tuple.hpp> 8 : 9 : using boost::tie; 10 : 11 : // 12 : // Initialize the UpdateQueue and add the tail marker to the FIFO. 13 : // 14 271360 : UpdateQueue::UpdateQueue(const RibOut *ribout, int queue_id) 15 271360 : : queue_id_(queue_id), 16 271360 : encoding_is_xmpp_(ribout->IsEncodingXmpp()), 17 271360 : tstamp_(0), 18 814080 : marker_count_(0) { 19 271360 : queue_.push_back(tail_marker_); 20 271360 : } 21 : 22 : // 23 : // Destroy the UpdateQueue after removing the tail marker from the FIFO. 24 : // 25 271360 : UpdateQueue::~UpdateQueue() { 26 542720 : queue_.erase(queue_.iterator_to(tail_marker_)); 27 271360 : assert(queue_.empty()); 28 271360 : assert(attr_set_.empty()); 29 271360 : } 30 : 31 : // 32 : // Enqueue the specified RouteUpdate to the UpdateQueue. Updates both the 33 : // FIFO and the set container. 34 : // 35 : // Return true if the UpdateQueue had no RouteUpdates after the tail marker. 36 : // 37 979419 : bool UpdateQueue::Enqueue(RouteUpdate *rt_update) { 38 979419 : rt_update->set_tstamp(++tstamp_); 39 : 40 : // Insert at the end of the FIFO. Remember if the FIFO previously had 41 : // no RouteUpdates after the tail marker. 42 979427 : UpdateEntry *tail_upentry = &(queue_.back()); 43 979324 : bool need_tail_dequeue = (tail_upentry == &tail_marker_); 44 979324 : queue_.push_back(*rt_update); 45 : 46 : // Go through the UpdateInfo list and insert each element into the set 47 : // container for the UpdateQueue. Also set up the back pointer to the 48 : // RouteUpdate. 49 979306 : UpdateInfoSList &uinfo_slist = rt_update->Updates(); 50 979274 : for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin(); 51 3927506 : iter != uinfo_slist->end(); ++iter) { 52 1969318 : iter->update = rt_update; 53 : UpdatesByAttr::iterator set_iter; 54 : bool result; 55 1969318 : tie(set_iter, result) = attr_set_.insert(*iter); 56 984820 : assert(result); 57 : } 58 978850 : return need_tail_dequeue; 59 : } 60 : 61 : // 62 : // Dequeue the specified RouteUpdate from the UpdateQueue. All UpdateInfo 63 : // elements for the RouteUpdate are removed from the set container. 64 : // 65 979104 : void UpdateQueue::Dequeue(RouteUpdate *rt_update) { 66 1958224 : queue_.erase(queue_.iterator_to(*rt_update)); 67 979508 : UpdateInfoSList &uinfo_slist = rt_update->Updates(); 68 979431 : for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin(); 69 1962363 : iter != uinfo_slist->end(); ++iter) { 70 5358 : attr_set_.erase(attr_set_.iterator_to(*iter)); 71 : } 72 979360 : } 73 : 74 : // 75 : // Return the next RouteUpdate after the given UpdateEntry on the UpdateQueue. 76 : // Traverses the FIFO and skips over MARKERs and other element types to find 77 : // the next RouteUpdate. 78 : // 79 : // Return NULL if there's no such RouteUpdate on the FIFO. 80 : // 81 1241431 : RouteUpdate *UpdateQueue::NextUpdate(UpdateEntry *current_upentry) { 82 1241431 : UpdatesByOrder::iterator iter = queue_.iterator_to(*current_upentry); 83 3726683 : while (++iter != queue_.end()) { 84 632658 : UpdateEntry *upentry = iter.operator->(); 85 632658 : if (upentry->IsUpdate()) { 86 631959 : return static_cast<RouteUpdate *>(upentry); 87 : } 88 : } 89 609583 : return NULL; 90 : } 91 : 92 : // 93 : // Return the next UpdateEntry on the UpdateQueue after the one specified. 94 : // Traverses the FIFO and returns the next UpdateEntry, irrespective of the 95 : // type. 96 : // 97 : // Return NULL if there's no such UpdateEntry on the FIFO. 98 : // 99 507 : UpdateEntry *UpdateQueue::NextEntry(UpdateEntry *current_upentry) { 100 507 : UpdatesByOrder::iterator iter = queue_.iterator_to(*current_upentry); 101 1521 : if (++iter == queue_.end()) { 102 0 : return NULL; 103 : } 104 507 : return iter.operator->(); 105 : } 106 : 107 : // 108 : // Dequeue the specified UpdateInfo from the set container. 109 : // 110 982937 : void UpdateQueue::AttrDequeue(UpdateInfo *current_uinfo) { 111 1965919 : attr_set_.erase(attr_set_.iterator_to(*current_uinfo)); 112 983156 : } 113 : 114 : // 115 : // Return the next UpdateInfo after the one provided. This traverses the set 116 : // container and returns the next one if it has the same BgpAttr. Note that 117 : // we must not consider the label in order to ensure optimal packing. 118 : // 119 : // Returns NULL if there are no more updates with the same BgpAttr. This is 120 : // relaxed if the RibOut encoding is XMPP since it's possible to pack items 121 : // with different BgpAttrs in the same message given that each item is self 122 : // contained. 123 : // 124 : // Also return NULL if the next UpdateInfo is for the same RouteUpdate. This 125 : // can happen in corner cases where the label (or the set for ecmp nexthops 126 : // in case of an XMPP ribout) for a route changes between Join operations for 127 : // 2 different sets of IPeerUpdates. Returning such an UpdateInfo results in 128 : // data corruption. 129 : // 130 927169 : UpdateInfo *UpdateQueue::AttrNext(UpdateInfo *current_uinfo) { 131 927169 : UpdatesByAttr::iterator iter = attr_set_.iterator_to(*current_uinfo); 132 : ++iter; 133 1854607 : if (iter == attr_set_.end()) { 134 604545 : return NULL; 135 : } 136 322853 : UpdateInfo *next_uinfo = iter.operator->(); 137 322853 : if (next_uinfo->update == current_uinfo->update) { 138 698 : return NULL; 139 : } 140 322155 : if (encoding_is_xmpp_) { 141 243779 : const RibOutAttr &next_roattr = next_uinfo->roattr; 142 243779 : const RibOutAttr ¤t_roattr = current_uinfo->roattr; 143 243779 : if (next_roattr.IsReachable() == current_roattr.IsReachable()) { 144 243532 : return next_uinfo; 145 : } else { 146 233 : return NULL; 147 : } 148 : } 149 78376 : if (next_uinfo->roattr.attr() == current_uinfo->roattr.attr()) { 150 67934 : return next_uinfo; 151 : } 152 10499 : return NULL; 153 : } 154 : 155 : // 156 : // Add the provided UpdateMarker after a specific RouteUpdate. Also update 157 : // the MarkerList so that all peers in the provided UpdateMarker now point 158 : // to it. 159 : // 160 705 : void UpdateQueue::AddMarker(UpdateMarker *marker, RouteUpdate *rt_update) { 161 705 : assert(!marker->members.empty()); 162 705 : marker_count_++; 163 2115 : queue_.insert(++queue_.iterator_to(*rt_update), *marker); 164 : 165 705 : for (size_t i = marker->members.find_first(); 166 1536 : i != BitSet::npos; i = marker->members.find_next(i)) { 167 831 : assert(markers_[i]); 168 831 : markers_[i] = marker; 169 : } 170 705 : } 171 : 172 : // 173 : // Move the provided UpdateMarker so that it's after the RouteUpdate. Since 174 : // the entire UpdateMarker itself is being moved, there's no need to update 175 : // the MarkerList. 176 : // 177 : // If the UpdateMarker is the tail marker, skip over all markers after the 178 : // RouteUpdate till we hit the next RouteUpdate or the end of the queue. 179 : // This is needed for the case where some peers get blocked after sending 180 : // the last RouteUpdate in the queue. In that scenario, the marker for the 181 : // blocked peers is inserted after the RouteUpdate and we then try to move 182 : // the tail marker after the RouteUpdate. 183 : // 184 609314 : void UpdateQueue::MoveMarker(UpdateMarker *marker, RouteUpdate *rt_update) { 185 1218643 : queue_.erase(queue_.iterator_to(*marker)); 186 : 187 609364 : UpdatesByOrder::iterator iter = queue_.iterator_to(*rt_update); 188 609337 : if (marker == &tail_marker_) { 189 1220536 : for (iter++; iter != queue_.end() && iter->IsMarker(); iter++) { 190 : } 191 1218618 : queue_.insert(iter, *marker); 192 : } else { 193 45 : queue_.insert(++iter, *marker); 194 : } 195 609322 : } 196 : 197 : // 198 : // Split the specified UpdateMarker based on the RibPeerSet. A new marker is 199 : // created for the peers in the RibPeerSet and is inserted immediately before 200 : // the existing marker. The bits corresponding to the RibPeerSet being split 201 : // are reset in the existing marker. 202 : // 203 : // The MarkerList is updated so that all the peers in in the RibPeerSet point 204 : // to the new marker. 205 : // 206 2658 : void UpdateQueue::MarkerSplit(UpdateMarker *marker, const RibPeerSet &msplit) { 207 2658 : assert(!msplit.empty()); 208 2658 : UpdateMarker *split_marker = new UpdateMarker(); 209 2660 : split_marker->members = msplit; 210 : 211 2660 : marker->members.Reset(msplit); 212 2660 : assert(!marker->members.empty()); 213 2660 : UpdatesByOrder::iterator mpos = queue_.iterator_to(*marker); 214 5320 : queue_.insert(mpos, *split_marker); 215 2660 : marker_count_++; 216 : 217 2660 : for (size_t i = msplit.find_first(); 218 5655 : i != BitSet::npos; i = msplit.find_next(i)) { 219 2996 : assert(markers_[i]); 220 2996 : markers_[i] = split_marker; 221 : } 222 2659 : } 223 : 224 : // 225 : // Merge the peers corresponding to the RibPeerSet from the src UpdateMarker 226 : // to the dst. The bits corresponding to the RibPeerSet being merged into the 227 : // dst are reset in the src marker. If the src UpdateMarker has no more peers, 228 : // as a result, it is removed from the FIFO and destroyed. 229 : // 230 84 : void UpdateQueue::MarkerMerge(UpdateMarker *dst_marker, 231 : UpdateMarker *src_marker, const RibPeerSet &bitset) { 232 84 : assert(!bitset.empty()); 233 : 234 : // Set the bits in dst and update the MarkerList. Be sure to set the dst 235 : // before we reset the src since bitset maybe a reference to src->members. 236 84 : dst_marker->members.Set(bitset); 237 84 : for (size_t i = bitset.find_first(); 238 261 : i != BitSet::npos; i = bitset.find_next(i)) { 239 177 : assert(markers_[i]); 240 177 : markers_[i] = dst_marker; 241 : } 242 : 243 : // Reset the bits in the src and get rid of it in case it's now empty. 244 84 : src_marker->members.Reset(bitset); 245 84 : if (src_marker->members.empty()) { 246 168 : queue_.erase(queue_.iterator_to(*src_marker)); 247 84 : assert(src_marker != &tail_marker_); 248 84 : delete src_marker; 249 84 : marker_count_--; 250 : } 251 84 : } 252 : 253 : // 254 : // Return the UpdateMarker for the peer specified by the bit position. 255 : // 256 299 : UpdateMarker *UpdateQueue::GetMarker(int bit) { 257 299 : assert(markers_[bit]); 258 299 : return markers_[bit]; 259 : } 260 : 261 : // 262 : // Join a new peer, as represented by it's bit index, to the UpdateQueue. 263 : // Since it's a new peer, it starts out at the tail marker. Also add the 264 : // peer's bit index and the tail marker pair to the MarkerList, growing 265 : // the MarkerList if necessary. 266 : // 267 : // Return true if the tail marker is not the last entry in the queue. The 268 : // caller should trigger a tail dequeue for the RibOut if so to take care 269 : // of the possibility that all peers in the tail marker are blocked. Note 270 : // that a tail dequeue may already be pending in the BgpUpdateSender, but 271 : // an extra one is harmless. 272 : // 273 735010 : bool UpdateQueue::Join(int bit) { 274 735010 : UpdateMarker *marker = &tail_marker_; 275 735010 : marker->members.set(bit); 276 735010 : if (markers_.size() < (size_t)(bit + 1)) 277 724610 : markers_.resize(bit + 1, NULL); 278 735010 : assert(!markers_[bit]); 279 735010 : markers_[bit] = marker; 280 735010 : return (&tail_marker_ != &queue_.back()); 281 : } 282 : 283 : // 284 : // Leave a peer, as represented by it's bit index, from the UpdateQueue. 285 : // Find the current marker for the peer and clear the peer bit's entry in 286 : // the MarkerList. Don't bother shrinking the MarkerList - the entry can 287 : // be reused by a subsequent Join. 288 : // Reset the peer's bit in the marker and get rid of the marker itself if 289 : // it's now empty. 290 : // 291 735010 : void UpdateQueue::Leave(int bit) { 292 735010 : UpdateMarker *marker = markers_[bit]; 293 735010 : markers_[bit] = NULL; 294 735010 : assert(marker); 295 735010 : marker->members.reset(bit); 296 735010 : if (marker != &tail_marker_ && marker->members.empty()) { 297 6562 : queue_.erase(queue_.iterator_to(*marker)); 298 3281 : delete marker; 299 : } 300 735010 : } 301 : 302 856 : bool UpdateQueue::CheckInvariants() const { 303 20632 : for (size_t idx = 0; idx < markers_.size(); ++idx) { 304 19776 : UpdateMarker *marker = markers_[idx]; 305 19776 : if (!marker) 306 0 : continue; 307 19776 : CHECK_INVARIANT(marker->members.test(idx)); 308 : } 309 856 : return true; 310 : } 311 : 312 : // 313 : // Return true if the UpdateQueue is empty. It's considered empty if there 314 : // are as no UpdateInfo elements in the set container. We don't look at the 315 : // FIFO since that may still have the tail marker on it. 316 : // 317 16 : bool UpdateQueue::empty() const { 318 16 : return attr_set_.empty(); 319 : } 320 : 321 13494 : size_t UpdateQueue::size() const { 322 13494 : return attr_set_.size(); 323 : } 324 : 325 640912 : size_t UpdateQueue::marker_count() const { 326 640912 : return marker_count_; 327 : }