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 : }
|