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 2166192 : RouteUpdatePtr::RouteUpdatePtr(RouteUpdate *rt_update) : rt_update_(rt_update) {
13 2166192 : }
14 :
15 5240826 : RouteUpdatePtr::~RouteUpdatePtr() {
16 5240826 : }
17 :
18 135844 : RibUpdateMonitor::RibUpdateMonitor(RibOut *ribout, QueueVec *queue_vec)
19 135844 : : ribout_(ribout), queue_vec_(queue_vec) {
20 135844 : }
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 1921 : DBState *RibUpdateMonitor::GetRouteUpdateAndDequeue(DBEntryBase *db_entry,
29 : RouteUpdate *rt_update, UpdateCmp cmp, bool *duplicate) {
30 1921 : 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 1921 : if (rt_update->queue_id() == RibOutUpdates::QUPDATE) {
35 1808 : *duplicate = cmp(rt_update);
36 1808 : if (*duplicate) {
37 293 : 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 1628 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
45 1628 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
46 1627 : queue->Dequeue(rt_update);
47 1628 : rt_update->set_queue_id(RibOutUpdates::QUPDATE);
48 1628 : 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 1461711 : DBState *RibUpdateMonitor::GetDBStateAndDequeue(DBEntryBase *db_entry,
126 : UpdateCmp cmp, bool *duplicate) {
127 1461711 : CHECK_CONCURRENCY("db::DBTable");
128 :
129 1461540 : *duplicate = false;
130 :
131 : // Don't need to bother going through the monitor if there's no DBState.
132 : DBState *dbstate =
133 1461540 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
134 1463000 : if (dbstate == NULL) {
135 911224 : 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 551776 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
145 551797 : if (dbstate == NULL) {
146 0 : return dbstate;
147 : }
148 :
149 : // Return if it's a RouteState.
150 551797 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
151 551797 : if (rstate != NULL) {
152 549819 : return rstate;
153 : }
154 :
155 : // Handle the case where it's a RouteUpdate.
156 1978 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
157 1978 : if (rt_update != NULL) {
158 3842 : return GetRouteUpdateAndDequeue(db_entry, rt_update, cmp,
159 1921 : duplicate);
160 : }
161 :
162 : // Handle the case where it's a UpdateList.
163 57 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
164 : DBState *db_state;
165 57 : assert(uplist);
166 57 : 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 440267 : static void RouteStateCurrent(const RouteState *rstate,
180 : RibPeerSet *mcurrent) {
181 440267 : CHECK_CONCURRENCY("db::DBTable");
182 :
183 439667 : const AdvertiseSList &adv_slist = rstate->Advertised();
184 439639 : for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
185 1992360 : iter != adv_slist->end(); ++iter) {
186 1113670 : mcurrent->Set(iter->bitset);
187 : }
188 439202 : }
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 163122 : static void RouteUpdateCurrentAndScheduled(const RouteUpdate *rt_update,
198 : int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
199 163122 : CHECK_CONCURRENCY("db::DBTable");
200 :
201 162819 : const AdvertiseSList &adv_slist = rt_update->History();
202 162819 : for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
203 700497 : iter != adv_slist->end(); ++iter) {
204 375148 : mcurrent->Set(iter->bitset);
205 : }
206 :
207 162606 : if (queue_id != RibOutUpdates::QCOUNT && queue_id != rt_update->queue_id())
208 12597 : return;
209 :
210 150007 : const UpdateInfoSList &uinfo_slist = rt_update->Updates();
211 150272 : for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
212 602904 : iter != uinfo_slist->end(); ++iter) {
213 302422 : 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 847856 : bool RibUpdateMonitor::GetPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
249 : int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
250 847856 : CHECK_CONCURRENCY("db::DBTable");
251 :
252 : // Don't need to bother going through the monitor if there's no DBState.
253 : DBState *dbstate =
254 847506 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
255 848788 : if (dbstate == NULL) {
256 245622 : 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 603086 : DBState *dbstate = db_entry->GetState(ribout_->table(),
265 603166 : ribout_->listener_id());
266 603255 : if (dbstate == NULL) {
267 0 : return false;
268 : }
269 :
270 : // Handle the case where it's a RouteState.
271 603255 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
272 603255 : if (rstate != NULL) {
273 440315 : RouteStateCurrent(rstate, mcurrent);
274 439517 : return false;
275 : }
276 :
277 : // Handle the case where it's a RouteUpdate.
278 162940 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
279 162940 : if (rt_update != NULL) {
280 162862 : RouteUpdateCurrentAndScheduled(rt_update, queue_id,
281 : mcurrent, mscheduled);
282 162589 : break;
283 : }
284 :
285 : // Handle the case where it's a UpdateList.
286 78 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
287 78 : 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 162729 : 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 53046 : bool RibUpdateMonitor::RouteStateMergeUpdate(DBEntryBase *db_entry,
311 : RouteUpdate *rt_update, RouteState *rstate) {
312 53046 : CHECK_CONCURRENCY("db::DBTable");
313 :
314 52996 : rstate->MoveHistory(rt_update);
315 53000 : delete rstate;
316 53001 : 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 79026 : bool RibUpdateMonitor::MergeUpdate(DBEntryBase *db_entry,
388 : RouteUpdate *rt_update) {
389 79026 : 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 79028 : 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 79082 : if (dbstate == NULL) {
402 25958 : return EnqueueUpdate(db_entry, rt_update);
403 : }
404 :
405 : // Handle the case where it's a RouteState.
406 53124 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
407 53124 : if (rstate != NULL) {
408 53052 : 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 259323 : void RibUpdateMonitor::AdvertiseSListClearBits(AdvertiseSList &adv_slist,
438 : const RibPeerSet &clear) {
439 259323 : CHECK_CONCURRENCY("db::DBTable");
440 :
441 259342 : for (AdvertiseSList::List::iterator iter = adv_slist->begin();
442 1161730 : iter != adv_slist->end(); ) {
443 321538 : iter->bitset.Reset(clear);
444 321453 : if (iter->bitset.empty()) {
445 547113 : iter = adv_slist->erase_and_dispose(iter, AdvertiseInfoDisposer());
446 : } else {
447 139151 : iter++;
448 : }
449 : }
450 259186 : }
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 67295 : void RibUpdateMonitor::UpdateInfoSListClearBits(UpdateInfoSList &uinfo_slist,
458 : const RibPeerSet &clear) {
459 67295 : CHECK_CONCURRENCY("db::DBTable");
460 :
461 67265 : for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin();
462 270240 : iter != uinfo_slist->end(); ) {
463 67809 : iter->target.Reset(clear);
464 67773 : if (iter->target.empty()) {
465 66447 : RouteUpdate *rt_update = iter->update;
466 66447 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
467 132928 : queue->AttrDequeue(iter.operator->());
468 199587 : iter = uinfo_slist->erase_and_dispose(iter, UpdateInfoDisposer());
469 : } else {
470 1325 : iter++;
471 : }
472 : }
473 67255 : }
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 192340 : void RibUpdateMonitor::RouteStateClearPeerSet(DBEntryBase *db_entry,
482 : RouteState *rstate, const RibPeerSet &mleave) {
483 192340 : CHECK_CONCURRENCY("db::DBTable");
484 :
485 : // Clear the bits for each element in the AdvertiseSList.
486 192179 : AdvertiseSListClearBits(rstate->Advertised(), mleave);
487 :
488 : // Get rid of the RouteState itself if it's empty.
489 192214 : if (rstate->Advertised()->empty()) {
490 100422 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
491 100469 : delete rstate;
492 : }
493 192182 : }
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 67221 : bool RibUpdateMonitor::RouteUpdateClearPeerSet(DBEntryBase *db_entry,
504 : RouteUpdate *rt_update, const RibPeerSet &mleave) {
505 67221 : CHECK_CONCURRENCY("db::DBTable");
506 :
507 : // Clear the bits for each element in the AdvertiseSList.
508 67118 : AdvertiseSListClearBits(rt_update->History(), mleave);
509 :
510 : // Clear the bits for each element in the UpdateInfoSList.
511 67122 : UpdateInfoSListClearBits(rt_update->Updates(), mleave);
512 :
513 : // Update the DBstate for the DBentry as appropriate.
514 67168 : if (!rt_update->Updates()->empty()) {
515 : // There are more scheduled updates, do nothing.
516 1187 : return false;
517 : }
518 :
519 65961 : 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 15733 : DequeueUpdate(rt_update);
524 15719 : RouteState *rstate = new RouteState;
525 15712 : rt_update->MoveHistory(rstate);
526 15728 : 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 50224 : DequeueUpdate(rt_update);
531 50215 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
532 : }
533 :
534 66056 : 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 259337 : void RibUpdateMonitor::ClearPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
599 : const RibPeerSet &mleave) {
600 259337 : CHECK_CONCURRENCY("db::DBTable");
601 :
602 : // Don't need to bother going through the monitor if there's no DBState.
603 : DBState *dbstate =
604 259309 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
605 259677 : 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 259677 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
615 :
616 : // Handle the case where there's no DBState.
617 259683 : if (dbstate == NULL) {
618 0 : return;
619 : }
620 :
621 : // Handle the case where it's a RouteState.
622 259683 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
623 259683 : if (rstate != NULL) {
624 192366 : RouteStateClearPeerSet(db_entry, rstate, mleave);
625 192180 : return;
626 : }
627 :
628 : // Handle the case where it's a RouteUpdate.
629 67317 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
630 67317 : if (rt_update != NULL) {
631 : bool delete_rt_update =
632 67247 : RouteUpdateClearPeerSet(db_entry, rt_update, mleave);
633 67243 : if (delete_rt_update) {
634 66056 : delete rt_update;
635 : }
636 67224 : return;
637 : }
638 :
639 : // Handle the case where it's a UpdateList.
640 70 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
641 : bool delete_uplist;
642 :
643 70 : assert(uplist);
644 70 : 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 982095 : bool RibUpdateMonitor::EnqueueUpdate(DBEntryBase *db_entry,
664 : RouteUpdate *rt_update, UpdateList *uplist) {
665 982095 : CHECK_CONCURRENCY("db::DBTable");
666 :
667 982112 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
668 982083 : if (uplist) {
669 35 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), uplist);
670 : } else {
671 982048 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
672 : }
673 982723 : return queue->Enqueue(rt_update);
674 : }
675 :
676 : //
677 : // Dequeue the specified RouteUpdate from it's UpdateQueue.
678 : //
679 980738 : void RibUpdateMonitor::DequeueUpdate(RouteUpdate *rt_update) {
680 980738 : CHECK_CONCURRENCY("db::DBTable", "bgp::SendUpdate");
681 :
682 980777 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
683 980762 : queue->Dequeue(rt_update);
684 980800 : }
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 1241559 : RouteUpdatePtr RibUpdateMonitor::GetNextUpdate(int queue_id,
696 : UpdateEntry *upentry) {
697 1241559 : CHECK_CONCURRENCY("bgp::SendUpdate");
698 :
699 1241315 : UpdateQueue *queue = queue_vec_->at(queue_id);
700 1241299 : RouteUpdate *next_rt_update = queue->NextUpdate(upentry);
701 1241307 : RouteUpdatePtr update(next_rt_update);
702 1241645 : if (!next_rt_update && upentry->IsUpdate()) {
703 607773 : RouteUpdate *rt_update = static_cast<RouteUpdate *>(upentry);
704 607773 : queue->MoveMarker(queue->tail_marker(), rt_update);
705 : }
706 1241639 : 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 924604 : RouteUpdatePtr RibUpdateMonitor::GetAttrNext(int queue_id,
740 : UpdateInfo *current_uinfo, UpdateInfo **next_uinfo_p) {
741 924604 : CHECK_CONCURRENCY("bgp::SendUpdate");
742 :
743 924444 : UpdateQueue *queue = queue_vec_->at(queue_id);
744 924451 : UpdateInfo *next_uinfo = queue->AttrNext(current_uinfo);
745 924580 : RouteUpdate *rt_update = NULL;
746 924580 : if (next_uinfo) {
747 309533 : rt_update = next_uinfo->update;
748 : }
749 924580 : RouteUpdatePtr update(rt_update);
750 924591 : *next_uinfo_p = next_uinfo;
751 924591 : return update;
752 : }
753 :
754 : //
755 : // Set the listener state for the DBEntryBase to be the DBState.
756 : //
757 582097 : void RibUpdateMonitor::SetEntryState(DBEntryBase *db_entry, DBState *dbstate) {
758 582097 : CHECK_CONCURRENCY("bgp::SendUpdate");
759 :
760 582109 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
761 582308 : }
762 :
763 : //
764 : // Clear the listener state for the DBEntryBase.
765 : //
766 332487 : void RibUpdateMonitor::ClearEntryState(DBEntryBase *db_entry) {
767 332487 : CHECK_CONCURRENCY("bgp::SendUpdate");
768 :
769 332490 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
770 332683 : }
|