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_monitor.h"
7 :
8 : #include "base/task_annotations.h"
9 : #include "bgp/bgp_ribout_updates.h"
10 : #include "bgp/bgp_table.h"
11 : #include "bgp/bgp_update_queue.h"
12 :
13 2162486 : RouteUpdatePtr::RouteUpdatePtr(RouteUpdate *rt_update) : rt_update_(rt_update) {
14 2162486 : }
15 :
16 5237208 : RouteUpdatePtr::~RouteUpdatePtr() {
17 5237208 : }
18 :
19 136016 : RibUpdateMonitor::RibUpdateMonitor(RibOut *ribout, QueueVec *queue_vec)
20 136016 : : ribout_(ribout), queue_vec_(queue_vec) {
21 136016 : }
22 :
23 : //
24 : // Helper routine for GetRouteStateAndDequeue to handle a RouteUpdate. Do
25 : // nothing if it's a duplicate.
26 : //
27 : // Return a pointer to the DBState and update duplicate as appropriate.
28 : //
29 1855 : DBState *RibUpdateMonitor::GetRouteUpdateAndDequeue(DBEntryBase *db_entry,
30 : RouteUpdate *rt_update, UpdateCmp cmp, bool *duplicate) {
31 1855 : CHECK_CONCURRENCY("db::DBTable");
32 :
33 : // Compare desired state with current update. If the desired state
34 : // is a NOP just return without modifying the queue.
35 1855 : if (rt_update->queue_id() == RibOutUpdates::QUPDATE) {
36 1760 : *duplicate = cmp(rt_update);
37 1759 : if (*duplicate) {
38 290 : return NULL;
39 : }
40 : }
41 :
42 : // Set the state on the DBEntryBase and remove the RouteUpdate from
43 : // the queue. Change the queue to QUPDATE since that's where we will
44 : // re-enqueue the RouteUpdate, if we do it.
45 1564 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
46 1565 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
47 1565 : queue->Dequeue(rt_update);
48 1565 : rt_update->set_queue_id(RibOutUpdates::QUPDATE);
49 1565 : return rt_update;
50 : }
51 :
52 : //
53 : // Helper routine for GetRouteStateAndDequeue to handle a UpdateList.
54 : //
55 : // Since an update operation overrides any join/refresh state, get rid of
56 : // RouteUpdates that are not for QUPDATE.
57 : //
58 : // Note that we should NOT check for duplicate state in QUPDATE and bail.
59 : // Doing so could result in a failure to remove pending RouteUpdates from
60 : // QBULK which are no longer required.
61 : //
62 : // Return a pointer to the RouteUpdate for QUPDATE if it exists, else
63 : // Return a pointer to new RouteState if the UpdateList has history, else
64 : // Return NULL.
65 : //
66 60 : DBState *RibUpdateMonitor::GetUpdateListAndDequeue(DBEntryBase *db_entry,
67 : UpdateList *uplist) {
68 60 : CHECK_CONCURRENCY("db::DBTable");
69 :
70 : DBState *dbstate;
71 60 : RouteUpdate *rt_update = uplist->FindUpdate(RibOutUpdates::QUPDATE);
72 60 : if (rt_update) {
73 : // There's a RouteUpdate for QUPDATE. Remove it from the UpdateList
74 : // and move the history to it. The entry will be reused as if that
75 : // was the DBState instead of the UpdateList.
76 30 : uplist->RemoveUpdate(rt_update);
77 30 : queue_vec_->at(rt_update->queue_id())->Dequeue(rt_update);
78 30 : uplist->MoveHistory(rt_update);
79 30 : dbstate = rt_update;
80 30 : } else if (!uplist->History()->empty()) {
81 : // There's no RouteUpdate for QUPDATE, but the history is not empty.
82 : // Move the history to a new RouteState.
83 20 : RouteState *rstate = new RouteState();
84 20 : uplist->MoveHistory(rstate);
85 20 : dbstate = rstate;
86 : } else {
87 : // There's no RouteUpdate for QUPDATE and the history is empty. We
88 : // can simply pretend that there was no DBState after we get rid
89 : // of the UpdateList.
90 10 : dbstate = NULL;
91 : }
92 :
93 : // Get rid of all other RouteUpdates and the UpdateList itself. Note
94 : // that the RouteUpdate for QUPDATE, if any, has already been removed
95 : // from the UpdateList at this point.
96 60 : UpdateList::List *list = uplist->GetList();
97 60 : for (UpdateList::List::iterator iter = list->begin();
98 120 : iter != list->end(); iter++) {
99 60 : RouteUpdate *temp_rt_update = *iter;
100 60 : queue_vec_->at(temp_rt_update->queue_id())->Dequeue(temp_rt_update);
101 60 : delete temp_rt_update;
102 : }
103 :
104 : // Update the DBState and return it.
105 60 : if (dbstate) {
106 50 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
107 : } else {
108 10 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
109 : }
110 60 : return dbstate;
111 : }
112 :
113 : //
114 : // Get the DBState for the DBEntryBase and dequeue it from an UpdateQueue
115 : // if required. The DBState could be a RouteState, a RouteUpdate or an
116 : // UpdateList. If it's a RouteState, it won't be on an UpdateQueue. If
117 : // it's a RouteUpdate or an UpdateList, see if it's a duplicate i.e. the
118 : // desired state is the same as the currently advertised state.
119 : //
120 : // This routine is used by the export module to obtain exclusive access to
121 : // the RouteUpdate or UpdateList. The access is exclusive by definition if
122 : // there's no DBState or it's a RouteState.
123 : //
124 : // Return a pointer to the DBState and update duplicate as appropriate.
125 : //
126 1472774 : DBState *RibUpdateMonitor::GetDBStateAndDequeue(DBEntryBase *db_entry,
127 : UpdateCmp cmp, bool *duplicate) {
128 1472774 : CHECK_CONCURRENCY("db::DBTable");
129 :
130 1472758 : *duplicate = false;
131 :
132 : // Don't need to bother going through the monitor if there's no DBState.
133 : DBState *dbstate =
134 1472758 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
135 1473959 : if (dbstate == NULL) {
136 926152 : return dbstate;
137 : }
138 :
139 : // Go through the monitor and handle the DBState as appropriate. Note
140 : // that we still need to check for the DBState being NULL as things may
141 : // have changed after the check made above.
142 : while (true) {
143 : // Get the DBState; bail if there's no existing state.
144 : DBState *dbstate =
145 547807 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
146 547809 : if (dbstate == NULL) {
147 0 : return dbstate;
148 : }
149 :
150 : // Return if it's a RouteState.
151 547809 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
152 547809 : if (rstate != NULL) {
153 545886 : return rstate;
154 : }
155 :
156 : // Handle the case where it's a RouteUpdate.
157 1923 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
158 1923 : if (rt_update != NULL) {
159 3710 : return GetRouteUpdateAndDequeue(db_entry, rt_update, cmp,
160 1855 : duplicate);
161 : }
162 :
163 : // Handle the case where it's a UpdateList.
164 68 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
165 : DBState *db_state;
166 68 : assert(uplist);
167 68 : db_state = GetUpdateListAndDequeue(db_entry, uplist);
168 60 : delete uplist;
169 60 : return db_state;
170 : }
171 :
172 : assert(false);
173 : return NULL;
174 : }
175 :
176 : //
177 : // Helper routine for GetPeerSetCurrentAndScheduled to handle a RouteState.
178 : // Go through all the AdvertiseInfo to build the bitset.
179 : //
180 456564 : static void RouteStateCurrent(const RouteState *rstate,
181 : RibPeerSet *mcurrent) {
182 456564 : CHECK_CONCURRENCY("db::DBTable");
183 :
184 455940 : const AdvertiseSList &adv_slist = rstate->Advertised();
185 455919 : for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
186 2063330 : iter != adv_slist->end(); ++iter) {
187 1152364 : mcurrent->Set(iter->bitset);
188 : }
189 455295 : }
190 :
191 : //
192 : // Helper routine for GetPeerSetCurrentAndScheduled to handle a RouteUpdate.
193 : // Go through all the AdvertiseInfo and UpdateInfo to build the bitset.
194 : //
195 : // Do not consider the UpdateInfos if the given queue_id does not match the
196 : // one in the RouteUpdate. Note that QCOUNT is considered to be a wildcard.
197 : //
198 156117 : static void RouteUpdateCurrentAndScheduled(const RouteUpdate *rt_update,
199 : int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
200 156117 : CHECK_CONCURRENCY("db::DBTable");
201 :
202 156008 : const AdvertiseSList &adv_slist = rt_update->History();
203 156007 : for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
204 671982 : iter != adv_slist->end(); ++iter) {
205 360222 : mcurrent->Set(iter->bitset);
206 : }
207 :
208 155834 : if (queue_id != RibOutUpdates::QCOUNT && queue_id != rt_update->queue_id())
209 11578 : return;
210 :
211 144256 : const UpdateInfoSList &uinfo_slist = rt_update->Updates();
212 144410 : for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
213 579723 : iter != uinfo_slist->end(); ++iter) {
214 290990 : mscheduled->Set(iter->target);
215 : }
216 : }
217 :
218 : //
219 : // Helper routine for GetPeerSetCurrentAndScheduled to handle a UpdateList.
220 : // Go through all AdvertiseInfo in the UpdateList and then each RouteUpdate
221 : // in the UpdateList to build the bitset.
222 : //
223 140 : static void UpdateListCurrentAndScheduled(const UpdateList *uplist,
224 : int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
225 140 : CHECK_CONCURRENCY("db::DBTable");
226 :
227 140 : const AdvertiseSList &adv_slist = uplist->History();
228 140 : for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
229 650 : iter != adv_slist->end(); ++iter) {
230 370 : mcurrent->Set(iter->bitset);
231 : }
232 :
233 140 : const UpdateList::List *list = uplist->GetList();
234 140 : for (UpdateList::List::const_iterator iter = list->begin();
235 415 : iter != list->end(); ++iter) {
236 275 : RouteUpdateCurrentAndScheduled(*iter, queue_id, mcurrent, mscheduled);
237 : }
238 140 : }
239 :
240 : //
241 : // Build the RibPeerSet of peers that are currently advertising the given
242 : // DBEntryBase or are scheduled to advertise it. The mcurrent parameter is
243 : // filled with the contents of the advertised bitmask (history) while the
244 : // mscheduled parameter is filled with the bitmask of updates in the queue.
245 : //
246 : // Return false if there's no associated DBState or if it's a RouteState
247 : // i.e. there's no pending state to be advertised.
248 : //
249 863811 : bool RibUpdateMonitor::GetPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
250 : int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
251 863811 : CHECK_CONCURRENCY("db::DBTable");
252 :
253 : // Don't need to bother going through the monitor if there's no DBState.
254 : DBState *dbstate =
255 863706 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
256 864830 : if (dbstate == NULL) {
257 252260 : return false;
258 : }
259 :
260 : // Go through the monitor and handle the DBState as appropriate. Note
261 : // that we still need to check for the DBState being NULL as things may
262 : // have changed after the check made above.
263 : while (true) {
264 : // Get the DBState; bail if there's no existing state.
265 612517 : DBState *dbstate = db_entry->GetState(ribout_->table(),
266 612570 : ribout_->listener_id());
267 612586 : if (dbstate == NULL) {
268 0 : return false;
269 : }
270 :
271 : // Handle the case where it's a RouteState.
272 612586 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
273 612586 : if (rstate != NULL) {
274 456580 : RouteStateCurrent(rstate, mcurrent);
275 455703 : return false;
276 : }
277 :
278 : // Handle the case where it's a RouteUpdate.
279 156006 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
280 156006 : if (rt_update != NULL) {
281 155869 : RouteUpdateCurrentAndScheduled(rt_update, queue_id,
282 : mcurrent, mscheduled);
283 155692 : break;
284 : }
285 :
286 : // Handle the case where it's a UpdateList.
287 137 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
288 137 : if (uplist != NULL) {
289 140 : UpdateListCurrentAndScheduled(uplist, queue_id,
290 : mcurrent, mscheduled);
291 140 : break;
292 : }
293 :
294 : // Unknown DBState.
295 0 : assert(false);
296 : }
297 :
298 155832 : return true;
299 : }
300 :
301 : //
302 : // Helper routine for MergeUpdate to handle case where the current DBState
303 : // is a RouteState.
304 : //
305 : // Move the previous history from the RouteState to the RouteUpdate and
306 : // enqueue it.
307 : //
308 : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
309 : // the (RibOut, QueueId).
310 : //
311 53122 : bool RibUpdateMonitor::RouteStateMergeUpdate(DBEntryBase *db_entry,
312 : RouteUpdate *rt_update, RouteState *rstate) {
313 53122 : CHECK_CONCURRENCY("db::DBTable");
314 :
315 53104 : rstate->MoveHistory(rt_update);
316 53104 : delete rstate;
317 53109 : return EnqueueUpdate(db_entry, rt_update);
318 : }
319 :
320 : //
321 : // Helper routine for MergeUpdate to handle case where the current DBState
322 : // is a RouteUpdate.
323 : //
324 : // If the new RouteUpdate is for the same queue as the current RouteUpdate,
325 : // dequeue the current, merge in the UpdateInfo from the new and enqueue it
326 : // again. Otherwise, build an UpdateList containing both RouteUpdates and
327 : // enqueue the new RouteUpdate.
328 : //
329 : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
330 : // the (RibOut, QueueId).
331 : //
332 47 : bool RibUpdateMonitor::RouteUpdateMergeUpdate(DBEntryBase *db_entry,
333 : RouteUpdate *rt_update, RouteUpdate *current_rt_update) {
334 47 : CHECK_CONCURRENCY("db::DBTable");
335 :
336 47 : if (current_rt_update->queue_id() == rt_update->queue_id()) {
337 37 : UpdateQueue *queue = queue_vec_->at(current_rt_update->queue_id());
338 37 : queue->Dequeue(current_rt_update);
339 37 : current_rt_update->MergeUpdateInfo(rt_update->Updates());
340 37 : assert(rt_update->Updates()->empty());
341 37 : delete rt_update;
342 37 : return EnqueueUpdate(db_entry, current_rt_update);
343 : } else {
344 10 : UpdateList *uplist = current_rt_update->MakeUpdateList();
345 10 : uplist->AddUpdate(rt_update);
346 10 : return EnqueueUpdate(db_entry, rt_update, uplist);
347 : }
348 : }
349 :
350 : //
351 : // Helper routine for MergeUpdate to handle case where the current DBState
352 : // is a UpdateList.
353 : //
354 : // Figure out if the UpdateList already has a RouteUpdate for this queue.
355 : // If so, dequeue the existing RouteUpdate, merge the UpdateInfo from the
356 : // new one and enqueue the existing RouteUpdate again. Otherwise, add the
357 : // new RouteUpdate to the UpdateList and enqueue the RouteUpdate.
358 : //
359 : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
360 : // the (RibOut, QueueId).
361 : //
362 25 : bool RibUpdateMonitor::UpdateListMergeUpdate(DBEntryBase *db_entry,
363 : RouteUpdate *rt_update, UpdateList *uplist) {
364 25 : CHECK_CONCURRENCY("db::DBTable");
365 :
366 25 : RouteUpdate *current_rt_update = uplist->FindUpdate(rt_update->queue_id());
367 25 : if (current_rt_update) {
368 20 : UpdateQueue *queue = queue_vec_->at(current_rt_update->queue_id());
369 20 : queue->Dequeue(current_rt_update);
370 20 : current_rt_update->MergeUpdateInfo(rt_update->Updates());
371 20 : assert(rt_update->Updates()->empty());
372 20 : delete rt_update;
373 20 : return EnqueueUpdate(db_entry, current_rt_update, uplist);
374 : } else {
375 5 : uplist->AddUpdate(rt_update);
376 5 : return EnqueueUpdate(db_entry, rt_update, uplist);
377 : }
378 : }
379 :
380 : //
381 : // Take the desired state as represented by the RouteUpdate and merge it with
382 : // any previously advertised state for the DBEntryBase. Note that the previous
383 : // state could be for a different queue and/or other peers in the RibOut.
384 : //
385 : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
386 : // the (RibOut, QueueId).
387 : //
388 79396 : bool RibUpdateMonitor::MergeUpdate(DBEntryBase *db_entry,
389 : RouteUpdate *rt_update) {
390 79396 : CHECK_CONCURRENCY("db::DBTable");
391 :
392 : // Go through the monitor and handle the DBState as necessary. Need
393 : // to use the monitor even if there's no DBState in order to protect
394 : // against race conditions which could result in an incorrect return
395 : // value.
396 : while (true) {
397 : DBState *dbstate =
398 79405 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
399 :
400 : // Handle the case where there's no DBState. Simply add the new
401 : // RouteUpdate to the queue.
402 79426 : if (dbstate == NULL) {
403 26230 : return EnqueueUpdate(db_entry, rt_update);
404 : }
405 :
406 : // Handle the case where it's a RouteState.
407 53196 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
408 53196 : if (rstate != NULL) {
409 53123 : return RouteStateMergeUpdate(db_entry, rt_update, rstate);
410 : }
411 :
412 : // Handle the case where it's a RouteUpdate.
413 73 : RouteUpdate *current_rt_update = dynamic_cast<RouteUpdate *>(dbstate);
414 73 : if (current_rt_update != NULL) {
415 47 : return RouteUpdateMergeUpdate(db_entry, rt_update,
416 47 : current_rt_update);
417 : }
418 :
419 : // Handle the case where it's a UpdateList.
420 26 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
421 26 : if (uplist != NULL) {
422 25 : return UpdateListMergeUpdate(db_entry, rt_update, uplist);
423 : }
424 :
425 : // Unknown DBState.
426 1 : assert(false);
427 : }
428 :
429 : assert(false);
430 : return false;
431 : }
432 :
433 : //
434 : // Traipse through all the AdvertiseInfo elements in the list and clear the
435 : // RibPeerSet. If the RibPeerSet in an element becomes empty, remove it from
436 : // the list container and get rid of the element.
437 : //
438 261203 : void RibUpdateMonitor::AdvertiseSListClearBits(AdvertiseSList &adv_slist,
439 : const RibPeerSet &clear) {
440 261203 : CHECK_CONCURRENCY("db::DBTable");
441 :
442 261199 : for (AdvertiseSList::List::iterator iter = adv_slist->begin();
443 1172287 : iter != adv_slist->end(); ) {
444 324967 : iter->bitset.Reset(clear);
445 324870 : if (iter->bitset.empty()) {
446 548337 : iter = adv_slist->erase_and_dispose(iter, AdvertiseInfoDisposer());
447 : } else {
448 142162 : iter++;
449 : }
450 : }
451 261043 : }
452 :
453 : //
454 : // Traipse through all the UpdateInfo elements in the list and clear the
455 : // RibPeerSet. If the RibPeerSet in an element becomes empty, remove it
456 : // from the set and list containers and get rid of the element.
457 : //
458 61102 : void RibUpdateMonitor::UpdateInfoSListClearBits(UpdateInfoSList &uinfo_slist,
459 : const RibPeerSet &clear) {
460 61102 : CHECK_CONCURRENCY("db::DBTable");
461 :
462 61087 : for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin();
463 245721 : iter != uinfo_slist->end(); ) {
464 61723 : iter->target.Reset(clear);
465 61714 : if (iter->target.empty()) {
466 60394 : RouteUpdate *rt_update = iter->update;
467 60394 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
468 120816 : queue->AttrDequeue(iter.operator->());
469 181357 : iter = uinfo_slist->erase_and_dispose(iter, UpdateInfoDisposer());
470 : } else {
471 1322 : iter++;
472 : }
473 : }
474 61099 : }
475 :
476 : //
477 : // Helper routine for ClearPeerSetCurrentAndScheduled to handle case where
478 : // the current DBState is a RouteState.
479 : //
480 : // Clean up the AdvertiseInfos corresponding to the peers in RibPeerSet.
481 : //
482 200408 : void RibUpdateMonitor::RouteStateClearPeerSet(DBEntryBase *db_entry,
483 : RouteState *rstate, const RibPeerSet &mleave) {
484 200408 : CHECK_CONCURRENCY("db::DBTable");
485 :
486 : // Clear the bits for each element in the AdvertiseSList.
487 200224 : AdvertiseSListClearBits(rstate->Advertised(), mleave);
488 :
489 : // Get rid of the RouteState itself if it's empty.
490 200254 : if (rstate->Advertised()->empty()) {
491 104507 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
492 104585 : delete rstate;
493 : }
494 200237 : }
495 :
496 : //
497 : // Helper routine for ClearPeerSetCurrentAndScheduled to handle case where
498 : // the current DBState is a RouteUpdate.
499 : //
500 : // Clean up the UpdateInfos corresponding to the peers in RibPeerSet.
501 : //
502 : // Return true if rt_update has to be deleted, false otherwise
503 : //
504 61042 : bool RibUpdateMonitor::RouteUpdateClearPeerSet(DBEntryBase *db_entry,
505 : RouteUpdate *rt_update, const RibPeerSet &mleave) {
506 61042 : CHECK_CONCURRENCY("db::DBTable");
507 :
508 : // Clear the bits for each element in the AdvertiseSList.
509 60937 : AdvertiseSListClearBits(rt_update->History(), mleave);
510 :
511 : // Clear the bits for each element in the UpdateInfoSList.
512 60949 : UpdateInfoSListClearBits(rt_update->Updates(), mleave);
513 :
514 : // Update the DBstate for the DBentry as appropriate.
515 60990 : if (!rt_update->Updates()->empty()) {
516 : // There are more scheduled updates, do nothing.
517 1182 : return false;
518 : }
519 :
520 59796 : if (!rt_update->History()->empty()) {
521 : // No more scheduled updates but there are current updates. Dequeue
522 : // the RouteUpdate, move the history to a new RouteState and get rid
523 : // of the RouteUpdate.
524 14724 : DequeueUpdate(rt_update);
525 14727 : RouteState *rstate = new RouteState;
526 14727 : rt_update->MoveHistory(rstate);
527 14725 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rstate);
528 : } else {
529 : // No more scheduled or current updates. Dequeue the RouteUpdate,
530 : // clear the state on the DBEntry and get rid of the RouteUpdate.
531 45063 : DequeueUpdate(rt_update);
532 45047 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
533 : }
534 :
535 59855 : return true;
536 : }
537 :
538 : //
539 : // Helper routine for ClearPeerSetCurrentAndScheduled to handle case where
540 : // the current DBState is a UpdateList.
541 : //
542 : // Return true if uplist has to be deleted, false otherwise
543 : //
544 80 : bool RibUpdateMonitor::UpdateListClearPeerSet(DBEntryBase *db_entry,
545 : UpdateList *uplist, const RibPeerSet &mleave) {
546 80 : CHECK_CONCURRENCY("db::DBTable");
547 :
548 80 : UpdateList::List *list = uplist->GetList();
549 :
550 : // Clear the bits for each element in the AdvertiseSList.
551 80 : AdvertiseSListClearBits(uplist->History(), mleave);
552 :
553 : // Clear the bits for each element in the UpdateInfoSList for each
554 : // RouteUpdate. If a RouteUpdate becomes empty as a result, get rid
555 : // of it.
556 80 : for (UpdateList::List::iterator iter = list->begin();
557 240 : iter != list->end(); ) {
558 160 : RouteUpdate *rt_update = *iter++;
559 160 : UpdateInfoSListClearBits(rt_update->Updates(), mleave);
560 160 : if (rt_update->Updates()->empty()) {
561 80 : uplist->RemoveUpdate(rt_update);
562 80 : DequeueUpdate(rt_update);
563 80 : delete rt_update;
564 : }
565 : }
566 :
567 : // Update the DBstate for the DBentry as appropriate.
568 80 : if (list->size() > 1) {
569 : // There's multiple RouteUpdates on the UpdateList, do nothing.
570 30 : return false;
571 50 : } else if (list->size() == 1) {
572 : // There's exactly 1 RouteUpdate on the UpdateList. Downgrade
573 : // the UpdateList to a RouteUpdate and get rid of UpdateList.
574 : // Note that the history will be moved to the RouteUpdate as
575 : // part of the downgrade.
576 20 : RouteUpdate *rt_update = uplist->MakeRouteUpdate();
577 20 : assert(rt_update);
578 20 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
579 30 : } else if (!uplist->History()->empty()) {
580 : // There's no RouteUpdates on the UpdateList, but the history
581 : // is not empty. Move the history to a new RouteState and get
582 : // rid of the UpdateList.
583 20 : RouteState *rstate = new RouteState;
584 20 : uplist->MoveHistory(rstate);
585 20 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rstate);
586 : } else {
587 : // There's no RouteUpdates on the UpdateList and the history is
588 : // empty. Clear state on the DBEntry and get rid of UpdateList.
589 10 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
590 : }
591 :
592 50 : return true;
593 : }
594 :
595 : //
596 : // Cancel all scheduled updates and clean up AdvertiseInfo corresponding
597 : // to any current updates for the peers in the RibPeerSet.
598 : //
599 261101 : void RibUpdateMonitor::ClearPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
600 : const RibPeerSet &mleave) {
601 261101 : CHECK_CONCURRENCY("db::DBTable");
602 :
603 : // Don't need to bother going through the monitor if there's no DBState.
604 : DBState *dbstate =
605 261194 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
606 261513 : if (dbstate == NULL) {
607 0 : return;
608 : }
609 :
610 : // Go through the monitor and handle the DBState as appropriate. Note
611 : // that we still need to check for the DBState being NULL as things may
612 : // have changed after the check made above.
613 : while (true) {
614 : DBState *dbstate =
615 261513 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
616 :
617 : // Handle the case where there's no DBState.
618 261529 : if (dbstate == NULL) {
619 0 : return;
620 : }
621 :
622 : // Handle the case where it's a RouteState.
623 261529 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
624 261529 : if (rstate != NULL) {
625 200407 : RouteStateClearPeerSet(db_entry, rstate, mleave);
626 200223 : return;
627 : }
628 :
629 : // Handle the case where it's a RouteUpdate.
630 61122 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
631 61122 : if (rt_update != NULL) {
632 : bool delete_rt_update =
633 61042 : RouteUpdateClearPeerSet(db_entry, rt_update, mleave);
634 61036 : if (delete_rt_update) {
635 59854 : delete rt_update;
636 : }
637 61032 : return;
638 : }
639 :
640 : // Handle the case where it's a UpdateList.
641 80 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
642 : bool delete_uplist;
643 :
644 80 : assert(uplist);
645 80 : delete_uplist = UpdateListClearPeerSet(db_entry, uplist, mleave);
646 80 : if (delete_uplist) {
647 50 : delete uplist;
648 : }
649 80 : return;
650 : }
651 :
652 : assert(false);
653 : }
654 :
655 : //
656 : // Enqueue the specified RouteUpdate to the UpdateQueue and set the listener
657 : // state for the for the DBEntry to point to the RouteUpdate.
658 : // Set the listener state to the UpdateList if it is non-NULL, else set it to
659 : // the RouteUpdate.
660 : //
661 : // Return true if the BgpUpdateSender needs to trigger a tail dequeue for
662 : // the (RibOut, QueueId).
663 : //
664 979458 : bool RibUpdateMonitor::EnqueueUpdate(DBEntryBase *db_entry,
665 : RouteUpdate *rt_update, UpdateList *uplist) {
666 979458 : CHECK_CONCURRENCY("db::DBTable");
667 :
668 979505 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
669 979497 : if (uplist) {
670 35 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), uplist);
671 : } else {
672 979462 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
673 : }
674 979960 : return queue->Enqueue(rt_update);
675 : }
676 :
677 : //
678 : // Dequeue the specified RouteUpdate from it's UpdateQueue.
679 : //
680 978020 : void RibUpdateMonitor::DequeueUpdate(RouteUpdate *rt_update) {
681 978020 : CHECK_CONCURRENCY("db::DBTable", "bgp::SendUpdate");
682 :
683 978029 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
684 978021 : queue->Dequeue(rt_update);
685 978121 : }
686 :
687 : //
688 : // Get the next RouteUpdate after the provided UpdateEntry and return
689 : // the RouteUpdatePtr encapsulator for it.
690 : //
691 : // If the next RouteUpdate is NULL, move the tail marker to be after
692 : // the UpdateEntry. The tail marker move must be done must be done
693 : // atomically with returning a NULL RouteUpdatePtr. This ensures that
694 : // Enqueue can correctly detect the need to trigger a tail dequeue.
695 : //
696 1234283 : RouteUpdatePtr RibUpdateMonitor::GetNextUpdate(int queue_id,
697 : UpdateEntry *upentry) {
698 1234283 : CHECK_CONCURRENCY("bgp::SendUpdate");
699 :
700 1234202 : UpdateQueue *queue = queue_vec_->at(queue_id);
701 1234209 : RouteUpdate *next_rt_update = queue->NextUpdate(upentry);
702 1234248 : RouteUpdatePtr update(next_rt_update);
703 1234408 : if (!next_rt_update && upentry->IsUpdate()) {
704 604650 : RouteUpdate *rt_update = static_cast<RouteUpdate *>(upentry);
705 604650 : queue->MoveMarker(queue->tail_marker(), rt_update);
706 : }
707 1234383 : return update;
708 0 : }
709 :
710 : //
711 : // Get the next UpdateEntry after the one provided and return it via
712 : // the output parameter.
713 : //
714 : // Return the RouteUpdatePtr encapsulator for the next UpdateEntry
715 : // if it's a RouteUpdate. If it's not, return an encapsulator for
716 : // a NULL RouteUpdate.
717 : //
718 507 : RouteUpdatePtr RibUpdateMonitor::GetNextEntry(int queue_id,
719 : UpdateEntry *upentry, UpdateEntry **next_upentry_p) {
720 507 : CHECK_CONCURRENCY("bgp::SendUpdate");
721 :
722 507 : UpdateQueue *queue = queue_vec_->at(queue_id);
723 507 : UpdateEntry *next_upentry = *next_upentry_p = queue->NextEntry(upentry);
724 507 : if (next_upentry != NULL && next_upentry->IsUpdate()) {
725 419 : RouteUpdate *rt_update = static_cast<RouteUpdate *>(next_upentry);
726 419 : RouteUpdatePtr update(rt_update);
727 419 : return update;
728 419 : }
729 88 : return RouteUpdatePtr();
730 : }
731 :
732 : //
733 : // Get the next UpdateInfo after the one provided and return it via
734 : // the output parameter.
735 : //
736 : // Return the RouteUpdatePtr encapsulator for the RouteUpdate that
737 : // corresponds to the next UpdateInfo. If there's no next UpdateInfo
738 : // return an encapsulator for a NULL RouteUpdate.
739 : //
740 927953 : RouteUpdatePtr RibUpdateMonitor::GetAttrNext(int queue_id,
741 : UpdateInfo *current_uinfo, UpdateInfo **next_uinfo_p) {
742 927953 : CHECK_CONCURRENCY("bgp::SendUpdate");
743 :
744 927813 : UpdateQueue *queue = queue_vec_->at(queue_id);
745 927802 : UpdateInfo *next_uinfo = queue->AttrNext(current_uinfo);
746 927958 : RouteUpdate *rt_update = NULL;
747 927958 : if (next_uinfo) {
748 315513 : rt_update = next_uinfo->update;
749 : }
750 927958 : RouteUpdatePtr update(rt_update);
751 927965 : *next_uinfo_p = next_uinfo;
752 927965 : return update;
753 : }
754 :
755 : //
756 : // Set the listener state for the DBEntryBase to be the DBState.
757 : //
758 584115 : void RibUpdateMonitor::SetEntryState(DBEntryBase *db_entry, DBState *dbstate) {
759 584115 : CHECK_CONCURRENCY("bgp::SendUpdate");
760 :
761 584149 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
762 584262 : }
763 :
764 : //
765 : // Clear the listener state for the DBEntryBase.
766 : //
767 333976 : void RibUpdateMonitor::ClearEntryState(DBEntryBase *db_entry) {
768 333976 : CHECK_CONCURRENCY("bgp::SendUpdate");
769 :
770 333984 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
771 334150 : }
|