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 13 : RouteWalkerDBState::RouteWalkerDBState() : walker_ref_map_() {
24 13 : }
25 :
26 : static void
27 28 : RemoveWalkReferencesInRoutetable(RouteWalkerDBState::AgentRouteWalkerRefMapIter &it,
28 : RouteWalkerDBState *state) {
29 28 : for (uint8_t table_type = Agent::ROUTE_TABLE_START;
30 196 : table_type < Agent::ROUTE_TABLE_MAX; table_type++) {
31 168 : it->second[table_type] = DBTable::DBTableWalkRef();
32 : }
33 28 : }
34 :
35 1 : AgentRouteWalkerManager::AgentRouteWalkerManager(Agent *agent) : agent_(agent),
36 1 : walk_ref_list_(), marked_for_deletion_(false) {
37 1 : vrf_listener_id_ = agent->vrf_table()->Register(
38 : boost::bind(&AgentRouteWalkerManager::VrfNotify, this,
39 : _1, _2));
40 1 : }
41 :
42 2 : AgentRouteWalkerManager::~AgentRouteWalkerManager() {
43 : //Since walker ref list is going off, mark all walkers to have NULL
44 : //managers.
45 : //TODO
46 2 : }
47 :
48 38 : void AgentRouteWalkerManager::ReleaseWalker(AgentRouteWalker *walker) {
49 38 : assert(std::find(walk_ref_list_.begin(), walk_ref_list_.end(), walker)
50 : != walk_ref_list_.end());
51 38 : walker->ReleaseVrfWalkReference();
52 38 : }
53 :
54 : void
55 1 : AgentRouteWalkerManager::RemoveWalkReferencesInVrf(RouteWalkerDBState *state,
56 : VrfEntry *vrf) {
57 1 : for (RouteWalkerDBState::AgentRouteWalkerRefMapIter it =
58 1 : state->walker_ref_map_.begin();
59 4 : it != state->walker_ref_map_.end(); it++) {
60 3 : RemoveWalkReferencesInRoutetable(it, state);
61 : }
62 1 : }
63 :
64 : //Handles VRF addition and deletion.
65 : //Releases all vrf walker reference and route table walker references.
66 83 : void AgentRouteWalkerManager::VrfNotify(DBTablePartBase *partition,
67 : DBEntryBase *e) {
68 83 : VrfEntry *vrf = static_cast<VrfEntry *>(e);
69 : RouteWalkerDBState *state =
70 83 : static_cast<RouteWalkerDBState *>(vrf->GetState(partition->parent(),
71 : vrf_listener_id_));
72 83 : if (vrf->IsDeleted()) {
73 68 : if (!state)
74 67 : return;
75 1 : RemoveWalkReferencesInVrf(state, vrf);
76 1 : vrf->ClearState(partition->parent(), vrf_listener_id_);
77 1 : delete state;
78 1 : return;
79 : }
80 :
81 15 : if (!state) {
82 9 : CreateState(vrf);
83 : }
84 : }
85 :
86 13 : RouteWalkerDBState *AgentRouteWalkerManager::CreateState(VrfEntry *vrf) {
87 13 : RouteWalkerDBState *state = new RouteWalkerDBState();
88 13 : vrf->SetState(vrf->get_table(), vrf_listener_id_, state);
89 13 : return state;
90 : }
91 :
92 38 : void AgentRouteWalkerManager::RemoveWalker(AgentRouteWalkerPtr walker) {
93 38 : walk_ref_list_.erase(std::find(walk_ref_list_.begin(), walk_ref_list_.end(),
94 : walker));
95 38 : }
96 :
97 1 : void AgentRouteWalkerManager::Shutdown() {
98 1 : marked_for_deletion_ = true;
99 1 : TryUnregister();
100 1 : }
101 :
102 39 : void AgentRouteWalkerManager::TryUnregister() {
103 39 : if (walk_ref_list_.size() != 0)
104 38 : return;
105 1 : agent_->vrf_table()->Unregister(vrf_listener_id_);
106 : }
107 :
108 36 : void AgentRouteWalkerManager::ValidateAgentRouteWalker
109 : (AgentRouteWalkerPtr walker) const {
110 36 : assert (std::find(walk_ref_list_.begin(), walk_ref_list_.end(), walker) !=
111 : walk_ref_list_.end());
112 36 : }
113 :
114 38 : void AgentRouteWalkerManager::RegisterWalker(AgentRouteWalker *walker) {
115 38 : if (marked_for_deletion_)
116 0 : return;
117 38 : walk_ref_list_.insert(walker);
118 38 : walker->set_mgr(this);
119 : }
120 :
121 38 : AgentRouteWalker::AgentRouteWalker(const std::string &name,
122 38 : Agent *agent) : agent_(agent), name_(name),
123 38 : route_walk_count_(), walk_done_cb_(), route_walk_done_for_vrf_cb_(),
124 76 : mgr_(NULL), deregister_done_(false) {
125 38 : walk_count_ = AgentRouteWalker::kInvalidWalkCount;
126 76 : vrf_walk_ref_ = agent_->vrf_table()->AllocWalker(
127 : boost::bind(&AgentRouteWalker::VrfWalkNotify,
128 : this, _1, _2),
129 : boost::bind(&AgentRouteWalker::VrfWalkDoneInternal,
130 38 : this, _2));
131 76 : delete_walk_ref_ = agent_->vrf_table()->AllocWalker(
132 : boost::bind(&AgentRouteWalker::Deregister, this,
133 : _1, _2),
134 : boost::bind(&AgentRouteWalker::DeregisterDone,
135 38 : this));
136 38 : refcount_ = 0;
137 38 : }
138 :
139 38 : AgentRouteWalker::~AgentRouteWalker() {
140 38 : assert(vrf_walk_ref_.get() == NULL);
141 38 : assert(delete_walk_ref_.get() == NULL);
142 38 : }
143 :
144 : RouteWalkerDBState *
145 210 : AgentRouteWalker::LocateRouteWalkerDBState(VrfEntry *vrf) {
146 : RouteWalkerDBState *state =
147 210 : static_cast<RouteWalkerDBState *>(vrf->GetState(vrf->get_table(),
148 210 : mgr_->vrf_listener_id()));
149 210 : if (vrf->IsDeleted())
150 0 : return state;
151 :
152 210 : if (!state)
153 4 : state = mgr_->CreateState(vrf);
154 :
155 210 : return state;
156 : }
157 :
158 : DBTable::DBTableWalkRef
159 180 : AgentRouteWalker::LocateRouteTableWalkRef(const VrfEntry *vrf,
160 : RouteWalkerDBState *state,
161 : AgentRouteTable *table) {
162 180 : assert(state != NULL);
163 180 : Agent::RouteTableType table_type = table->GetTableType();
164 180 : 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 180 : state->walker_ref_map_.find(walker_ptr);
170 180 : DBTable::DBTableWalkRef route_table_walk_ref = DBTable::DBTableWalkRef();
171 :
172 : // Walker was not found in map, add it
173 180 : if (it == state->walker_ref_map_.end()) {
174 28 : RouteWalkerDBState::RouteWalkRef route_walk_ref;
175 28 : std::pair<RouteWalkerDBState::AgentRouteWalkerRefMapIter, bool> ret;
176 28 : ret = state->walker_ref_map_.insert(std::make_pair(walker_ptr, route_walk_ref));
177 28 : if (ret.second == false) {
178 0 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_,
179 : "LocateRouteTableWalkRef walker add failed.",
180 : "", "");
181 0 : return DBTable::DBTableWalkRef();
182 : }
183 28 : it = ret.first;
184 28 : }
185 :
186 : // table type had no reference allocated, so allocate it.
187 180 : if (it->second[table_type] == DBTable::DBTableWalkRef()) {
188 336 : it->second[table_type] = table->AllocWalker(
189 : boost::bind(&AgentRouteWalker::RouteWalkNotify,
190 : this, _1, _2),
191 336 : boost::bind(&AgentRouteWalker::RouteWalkDoneInternal,
192 168 : this, _2, walker_ptr));
193 : }
194 180 : return it->second[table_type];
195 180 : }
196 :
197 : /*
198 : * Starts walk for all VRF.
199 : */
200 6 : void AgentRouteWalker::StartVrfWalk() {
201 6 : mgr_->ValidateAgentRouteWalker(this);
202 6 : if (vrf_walk_ref_->in_progress() == false)
203 6 : IncrementWalkCount();
204 6 : agent_->vrf_table()->WalkAgain(vrf_walk_ref_);
205 : //TODO trace
206 6 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_, "StartVrfWalk",
207 : "", "");
208 6 : }
209 :
210 : DBTable::DBTableWalkRef
211 180 : AgentRouteWalker::AllocateRouteTableReferences(AgentRouteTable *table) {
212 180 : VrfEntry *vrf = table->vrf_entry();
213 180 : RouteWalkerDBState *state = LocateRouteWalkerDBState(vrf);
214 180 : return LocateRouteTableWalkRef(vrf, state, table);
215 : }
216 :
217 180 : void AgentRouteWalker::WalkTable(AgentRouteTable *table,
218 : DBTable::DBTableWalkRef &route_table_walk_ref) {
219 180 : if (route_table_walk_ref->in_progress() == false) {
220 180 : IncrementWalkCount();
221 180 : IncrementRouteWalkCount(table->vrf_entry());
222 : }
223 180 : table->WalkAgain(route_table_walk_ref);
224 180 : }
225 :
226 : /*
227 : * Starts route walk for given VRF.
228 : */
229 30 : void AgentRouteWalker::StartRouteWalk(VrfEntry *vrf) {
230 30 : mgr_->ValidateAgentRouteWalker(this);
231 30 : AgentRouteTable *table = NULL;
232 :
233 : //Start the walk for every route table
234 210 : for (uint8_t table_type = (Agent::INVALID + 1);
235 210 : table_type < Agent::ROUTE_TABLE_MAX;
236 : table_type++) {
237 : table = static_cast<AgentRouteTable *>
238 180 : (vrf->GetRouteTable(table_type));
239 180 : if (!table) continue;
240 : DBTable::DBTableWalkRef route_table_walk_ref =
241 180 : AllocateRouteTableReferences(table);
242 180 : WalkTable(table, route_table_walk_ref);
243 180 : }
244 30 : }
245 :
246 : /*
247 : * VRF entry notification handler
248 : */
249 6 : bool AgentRouteWalker::VrfWalkNotify(DBTablePartBase *partition,
250 : DBEntryBase *e) {
251 6 : 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 6 : 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 6 : StartRouteWalk(vrf);
271 6 : return true;
272 : }
273 :
274 6 : void AgentRouteWalker::VrfWalkDoneInternal(DBTableBase *part) {
275 6 : VrfWalkDone(part);
276 6 : DecrementWalkCount();
277 6 : Callback(NULL);
278 6 : }
279 :
280 6 : void AgentRouteWalker::VrfWalkDone(DBTableBase *part) {
281 6 : }
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 180 : void AgentRouteWalker::RouteWalkDone(DBTableBase *part) {
292 180 : }
293 :
294 180 : void AgentRouteWalker::RouteWalkDoneInternal(DBTableBase *part,
295 : AgentRouteWalkerPtr ptr) {
296 180 : RouteWalkDone(part);
297 :
298 180 : AgentRouteTable *table = static_cast<AgentRouteTable *>(part);
299 180 : DecrementWalkCount();
300 180 : DecrementRouteWalkCount(table->vrf_entry());
301 180 : uint32_t vrf_id = table->vrf_id();
302 :
303 180 : VrfEntry *vrf = agent_->vrf_table()->
304 180 : 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 180 : Callback(vrf);
310 180 : }
311 :
312 186 : void AgentRouteWalker::DecrementWalkCount() {
313 186 : assert(walk_count_ != AgentRouteWalker::kInvalidWalkCount);
314 186 : walk_count_--;
315 186 : }
316 :
317 180 : void AgentRouteWalker::DecrementRouteWalkCount(const VrfEntry *vrf) {
318 180 : VrfRouteWalkCountMap::iterator it = route_walk_count_.find(vrf);
319 180 : if (it != route_walk_count_.end()) {
320 180 : it->second--;
321 180 : if (it->second == AgentRouteWalker::kInvalidWalkCount)
322 30 : route_walk_count_.erase(vrf);
323 : }
324 180 : }
325 :
326 180 : void AgentRouteWalker::IncrementRouteWalkCount(const VrfEntry *vrf) {
327 180 : route_walk_count_[vrf]++;
328 180 : }
329 :
330 186 : void AgentRouteWalker::Callback(VrfEntry *vrf) {
331 186 : if (vrf && AreAllRouteWalksDone(vrf)) {
332 : //Deletes the state on VRF
333 30 : OnRouteTableWalkCompleteForVrf(vrf);
334 : }
335 186 : if (AreAllWalksDone()) {
336 : //To be executed in callback where surity is there
337 : //that all walks are done.
338 28 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_,
339 : "All Walks are done",
340 : (vrf != NULL) ? vrf->GetName() : "Unknown",
341 : "NA");
342 28 : if (walk_done_cb_.empty() == false) {
343 3 : walk_done_cb_();
344 : }
345 : }
346 186 : }
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 30 : void AgentRouteWalker::OnRouteTableWalkCompleteForVrf(VrfEntry *vrf) {
366 30 : RouteWalkerDBState *state = LocateRouteWalkerDBState(vrf);
367 30 : if (!state) {
368 0 : return;
369 : }
370 :
371 30 : AGENT_DBWALK_TRACE(AgentRouteWalkerTrace, name_,
372 : "All route walks are done",
373 : (vrf != NULL) ? vrf->GetName() : "Unknown",
374 : "NA");
375 :
376 30 : if (route_walk_done_for_vrf_cb_.empty())
377 28 : return;
378 :
379 2 : route_walk_done_for_vrf_cb_(vrf);
380 : }
381 :
382 186 : bool AgentRouteWalker::AreAllWalksDone() const {
383 186 : return (walk_count_ == AgentRouteWalker::kInvalidWalkCount);
384 : }
385 :
386 180 : bool AgentRouteWalker::AreAllRouteWalksDone(const VrfEntry *vrf) const {
387 180 : VrfRouteWalkCountMap::const_iterator it = route_walk_count_.find(vrf);
388 180 : return (it == route_walk_count_.end());
389 : }
390 :
391 : /* Callback set, his is called when all walks are done i.e. VRF + route */
392 4 : void AgentRouteWalker::WalkDoneCallback(WalkDone cb) {
393 4 : walk_done_cb_ = cb;
394 4 : }
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 38 : void AgentRouteWalker::ReleaseVrfWalkReference() {
405 38 : agent_->vrf_table()->WalkAgain(delete_walk_ref_);
406 38 : if (vrf_walk_ref_ != NULL) {
407 38 : agent_->vrf_table()->ReleaseWalker(vrf_walk_ref());
408 : }
409 38 : vrf_walk_ref_.reset();
410 38 : }
411 :
412 80 : bool AgentRouteWalker::Deregister(DBTablePartBase *partition,
413 : DBEntryBase *e) {
414 80 : VrfEntry *vrf = static_cast<VrfEntry *>(e);
415 80 : DBTable::ListenerId vrf_listener_id = mgr_->vrf_listener_id();
416 : RouteWalkerDBState *state =
417 80 : static_cast<RouteWalkerDBState *>(vrf->GetState(vrf->get_table(),
418 : vrf_listener_id));
419 80 : AgentRouteWalkerPtr walker_ptr = AgentRouteWalkerPtr(this);
420 :
421 80 : if (state == NULL)
422 68 : return true;
423 :
424 12 : 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 12 : for (RouteWalkerDBState::AgentRouteWalkerRefMapIter it2 =
435 12 : state->walker_ref_map_.begin();
436 37 : it2 != state->walker_ref_map_.end(); it2++) {
437 25 : RemoveWalkReferencesInRoutetable(it2, state);
438 : }
439 12 : vrf->ClearState(partition->parent(), vrf_listener_id);
440 12 : delete state;
441 12 : return true;
442 80 : }
443 :
444 : void
445 38 : AgentRouteWalker::DeregisterDone(AgentRouteWalkerPtr walker) {
446 38 : const Agent *agent = walker.get()->agent();
447 38 : if (walker.get()->vrf_walk_ref().get() != NULL) {
448 0 : walker.get()->agent()->vrf_table()->ReleaseWalker(walker.get()->
449 : vrf_walk_ref());
450 : }
451 38 : if (walker.get()->delete_walk_ref().get() != NULL) {
452 38 : walker.get()->agent()->vrf_table()->ReleaseWalker(walker.get()->
453 : delete_walk_ref());
454 : }
455 :
456 38 : if (agent->oper_db()->agent_route_walk_manager()) {
457 38 : walker.get()->mgr()->RemoveWalker(walker);
458 38 : walker.get()->mgr()->TryUnregister();
459 : }
460 38 : walker.get()->deregister_done_ = true;
461 38 : }
462 :
463 3368 : void intrusive_ptr_add_ref(AgentRouteWalker *w) {
464 3368 : w->refcount_++;
465 3368 : }
466 :
467 3368 : void intrusive_ptr_release(AgentRouteWalker *w) {
468 6736 : if (w->refcount_.fetch_sub(1) == 1) {
469 38 : delete w;
470 : }
471 3368 : }
|