Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 : #include <assert.h>
5 : #include <cmn/agent_cmn.h>
6 : #include <route/route.h>
7 : #include <vnc_cfg_types.h>
8 : #include <agent_types.h>
9 : #include <cmn/agent_db.h>
10 : #include <oper/agent_route_walker.h>
11 : #include <oper/vrf.h>
12 : #include <oper/agent_route.h>
13 : #include <sandesh/sandesh.h>
14 : #include <sandesh/sandesh_types.h>
15 : #include <sandesh/sandesh_trace.h>
16 : #include <sandesh/common/vns_constants.h>
17 :
18 : using namespace std;
19 :
20 : SandeshTraceBufferPtr AgentDBwalkTraceBuf(SandeshTraceBufferCreate(
21 : AGENT_DBWALK_TRACE_BUF, 1000));
22 :
23 16 : RouteWalkerDBState::RouteWalkerDBState() : walker_ref_map_() {
24 16 : }
25 :
26 : static void
27 37 : RemoveWalkReferencesInRoutetable(RouteWalkerDBState::AgentRouteWalkerRefMapIter &it,
28 : RouteWalkerDBState *state) {
29 37 : for (uint8_t table_type = Agent::ROUTE_TABLE_START;
30 259 : table_type < Agent::ROUTE_TABLE_MAX; table_type++) {
31 222 : it->second[table_type] = DBTable::DBTableWalkRef();
32 : }
33 37 : }
34 :
35 3 : AgentRouteWalkerManager::AgentRouteWalkerManager(Agent *agent) : agent_(agent),
36 3 : walk_ref_list_(), marked_for_deletion_(false) {
37 3 : vrf_listener_id_ = agent->vrf_table()->Register(
38 : boost::bind(&AgentRouteWalkerManager::VrfNotify, this,
39 : _1, _2));
40 3 : }
41 :
42 6 : AgentRouteWalkerManager::~AgentRouteWalkerManager() {
43 : //Since walker ref list is going off, mark all walkers to have NULL
44 : //managers.
45 : //TODO
46 6 : }
47 :
48 59 : void AgentRouteWalkerManager::ReleaseWalker(AgentRouteWalker *walker) {
49 59 : assert(std::find(walk_ref_list_.begin(), walk_ref_list_.end(), walker)
50 : != walk_ref_list_.end());
51 59 : walker->ReleaseVrfWalkReference();
52 59 : }
53 :
54 : void
55 0 : AgentRouteWalkerManager::RemoveWalkReferencesInVrf(RouteWalkerDBState *state,
56 : VrfEntry *vrf) {
57 0 : for (RouteWalkerDBState::AgentRouteWalkerRefMapIter it =
58 0 : state->walker_ref_map_.begin();
59 0 : it != state->walker_ref_map_.end(); it++) {
60 0 : RemoveWalkReferencesInRoutetable(it, state);
61 : }
62 0 : }
63 :
64 : //Handles VRF addition and deletion.
65 : //Releases all vrf walker reference and route table walker references.
66 106 : void AgentRouteWalkerManager::VrfNotify(DBTablePartBase *partition,
67 : DBEntryBase *e) {
68 106 : VrfEntry *vrf = static_cast<VrfEntry *>(e);
69 : RouteWalkerDBState *state =
70 106 : static_cast<RouteWalkerDBState *>(vrf->GetState(partition->parent(),
71 : vrf_listener_id_));
72 106 : if (vrf->IsDeleted()) {
73 88 : if (!state)
74 88 : return;
75 0 : RemoveWalkReferencesInVrf(state, vrf);
76 0 : vrf->ClearState(partition->parent(), vrf_listener_id_);
77 0 : delete state;
78 0 : return;
79 : }
80 :
81 18 : if (!state) {
82 12 : CreateState(vrf);
83 : }
84 : }
85 :
86 16 : RouteWalkerDBState *AgentRouteWalkerManager::CreateState(VrfEntry *vrf) {
87 16 : RouteWalkerDBState *state = new RouteWalkerDBState();
88 16 : vrf->SetState(vrf->get_table(), vrf_listener_id_, state);
89 16 : return state;
90 : }
91 :
92 59 : void AgentRouteWalkerManager::RemoveWalker(AgentRouteWalkerPtr walker) {
93 59 : walk_ref_list_.erase(std::find(walk_ref_list_.begin(), walk_ref_list_.end(),
94 : walker));
95 59 : }
96 :
97 3 : void AgentRouteWalkerManager::Shutdown() {
98 3 : marked_for_deletion_ = true;
99 3 : TryUnregister();
100 3 : }
101 :
102 62 : void AgentRouteWalkerManager::TryUnregister() {
103 62 : if (walk_ref_list_.size() != 0)
104 59 : return;
105 3 : agent_->vrf_table()->Unregister(vrf_listener_id_);
106 : }
107 :
108 58 : void AgentRouteWalkerManager::ValidateAgentRouteWalker
109 : (AgentRouteWalkerPtr walker) const {
110 58 : assert (std::find(walk_ref_list_.begin(), walk_ref_list_.end(), walker) !=
111 : walk_ref_list_.end());
112 58 : }
113 :
114 59 : void AgentRouteWalkerManager::RegisterWalker(AgentRouteWalker *walker) {
115 59 : if (marked_for_deletion_)
116 0 : return;
117 59 : walk_ref_list_.insert(walker);
118 59 : walker->set_mgr(this);
119 : }
120 :
121 59 : AgentRouteWalker::AgentRouteWalker(const std::string &name,
122 59 : Agent *agent) : agent_(agent), name_(name),
123 59 : route_walk_count_(), walk_done_cb_(), route_walk_done_for_vrf_cb_(),
124 118 : mgr_(NULL), deregister_done_(false) {
125 59 : walk_count_ = AgentRouteWalker::kInvalidWalkCount;
126 118 : vrf_walk_ref_ = agent_->vrf_table()->AllocWalker(
127 : boost::bind(&AgentRouteWalker::VrfWalkNotify,
128 : this, _1, _2),
129 : boost::bind(&AgentRouteWalker::VrfWalkDoneInternal,
130 59 : this, _2));
131 118 : delete_walk_ref_ = agent_->vrf_table()->AllocWalker(
132 : boost::bind(&AgentRouteWalker::Deregister, this,
133 : _1, _2),
134 : boost::bind(&AgentRouteWalker::DeregisterDone,
135 59 : this));
136 59 : refcount_ = 0;
137 59 : }
138 :
139 59 : AgentRouteWalker::~AgentRouteWalker() {
140 59 : assert(vrf_walk_ref_.get() == NULL);
141 59 : assert(delete_walk_ref_.get() == NULL);
142 59 : }
143 :
144 : RouteWalkerDBState *
145 308 : AgentRouteWalker::LocateRouteWalkerDBState(VrfEntry *vrf) {
146 : RouteWalkerDBState *state =
147 308 : static_cast<RouteWalkerDBState *>(vrf->GetState(vrf->get_table(),
148 308 : mgr_->vrf_listener_id()));
149 308 : if (vrf->IsDeleted())
150 0 : return state;
151 :
152 308 : if (!state)
153 4 : state = mgr_->CreateState(vrf);
154 :
155 308 : return state;
156 : }
157 :
158 : DBTable::DBTableWalkRef
159 264 : AgentRouteWalker::LocateRouteTableWalkRef(const VrfEntry *vrf,
160 : RouteWalkerDBState *state,
161 : AgentRouteTable *table) {
162 264 : assert(state != NULL);
163 264 : Agent::RouteTableType table_type = table->GetTableType();
164 264 : AgentRouteWalkerPtr walker_ptr = AgentRouteWalkerPtr(this);
165 :
166 : // Search for walker and if found then check if table type has a reference
167 : // allocated or not. If allocated return the same.
168 : RouteWalkerDBState::AgentRouteWalkerRefMapIter it =
169 264 : state->walker_ref_map_.find(walker_ptr);
170 264 : DBTable::DBTableWalkRef route_table_walk_ref = DBTable::DBTableWalkRef();
171 :
172 : // Walker was not found in map, add it
173 264 : if (it == state->walker_ref_map_.end()) {
174 37 : RouteWalkerDBState::RouteWalkRef route_walk_ref;
175 37 : std::pair<RouteWalkerDBState::AgentRouteWalkerRefMapIter, bool> ret;
176 37 : ret = state->walker_ref_map_.insert(std::make_pair(walker_ptr, route_walk_ref));
177 37 : if (ret.second == false) {
178 0 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_,
179 : "LocateRouteTableWalkRef walker add failed.",
180 : "", "");
181 0 : return DBTable::DBTableWalkRef();
182 : }
183 37 : it = ret.first;
184 37 : }
185 :
186 : // table type had no reference allocated, so allocate it.
187 264 : if (it->second[table_type] == DBTable::DBTableWalkRef()) {
188 444 : it->second[table_type] = table->AllocWalker(
189 : boost::bind(&AgentRouteWalker::RouteWalkNotify,
190 : this, _1, _2),
191 444 : boost::bind(&AgentRouteWalker::RouteWalkDoneInternal,
192 222 : this, _2, walker_ptr));
193 : }
194 264 : return it->second[table_type];
195 264 : }
196 :
197 : /*
198 : * Starts walk for all VRF.
199 : */
200 14 : void AgentRouteWalker::StartVrfWalk() {
201 14 : mgr_->ValidateAgentRouteWalker(this);
202 14 : if (vrf_walk_ref_->in_progress() == false)
203 14 : IncrementWalkCount();
204 14 : agent_->vrf_table()->WalkAgain(vrf_walk_ref_);
205 : //TODO trace
206 14 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_, "StartVrfWalk",
207 : "", "");
208 14 : }
209 :
210 : DBTable::DBTableWalkRef
211 264 : AgentRouteWalker::AllocateRouteTableReferences(AgentRouteTable *table) {
212 264 : VrfEntry *vrf = table->vrf_entry();
213 264 : RouteWalkerDBState *state = LocateRouteWalkerDBState(vrf);
214 264 : return LocateRouteTableWalkRef(vrf, state, table);
215 : }
216 :
217 264 : void AgentRouteWalker::WalkTable(AgentRouteTable *table,
218 : DBTable::DBTableWalkRef &route_table_walk_ref) {
219 264 : if (route_table_walk_ref->in_progress() == false) {
220 264 : IncrementWalkCount();
221 264 : IncrementRouteWalkCount(table->vrf_entry());
222 : }
223 264 : table->WalkAgain(route_table_walk_ref);
224 264 : }
225 :
226 : /*
227 : * Starts route walk for given VRF.
228 : */
229 44 : void AgentRouteWalker::StartRouteWalk(VrfEntry *vrf) {
230 44 : mgr_->ValidateAgentRouteWalker(this);
231 44 : AgentRouteTable *table = NULL;
232 :
233 : //Start the walk for every route table
234 308 : for (uint8_t table_type = (Agent::INVALID + 1);
235 308 : table_type < Agent::ROUTE_TABLE_MAX;
236 : table_type++) {
237 : table = static_cast<AgentRouteTable *>
238 264 : (vrf->GetRouteTable(table_type));
239 264 : if (!table) continue;
240 : DBTable::DBTableWalkRef route_table_walk_ref =
241 264 : AllocateRouteTableReferences(table);
242 264 : WalkTable(table, route_table_walk_ref);
243 264 : }
244 44 : }
245 :
246 : /*
247 : * VRF entry notification handler
248 : */
249 18 : bool AgentRouteWalker::VrfWalkNotify(DBTablePartBase *partition,
250 : DBEntryBase *e) {
251 18 : VrfEntry *vrf = static_cast<VrfEntry *>(e);
252 :
253 : // TODO - VRF deletion need not be ignored, as it may be a way to
254 : // unsubscribe. Needs to be fixed with controller walks.
255 : //
256 : // In case of controller-peer going down. We want to send 'unsubscribe' of
257 : // VRF to BGP. Assume sequence where XMPP goes down and VRF is deleted. If
258 : // XMPP walk starts before VRF deletion, we unsubscribe to the VRF
259 : // notification at end of walk. If we skip deleted VRF in walk, we will not
260 : // send 'unsubscribe' to BGP
261 : //
262 18 : if (vrf->IsDeleted()) {
263 0 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_,
264 : "VrfWalkNotify: Vrf deleted, no route walk.",
265 : (vrf != NULL) ? vrf->GetName() : "Unknown",
266 : "NA");
267 0 : return true;
268 : }
269 :
270 18 : StartRouteWalk(vrf);
271 18 : return true;
272 : }
273 :
274 14 : void AgentRouteWalker::VrfWalkDoneInternal(DBTableBase *part) {
275 14 : VrfWalkDone(part);
276 14 : DecrementWalkCount();
277 14 : Callback(NULL);
278 14 : }
279 :
280 14 : void AgentRouteWalker::VrfWalkDone(DBTableBase *part) {
281 14 : }
282 :
283 : /*
284 : * Route entry notification handler
285 : */
286 0 : bool AgentRouteWalker::RouteWalkNotify(DBTablePartBase *partition,
287 : DBEntryBase *e) {
288 0 : return true;
289 : }
290 :
291 264 : void AgentRouteWalker::RouteWalkDone(DBTableBase *part) {
292 264 : }
293 :
294 264 : void AgentRouteWalker::RouteWalkDoneInternal(DBTableBase *part,
295 : AgentRouteWalkerPtr ptr) {
296 264 : RouteWalkDone(part);
297 :
298 264 : AgentRouteTable *table = static_cast<AgentRouteTable *>(part);
299 264 : DecrementWalkCount();
300 264 : DecrementRouteWalkCount(table->vrf_entry());
301 264 : uint32_t vrf_id = table->vrf_id();
302 :
303 264 : VrfEntry *vrf = agent_->vrf_table()->
304 264 : FindVrfFromIdIncludingDeletedVrf(vrf_id);
305 : // If there is no vrf entry for table, that signifies that
306 : // routes have gone and table is empty. Since routes have gone
307 : // state from vncontroller on routes have been removed and so would
308 : // have happened on vrf entry as well.
309 264 : Callback(vrf);
310 264 : }
311 :
312 278 : void AgentRouteWalker::DecrementWalkCount() {
313 278 : assert(walk_count_ != AgentRouteWalker::kInvalidWalkCount);
314 278 : walk_count_--;
315 278 : }
316 :
317 264 : void AgentRouteWalker::DecrementRouteWalkCount(const VrfEntry *vrf) {
318 264 : VrfRouteWalkCountMap::iterator it = route_walk_count_.find(vrf);
319 264 : if (it != route_walk_count_.end()) {
320 264 : it->second--;
321 264 : if (it->second == AgentRouteWalker::kInvalidWalkCount)
322 44 : route_walk_count_.erase(vrf);
323 : }
324 264 : }
325 :
326 264 : void AgentRouteWalker::IncrementRouteWalkCount(const VrfEntry *vrf) {
327 264 : route_walk_count_[vrf]++;
328 264 : }
329 :
330 278 : void AgentRouteWalker::Callback(VrfEntry *vrf) {
331 278 : if (vrf && AreAllRouteWalksDone(vrf)) {
332 : //Deletes the state on VRF
333 44 : OnRouteTableWalkCompleteForVrf(vrf);
334 : }
335 278 : if (AreAllWalksDone()) {
336 : //To be executed in callback where surity is there
337 : //that all walks are done.
338 38 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_,
339 : "All Walks are done",
340 : (vrf != NULL) ? vrf->GetName() : "Unknown",
341 : "NA");
342 38 : if (walk_done_cb_.empty() == false) {
343 7 : walk_done_cb_();
344 : }
345 : }
346 278 : }
347 :
348 0 : bool AgentRouteWalker::IsRouteTableWalkCompleted(RouteWalkerDBState *state) {
349 : RouteWalkerDBState::AgentRouteWalkerRefMapConstIter it =
350 0 : state->walker_ref_map_.find(AgentRouteWalkerPtr(this));
351 0 : if (it == state->walker_ref_map_.end())
352 0 : return true;
353 :
354 0 : for (uint8_t table_type = Agent::ROUTE_TABLE_START;
355 0 : table_type < Agent::ROUTE_TABLE_MAX; table_type++) {
356 0 : if (it->second[table_type] != DBTable::DBTableWalkRef())
357 0 : return false;
358 : }
359 0 : return true;
360 : }
361 :
362 : /*
363 : * Check if all route table walk have been reset for this VRF
364 : */
365 44 : void AgentRouteWalker::OnRouteTableWalkCompleteForVrf(VrfEntry *vrf) {
366 44 : RouteWalkerDBState *state = LocateRouteWalkerDBState(vrf);
367 44 : if (!state) {
368 0 : return;
369 : }
370 :
371 44 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_,
372 : "All route walks are done",
373 : (vrf != NULL) ? vrf->GetName() : "Unknown",
374 : "NA");
375 :
376 44 : if (route_walk_done_for_vrf_cb_.empty())
377 42 : return;
378 :
379 2 : route_walk_done_for_vrf_cb_(vrf);
380 : }
381 :
382 278 : bool AgentRouteWalker::AreAllWalksDone() const {
383 278 : return (walk_count_ == AgentRouteWalker::kInvalidWalkCount);
384 : }
385 :
386 264 : bool AgentRouteWalker::AreAllRouteWalksDone(const VrfEntry *vrf) const {
387 264 : VrfRouteWalkCountMap::const_iterator it = route_walk_count_.find(vrf);
388 264 : return (it == route_walk_count_.end());
389 : }
390 :
391 : /* Callback set, his is called when all walks are done i.e. VRF + route */
392 8 : void AgentRouteWalker::WalkDoneCallback(WalkDone cb) {
393 8 : walk_done_cb_ = cb;
394 8 : }
395 :
396 : /*
397 : * Callback is registered to notify walk complete of all route tables for
398 : * a VRF.
399 : */
400 2 : void AgentRouteWalker::RouteWalkDoneForVrfCallback(RouteWalkDoneCb cb) {
401 2 : route_walk_done_for_vrf_cb_ = cb;
402 2 : }
403 :
404 59 : void AgentRouteWalker::ReleaseVrfWalkReference() {
405 59 : agent_->vrf_table()->WalkAgain(delete_walk_ref_);
406 59 : if (vrf_walk_ref_ != NULL) {
407 59 : agent_->vrf_table()->ReleaseWalker(vrf_walk_ref());
408 : }
409 59 : vrf_walk_ref_.reset();
410 59 : }
411 :
412 88 : bool AgentRouteWalker::Deregister(DBTablePartBase *partition,
413 : DBEntryBase *e) {
414 88 : VrfEntry *vrf = static_cast<VrfEntry *>(e);
415 88 : DBTable::ListenerId vrf_listener_id = mgr_->vrf_listener_id();
416 : RouteWalkerDBState *state =
417 88 : static_cast<RouteWalkerDBState *>(vrf->GetState(vrf->get_table(),
418 : vrf_listener_id));
419 88 : AgentRouteWalkerPtr walker_ptr = AgentRouteWalkerPtr(this);
420 :
421 88 : if (state == NULL)
422 72 : return true;
423 :
424 16 : if (vrf_walk_ref_.get() != NULL) {
425 : RouteWalkerDBState::AgentRouteWalkerRefMapIter it =
426 0 : state->walker_ref_map_.find(AgentRouteWalkerPtr(this));
427 0 : if (it != state->walker_ref_map_.end())
428 0 : RemoveWalkReferencesInRoutetable(it, state);
429 0 : return true;
430 : }
431 :
432 : //If walk reference is provided as NULL, its delete of walk manager and
433 : //agent shutdown. So delete all walk_refs.
434 16 : for (RouteWalkerDBState::AgentRouteWalkerRefMapIter it2 =
435 16 : state->walker_ref_map_.begin();
436 53 : it2 != state->walker_ref_map_.end(); it2++) {
437 37 : RemoveWalkReferencesInRoutetable(it2, state);
438 : }
439 16 : vrf->ClearState(partition->parent(), vrf_listener_id);
440 16 : delete state;
441 16 : return true;
442 88 : }
443 :
444 : void
445 59 : AgentRouteWalker::DeregisterDone(AgentRouteWalkerPtr walker) {
446 59 : const Agent *agent = walker.get()->agent();
447 59 : if (walker.get()->vrf_walk_ref().get() != NULL) {
448 0 : walker.get()->agent()->vrf_table()->ReleaseWalker(walker.get()->
449 : vrf_walk_ref());
450 : }
451 59 : if (walker.get()->delete_walk_ref().get() != NULL) {
452 59 : walker.get()->agent()->vrf_table()->ReleaseWalker(walker.get()->
453 : delete_walk_ref());
454 : }
455 :
456 59 : if (agent->oper_db()->agent_route_walk_manager()) {
457 59 : walker.get()->mgr()->RemoveWalker(walker);
458 59 : walker.get()->mgr()->TryUnregister();
459 : }
460 59 : walker.get()->deregister_done_ = true;
461 59 : }
462 :
463 4583 : void intrusive_ptr_add_ref(AgentRouteWalker *w) {
464 4583 : w->refcount_++;
465 4583 : }
466 :
467 4583 : void intrusive_ptr_release(AgentRouteWalker *w) {
468 9166 : if (w->refcount_.fetch_sub(1) == 1) {
469 59 : delete w;
470 : }
471 4583 : }
|