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 129 : RouteUpdatePtr::RouteUpdatePtr(RouteUpdate *rt_update) : rt_update_(rt_update) {
13 129 : }
14 :
15 309 : RouteUpdatePtr::~RouteUpdatePtr() {
16 309 : }
17 :
18 1648 : RibUpdateMonitor::RibUpdateMonitor(RibOut *ribout, QueueVec *queue_vec)
19 1648 : : ribout_(ribout), queue_vec_(queue_vec) {
20 1648 : }
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 0 : DBState *RibUpdateMonitor::GetRouteUpdateAndDequeue(DBEntryBase *db_entry,
29 : RouteUpdate *rt_update, UpdateCmp cmp, bool *duplicate) {
30 0 : 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 0 : if (rt_update->queue_id() == RibOutUpdates::QUPDATE) {
35 0 : *duplicate = cmp(rt_update);
36 0 : if (*duplicate) {
37 0 : 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 0 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
45 0 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
46 0 : queue->Dequeue(rt_update);
47 0 : rt_update->set_queue_id(RibOutUpdates::QUPDATE);
48 0 : 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 0 : DBState *RibUpdateMonitor::GetUpdateListAndDequeue(DBEntryBase *db_entry,
66 : UpdateList *uplist) {
67 0 : CHECK_CONCURRENCY("db::DBTable");
68 :
69 : DBState *dbstate;
70 0 : RouteUpdate *rt_update = uplist->FindUpdate(RibOutUpdates::QUPDATE);
71 0 : 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 0 : uplist->RemoveUpdate(rt_update);
76 0 : queue_vec_->at(rt_update->queue_id())->Dequeue(rt_update);
77 0 : uplist->MoveHistory(rt_update);
78 0 : dbstate = rt_update;
79 0 : } 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 0 : RouteState *rstate = new RouteState();
83 0 : uplist->MoveHistory(rstate);
84 0 : 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 0 : 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 0 : UpdateList::List *list = uplist->GetList();
96 0 : for (UpdateList::List::iterator iter = list->begin();
97 0 : iter != list->end(); iter++) {
98 0 : RouteUpdate *temp_rt_update = *iter;
99 0 : queue_vec_->at(temp_rt_update->queue_id())->Dequeue(temp_rt_update);
100 0 : delete temp_rt_update;
101 : }
102 :
103 : // Update the DBState and return it.
104 0 : if (dbstate) {
105 0 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
106 : } else {
107 0 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
108 : }
109 0 : 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 124 : DBState *RibUpdateMonitor::GetDBStateAndDequeue(DBEntryBase *db_entry,
126 : UpdateCmp cmp, bool *duplicate) {
127 124 : CHECK_CONCURRENCY("db::DBTable");
128 :
129 124 : *duplicate = false;
130 :
131 : // Don't need to bother going through the monitor if there's no DBState.
132 : DBState *dbstate =
133 124 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
134 124 : if (dbstate == NULL) {
135 107 : 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 17 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
145 17 : if (dbstate == NULL) {
146 0 : return dbstate;
147 : }
148 :
149 : // Return if it's a RouteState.
150 17 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
151 17 : if (rstate != NULL) {
152 17 : return rstate;
153 : }
154 :
155 : // Handle the case where it's a RouteUpdate.
156 0 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
157 0 : if (rt_update != NULL) {
158 0 : return GetRouteUpdateAndDequeue(db_entry, rt_update, cmp,
159 0 : duplicate);
160 : }
161 :
162 : // Handle the case where it's a UpdateList.
163 0 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
164 : DBState *db_state;
165 0 : assert(uplist);
166 0 : db_state = GetUpdateListAndDequeue(db_entry, uplist);
167 0 : delete uplist;
168 0 : 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 40 : static void RouteStateCurrent(const RouteState *rstate,
180 : RibPeerSet *mcurrent) {
181 40 : CHECK_CONCURRENCY("db::DBTable");
182 :
183 40 : const AdvertiseSList &adv_slist = rstate->Advertised();
184 40 : for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
185 160 : iter != adv_slist->end(); ++iter) {
186 80 : mcurrent->Set(iter->bitset);
187 : }
188 40 : }
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 0 : static void RouteUpdateCurrentAndScheduled(const RouteUpdate *rt_update,
198 : int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
199 0 : CHECK_CONCURRENCY("db::DBTable");
200 :
201 0 : const AdvertiseSList &adv_slist = rt_update->History();
202 0 : for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
203 0 : iter != adv_slist->end(); ++iter) {
204 0 : mcurrent->Set(iter->bitset);
205 : }
206 :
207 0 : if (queue_id != RibOutUpdates::QCOUNT && queue_id != rt_update->queue_id())
208 0 : return;
209 :
210 0 : const UpdateInfoSList &uinfo_slist = rt_update->Updates();
211 0 : for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
212 0 : iter != uinfo_slist->end(); ++iter) {
213 0 : 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 0 : static void UpdateListCurrentAndScheduled(const UpdateList *uplist,
223 : int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
224 0 : CHECK_CONCURRENCY("db::DBTable");
225 :
226 0 : const AdvertiseSList &adv_slist = uplist->History();
227 0 : for (AdvertiseSList::List::const_iterator iter = adv_slist->begin();
228 0 : iter != adv_slist->end(); ++iter) {
229 0 : mcurrent->Set(iter->bitset);
230 : }
231 :
232 0 : const UpdateList::List *list = uplist->GetList();
233 0 : for (UpdateList::List::const_iterator iter = list->begin();
234 0 : iter != list->end(); ++iter) {
235 0 : RouteUpdateCurrentAndScheduled(*iter, queue_id, mcurrent, mscheduled);
236 : }
237 0 : }
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 80 : bool RibUpdateMonitor::GetPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
249 : int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled) {
250 80 : CHECK_CONCURRENCY("db::DBTable");
251 :
252 : // Don't need to bother going through the monitor if there's no DBState.
253 : DBState *dbstate =
254 80 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
255 80 : if (dbstate == NULL) {
256 40 : 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 40 : DBState *dbstate = db_entry->GetState(ribout_->table(),
265 40 : ribout_->listener_id());
266 40 : if (dbstate == NULL) {
267 0 : return false;
268 : }
269 :
270 : // Handle the case where it's a RouteState.
271 40 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
272 40 : if (rstate != NULL) {
273 40 : RouteStateCurrent(rstate, mcurrent);
274 40 : return false;
275 : }
276 :
277 : // Handle the case where it's a RouteUpdate.
278 0 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
279 0 : if (rt_update != NULL) {
280 0 : RouteUpdateCurrentAndScheduled(rt_update, queue_id,
281 : mcurrent, mscheduled);
282 0 : break;
283 : }
284 :
285 : // Handle the case where it's a UpdateList.
286 0 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
287 0 : if (uplist != NULL) {
288 0 : UpdateListCurrentAndScheduled(uplist, queue_id,
289 : mcurrent, mscheduled);
290 0 : break;
291 : }
292 :
293 : // Unknown DBState.
294 0 : assert(false);
295 : }
296 :
297 0 : 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 0 : bool RibUpdateMonitor::RouteStateMergeUpdate(DBEntryBase *db_entry,
311 : RouteUpdate *rt_update, RouteState *rstate) {
312 0 : CHECK_CONCURRENCY("db::DBTable");
313 :
314 0 : rstate->MoveHistory(rt_update);
315 0 : delete rstate;
316 0 : 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 0 : bool RibUpdateMonitor::RouteUpdateMergeUpdate(DBEntryBase *db_entry,
332 : RouteUpdate *rt_update, RouteUpdate *current_rt_update) {
333 0 : CHECK_CONCURRENCY("db::DBTable");
334 :
335 0 : if (current_rt_update->queue_id() == rt_update->queue_id()) {
336 0 : UpdateQueue *queue = queue_vec_->at(current_rt_update->queue_id());
337 0 : queue->Dequeue(current_rt_update);
338 0 : current_rt_update->MergeUpdateInfo(rt_update->Updates());
339 0 : assert(rt_update->Updates()->empty());
340 0 : delete rt_update;
341 0 : return EnqueueUpdate(db_entry, current_rt_update);
342 : } else {
343 0 : UpdateList *uplist = current_rt_update->MakeUpdateList();
344 0 : uplist->AddUpdate(rt_update);
345 0 : 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 0 : bool RibUpdateMonitor::UpdateListMergeUpdate(DBEntryBase *db_entry,
362 : RouteUpdate *rt_update, UpdateList *uplist) {
363 0 : CHECK_CONCURRENCY("db::DBTable");
364 :
365 0 : RouteUpdate *current_rt_update = uplist->FindUpdate(rt_update->queue_id());
366 0 : if (current_rt_update) {
367 0 : UpdateQueue *queue = queue_vec_->at(current_rt_update->queue_id());
368 0 : queue->Dequeue(current_rt_update);
369 0 : current_rt_update->MergeUpdateInfo(rt_update->Updates());
370 0 : assert(rt_update->Updates()->empty());
371 0 : delete rt_update;
372 0 : return EnqueueUpdate(db_entry, current_rt_update, uplist);
373 : } else {
374 0 : uplist->AddUpdate(rt_update);
375 0 : 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 0 : bool RibUpdateMonitor::MergeUpdate(DBEntryBase *db_entry,
388 : RouteUpdate *rt_update) {
389 0 : 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 0 : 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 0 : if (dbstate == NULL) {
402 0 : return EnqueueUpdate(db_entry, rt_update);
403 : }
404 :
405 : // Handle the case where it's a RouteState.
406 0 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
407 0 : if (rstate != NULL) {
408 0 : return RouteStateMergeUpdate(db_entry, rt_update, rstate);
409 : }
410 :
411 : // Handle the case where it's a RouteUpdate.
412 0 : RouteUpdate *current_rt_update = dynamic_cast<RouteUpdate *>(dbstate);
413 0 : if (current_rt_update != NULL) {
414 0 : return RouteUpdateMergeUpdate(db_entry, rt_update,
415 0 : current_rt_update);
416 : }
417 :
418 : // Handle the case where it's a UpdateList.
419 0 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
420 0 : if (uplist != NULL) {
421 0 : 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 17 : void RibUpdateMonitor::AdvertiseSListClearBits(AdvertiseSList &adv_slist,
438 : const RibPeerSet &clear) {
439 17 : CHECK_CONCURRENCY("db::DBTable");
440 :
441 17 : for (AdvertiseSList::List::iterator iter = adv_slist->begin();
442 68 : iter != adv_slist->end(); ) {
443 17 : iter->bitset.Reset(clear);
444 17 : if (iter->bitset.empty()) {
445 51 : iter = adv_slist->erase_and_dispose(iter, AdvertiseInfoDisposer());
446 : } else {
447 0 : iter++;
448 : }
449 : }
450 17 : }
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 0 : void RibUpdateMonitor::UpdateInfoSListClearBits(UpdateInfoSList &uinfo_slist,
458 : const RibPeerSet &clear) {
459 0 : CHECK_CONCURRENCY("db::DBTable");
460 :
461 0 : for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin();
462 0 : iter != uinfo_slist->end(); ) {
463 0 : iter->target.Reset(clear);
464 0 : if (iter->target.empty()) {
465 0 : RouteUpdate *rt_update = iter->update;
466 0 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
467 0 : queue->AttrDequeue(iter.operator->());
468 0 : iter = uinfo_slist->erase_and_dispose(iter, UpdateInfoDisposer());
469 : } else {
470 0 : iter++;
471 : }
472 : }
473 0 : }
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 17 : void RibUpdateMonitor::RouteStateClearPeerSet(DBEntryBase *db_entry,
482 : RouteState *rstate, const RibPeerSet &mleave) {
483 17 : CHECK_CONCURRENCY("db::DBTable");
484 :
485 : // Clear the bits for each element in the AdvertiseSList.
486 17 : AdvertiseSListClearBits(rstate->Advertised(), mleave);
487 :
488 : // Get rid of the RouteState itself if it's empty.
489 17 : if (rstate->Advertised()->empty()) {
490 17 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
491 17 : delete rstate;
492 : }
493 17 : }
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 0 : bool RibUpdateMonitor::RouteUpdateClearPeerSet(DBEntryBase *db_entry,
504 : RouteUpdate *rt_update, const RibPeerSet &mleave) {
505 0 : CHECK_CONCURRENCY("db::DBTable");
506 :
507 : // Clear the bits for each element in the AdvertiseSList.
508 0 : AdvertiseSListClearBits(rt_update->History(), mleave);
509 :
510 : // Clear the bits for each element in the UpdateInfoSList.
511 0 : UpdateInfoSListClearBits(rt_update->Updates(), mleave);
512 :
513 : // Update the DBstate for the DBentry as appropriate.
514 0 : if (!rt_update->Updates()->empty()) {
515 : // There are more scheduled updates, do nothing.
516 0 : return false;
517 : }
518 :
519 0 : 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 0 : DequeueUpdate(rt_update);
524 0 : RouteState *rstate = new RouteState;
525 0 : rt_update->MoveHistory(rstate);
526 0 : 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 0 : DequeueUpdate(rt_update);
531 0 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
532 : }
533 :
534 0 : 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 0 : bool RibUpdateMonitor::UpdateListClearPeerSet(DBEntryBase *db_entry,
544 : UpdateList *uplist, const RibPeerSet &mleave) {
545 0 : CHECK_CONCURRENCY("db::DBTable");
546 :
547 0 : UpdateList::List *list = uplist->GetList();
548 :
549 : // Clear the bits for each element in the AdvertiseSList.
550 0 : 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 0 : for (UpdateList::List::iterator iter = list->begin();
556 0 : iter != list->end(); ) {
557 0 : RouteUpdate *rt_update = *iter++;
558 0 : UpdateInfoSListClearBits(rt_update->Updates(), mleave);
559 0 : if (rt_update->Updates()->empty()) {
560 0 : uplist->RemoveUpdate(rt_update);
561 0 : DequeueUpdate(rt_update);
562 0 : delete rt_update;
563 : }
564 : }
565 :
566 : // Update the DBstate for the DBentry as appropriate.
567 0 : if (list->size() > 1) {
568 : // There's multiple RouteUpdates on the UpdateList, do nothing.
569 0 : return false;
570 0 : } 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 0 : RouteUpdate *rt_update = uplist->MakeRouteUpdate();
576 0 : assert(rt_update);
577 0 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
578 0 : } 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 0 : RouteState *rstate = new RouteState;
583 0 : uplist->MoveHistory(rstate);
584 0 : 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 0 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
589 : }
590 :
591 0 : 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 17 : void RibUpdateMonitor::ClearPeerSetCurrentAndScheduled(DBEntryBase *db_entry,
599 : const RibPeerSet &mleave) {
600 17 : CHECK_CONCURRENCY("db::DBTable");
601 :
602 : // Don't need to bother going through the monitor if there's no DBState.
603 : DBState *dbstate =
604 17 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
605 17 : 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 17 : db_entry->GetState(ribout_->table(), ribout_->listener_id());
615 :
616 : // Handle the case where there's no DBState.
617 17 : if (dbstate == NULL) {
618 0 : return;
619 : }
620 :
621 : // Handle the case where it's a RouteState.
622 17 : RouteState *rstate = dynamic_cast<RouteState *>(dbstate);
623 17 : if (rstate != NULL) {
624 17 : RouteStateClearPeerSet(db_entry, rstate, mleave);
625 17 : return;
626 : }
627 :
628 : // Handle the case where it's a RouteUpdate.
629 0 : RouteUpdate *rt_update = dynamic_cast<RouteUpdate *>(dbstate);
630 0 : if (rt_update != NULL) {
631 : bool delete_rt_update =
632 0 : RouteUpdateClearPeerSet(db_entry, rt_update, mleave);
633 0 : if (delete_rt_update) {
634 0 : delete rt_update;
635 : }
636 0 : return;
637 : }
638 :
639 : // Handle the case where it's a UpdateList.
640 0 : UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
641 : bool delete_uplist;
642 :
643 0 : assert(uplist);
644 0 : delete_uplist = UpdateListClearPeerSet(db_entry, uplist, mleave);
645 0 : if (delete_uplist) {
646 0 : delete uplist;
647 : }
648 0 : 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 51 : bool RibUpdateMonitor::EnqueueUpdate(DBEntryBase *db_entry,
664 : RouteUpdate *rt_update, UpdateList *uplist) {
665 51 : CHECK_CONCURRENCY("db::DBTable");
666 :
667 51 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
668 51 : if (uplist) {
669 0 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), uplist);
670 : } else {
671 51 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), rt_update);
672 : }
673 51 : return queue->Enqueue(rt_update);
674 : }
675 :
676 : //
677 : // Dequeue the specified RouteUpdate from it's UpdateQueue.
678 : //
679 51 : void RibUpdateMonitor::DequeueUpdate(RouteUpdate *rt_update) {
680 51 : CHECK_CONCURRENCY("db::DBTable", "bgp::SendUpdate");
681 :
682 51 : UpdateQueue *queue = queue_vec_->at(rt_update->queue_id());
683 51 : queue->Dequeue(rt_update);
684 51 : }
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 78 : RouteUpdatePtr RibUpdateMonitor::GetNextUpdate(int queue_id,
696 : UpdateEntry *upentry) {
697 78 : CHECK_CONCURRENCY("bgp::SendUpdate");
698 :
699 78 : UpdateQueue *queue = queue_vec_->at(queue_id);
700 78 : RouteUpdate *next_rt_update = queue->NextUpdate(upentry);
701 78 : RouteUpdatePtr update(next_rt_update);
702 78 : if (!next_rt_update && upentry->IsUpdate()) {
703 39 : RouteUpdate *rt_update = static_cast<RouteUpdate *>(upentry);
704 39 : queue->MoveMarker(queue->tail_marker(), rt_update);
705 : }
706 78 : 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 0 : RouteUpdatePtr RibUpdateMonitor::GetNextEntry(int queue_id,
718 : UpdateEntry *upentry, UpdateEntry **next_upentry_p) {
719 0 : CHECK_CONCURRENCY("bgp::SendUpdate");
720 :
721 0 : UpdateQueue *queue = queue_vec_->at(queue_id);
722 0 : UpdateEntry *next_upentry = *next_upentry_p = queue->NextEntry(upentry);
723 0 : if (next_upentry != NULL && next_upentry->IsUpdate()) {
724 0 : RouteUpdate *rt_update = static_cast<RouteUpdate *>(next_upentry);
725 0 : RouteUpdatePtr update(rt_update);
726 0 : return update;
727 0 : }
728 0 : 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 51 : RouteUpdatePtr RibUpdateMonitor::GetAttrNext(int queue_id,
740 : UpdateInfo *current_uinfo, UpdateInfo **next_uinfo_p) {
741 51 : CHECK_CONCURRENCY("bgp::SendUpdate");
742 :
743 51 : UpdateQueue *queue = queue_vec_->at(queue_id);
744 51 : UpdateInfo *next_uinfo = queue->AttrNext(current_uinfo);
745 51 : RouteUpdate *rt_update = NULL;
746 51 : if (next_uinfo) {
747 12 : rt_update = next_uinfo->update;
748 : }
749 51 : RouteUpdatePtr update(rt_update);
750 51 : *next_uinfo_p = next_uinfo;
751 51 : return update;
752 : }
753 :
754 : //
755 : // Set the listener state for the DBEntryBase to be the DBState.
756 : //
757 34 : void RibUpdateMonitor::SetEntryState(DBEntryBase *db_entry, DBState *dbstate) {
758 34 : CHECK_CONCURRENCY("bgp::SendUpdate");
759 :
760 34 : db_entry->SetState(ribout_->table(), ribout_->listener_id(), dbstate);
761 34 : }
762 :
763 : //
764 : // Clear the listener state for the DBEntryBase.
765 : //
766 17 : void RibUpdateMonitor::ClearEntryState(DBEntryBase *db_entry) {
767 17 : CHECK_CONCURRENCY("bgp::SendUpdate");
768 :
769 17 : db_entry->ClearState(ribout_->table(), ribout_->listener_id());
770 17 : }
|