MPLS infrastructure improvments
[vpp.git] / vnet / vnet / fib / fib_walk.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/fib/fib_walk.h>
17 #include <vnet/fib/fib_node_list.h>
18
19 /**
20  * The flags on a walk
21  */
22 typedef enum fib_walk_flags_t_
23 {
24     /**
25      * A synchronous walk.
26      * This walk will run to completion, i.e. visit ALL the children.
27      * It is a depth first traversal of the graph.
28      */
29     FIB_WALK_FLAG_SYNC = (1 << 0),
30     /**
31      * An asynchronous walk.
32      * This walk will be scheduled to run in the background. It will thus visits
33      * the children at a later point in time.
34      * It is a depth first traversal of the graph.
35      */
36     FIB_WALK_FLAG_ASYNC = (1 << 1),
37     /**
38      * An indication that the walk is currently executing.
39      */
40     FIB_WALK_FLAG_EXECUTING = (1 << 2),
41 } fib_walk_flags_t;
42
43 /**
44  * A representation of a graph walk from a parent object to its children
45  */
46 typedef struct fib_walk_t_
47 {
48     /**
49      * FIB node linkage. This object is not in the FIB object graph,
50      * but it is present in other node's dependency lists, so it needs to
51      * be pointerable to.
52      */
53     fib_node_t fw_node;
54
55     /**
56      * the walk's flags
57      */
58     fib_walk_flags_t fw_flags;
59
60     /**
61      * Sibling index in the dependency list
62      */
63     u32 fw_dep_sibling;
64
65     /**
66      * Sibling index in the list of all walks
67      */
68     u32 fw_prio_sibling;
69
70     /**
71      * Pointer to the node whose dependants this walk is walking
72      */
73     fib_node_ptr_t fw_parent;
74
75     /**
76      * Number of nodes visited by this walk. saved for debugging purposes.
77      */
78     u32 fw_n_visits;
79
80     /**
81      * Time the walk started
82      */
83     f64 fw_start_time;
84
85     /**
86      * The reasons this walk is occuring.
87      * This is a vector ordered in time. The reasons and the front were started
88      * first, and so should be acted first when a node is visisted.
89      */
90     fib_node_back_walk_ctx_t *fw_ctx;
91 } fib_walk_t;
92
93 /**
94  * @brief The pool of all walk objects
95  */
96 static fib_walk_t *fib_walk_pool;
97
98 /**
99  * @brief There's only one event type sent to the walk process
100  */
101 #define FIB_WALK_EVENT 0
102
103 /**
104  * Statistics maintained per-walk queue
105  */
106 typedef enum fib_walk_queue_stats_t_
107 {
108     FIB_WALK_SCHEDULED,
109     FIB_WALK_COMPLETED,
110 } fib_walk_queue_stats_t;
111 #define FIB_WALK_QUEUE_STATS_NUM ((fib_walk_queue_stats_t)(FIB_WALK_COMPLETED+1))
112
113 #define FIB_WALK_QUEUE_STATS {           \
114     [FIB_WALK_SCHEDULED] = "scheduled",  \
115     [FIB_WALK_COMPLETED] = "completed",  \
116 }
117
118 #define FOR_EACH_FIB_WALK_QUEUE_STATS(_wqs)   \
119     for ((_wqs) = FIB_WALK_SCHEDULED;         \
120          (_wqs) < FIB_WALK_QUEUE_STATS_NUM;   \
121          (_wqs)++)
122
123 /**
124  * The names of the walk stats
125  */
126 static const char * const fib_walk_queue_stats_names[] = FIB_WALK_QUEUE_STATS;
127 /**
128  * The names of the walk reasons
129  */
130 static const char * const fib_node_bw_reason_names[] = FIB_NODE_BW_REASONS;
131
132 /**
133  * A represenation of one queue of walk
134  */
135 typedef struct fib_walk_queue_t_
136 {
137     /**
138      * Qeuee stats
139      */
140     u64 fwq_stats[FIB_WALK_QUEUE_STATS_NUM];
141
142     /**
143      * The node list which acts as the queue
144      */
145     fib_node_list_t fwq_queue;
146 } fib_walk_queue_t;
147
148 /**
149  * A set of priority queues for outstanding walks
150  */
151 typedef struct fib_walk_queues_t_
152 {
153     fib_walk_queue_t fwqs_queues[FIB_WALK_PRIORITY_NUM];
154 } fib_walk_queues_t;
155
156 /**
157  * The global queues of outstanding walks
158  */
159 static fib_walk_queues_t fib_walk_queues;
160
161 /**
162  * The names of the walk priorities
163  */
164 static const char * const fib_walk_priority_names[] = FIB_WALK_PRIORITIES;
165
166 /**
167  * @brief Histogram stats on the lenths of each walk in elemenets visisted.
168  * Store upto 1<<23 elements in increments of 1<<10
169  */
170 #define HISTOGRAM_VISITS_PER_WALK_MAX (1<<23)
171 #define HISTOGRAM_VISITS_PER_WALK_INCR (1<<10)
172 #define HISTOGRAM_VISITS_PER_WALK_N_BUCKETS \
173     (HISTOGRAM_VISITS_PER_WALK_MAX/HISTOGRAM_VISITS_PER_WALK_INCR)
174 static u64 fib_walk_hist_vists_per_walk[HISTOGRAM_VISITS_PER_WALK_N_BUCKETS];
175
176 /**
177  * @brief History of state for the last 128 walks
178  */
179 #define HISTORY_N_WALKS 128
180 static u32 history_last_walk_pos;
181 typedef struct fib_walk_history_t_ {
182     u32 fwh_n_visits;
183     f64 fwh_duration;
184     f64 fwh_completed;
185     fib_node_ptr_t fwh_parent;
186     fib_walk_flags_t fwh_flags;
187     fib_node_bw_reason_flag_t fwh_reason;
188 } fib_walk_history_t;
189 static fib_walk_history_t fib_walk_history[HISTORY_N_WALKS];
190
191 u8*
192 format_fib_walk_priority (u8 *s, va_list ap)
193 {
194     fib_walk_priority_t prio = va_arg(ap, fib_walk_priority_t);
195
196     ASSERT(prio < FIB_WALK_PRIORITY_NUM);
197
198     return (format(s, "%s", fib_walk_priority_names[prio]));
199 }
200 static u8*
201 format_fib_walk_queue_stats (u8 *s, va_list ap)
202 {
203     fib_walk_queue_stats_t wqs = va_arg(ap, fib_walk_queue_stats_t);
204
205     ASSERT(wqs < FIB_WALK_QUEUE_STATS_NUM);
206
207     return (format(s, "%s", fib_walk_queue_stats_names[wqs]));
208 }
209
210 static index_t
211 fib_walk_get_index (fib_walk_t *fwalk)
212 {
213     return (fwalk - fib_walk_pool);
214 }
215
216 static fib_walk_t *
217 fib_walk_get (index_t fwi)
218 {
219     return (pool_elt_at_index(fib_walk_pool, fwi));
220 }
221
222 /*
223  * not static so it can be used in the unit tests
224  */
225 u32
226 fib_walk_queue_get_size (fib_walk_priority_t prio)
227 {
228     return (fib_node_list_get_size(fib_walk_queues.fwqs_queues[prio].fwq_queue));
229 }
230
231 static fib_node_index_t
232 fib_walk_queue_get_front (fib_walk_priority_t prio)
233 {
234     fib_node_ptr_t wp;
235
236     fib_node_list_get_front(fib_walk_queues.fwqs_queues[prio].fwq_queue, &wp);
237
238     return (wp.fnp_index);
239 }
240
241 static void
242 fib_walk_destroy (fib_walk_t *fwalk)
243 {
244     fib_node_back_walk_ctx_t *ctx;
245     u32 bucket;
246
247     if (FIB_NODE_INDEX_INVALID != fwalk->fw_prio_sibling)
248     {
249         fib_node_list_elt_remove(fwalk->fw_prio_sibling);
250     }
251     fib_node_child_remove(fwalk->fw_parent.fnp_type,
252                           fwalk->fw_parent.fnp_index,
253                           fwalk->fw_dep_sibling);
254
255     /*
256      * add the stats to the continuous histogram collection.
257      */
258     bucket = (fwalk->fw_n_visits / HISTOGRAM_VISITS_PER_WALK_INCR);
259     bucket = (bucket >= HISTOGRAM_VISITS_PER_WALK_N_BUCKETS ?
260               HISTOGRAM_VISITS_PER_WALK_N_BUCKETS - 1 :
261               bucket);
262     fib_walk_hist_vists_per_walk[bucket]++;
263
264     /*
265      * save stats to the recent history
266      */
267
268     fib_walk_history[history_last_walk_pos].fwh_n_visits =
269         fwalk->fw_n_visits;
270     fib_walk_history[history_last_walk_pos].fwh_completed =
271         vlib_time_now(vlib_get_main());
272     fib_walk_history[history_last_walk_pos].fwh_duration =
273         fib_walk_history[history_last_walk_pos].fwh_completed -
274         fwalk->fw_start_time;
275     fib_walk_history[history_last_walk_pos].fwh_parent =
276         fwalk->fw_parent;
277     fib_walk_history[history_last_walk_pos].fwh_flags =
278         fwalk->fw_flags;
279
280     fib_walk_history[history_last_walk_pos].fwh_reason = 0;
281     vec_foreach(ctx, fwalk->fw_ctx)
282     {
283         fib_walk_history[history_last_walk_pos].fwh_reason |=
284             ctx->fnbw_reason;
285     }
286
287     history_last_walk_pos = (history_last_walk_pos + 1) % HISTORY_N_WALKS;
288
289     fib_node_deinit(&fwalk->fw_node);
290     pool_put(fib_walk_pool, fwalk);
291 }
292
293 /**
294  * return code when advancing a walk
295  */
296 typedef enum fib_walk_advance_rc_t_
297 {
298     /**
299      * The walk is complete
300      */
301     FIB_WALK_ADVANCE_DONE,
302     /**
303      * the walk has more work
304      */
305     FIB_WALK_ADVANCE_MORE,
306     /**
307      * The walk merged with the one in front
308      */
309     FIB_WALK_ADVANCE_MERGE,
310 } fib_walk_advance_rc_t;
311
312 /**
313  * @brief Advance the walk one element in its work list
314  */
315 static fib_walk_advance_rc_t
316 fib_walk_advance (fib_node_index_t fwi)
317 {
318     fib_node_back_walk_ctx_t *ctx;
319     fib_node_back_walk_rc_t wrc;
320     fib_node_ptr_t sibling;
321     fib_walk_t *fwalk;
322     int more_elts;
323
324     /*
325      * this walk function is re-entrant - walks acan spawn walks.
326      * fib_walk_t objects come from a pool, so they can realloc. we need
327      * to retch from said pool at the appropriate times.
328      */
329     fwalk = fib_walk_get(fwi);
330
331     more_elts = fib_node_list_elt_get_next(fwalk->fw_dep_sibling, &sibling);
332
333     if (more_elts)
334     {
335         vec_foreach(ctx, fwalk->fw_ctx)
336         {
337             wrc = fib_node_back_walk_one(&sibling, ctx);
338
339             fwalk = fib_walk_get(fwi);
340             fwalk->fw_n_visits++;
341
342             if (FIB_NODE_BACK_WALK_MERGE == wrc)
343             {
344                 /*
345                  * this walk has merged with the one further along the node's
346                  * dependecy list.
347                  */
348                 return (FIB_WALK_ADVANCE_MERGE);
349             }
350         }
351         /*
352          * move foward to the next node to visit
353          */
354         more_elts = fib_node_list_advance(fwalk->fw_dep_sibling);
355     }
356
357     if (more_elts)
358     {
359         return (FIB_WALK_ADVANCE_MORE);
360     }
361
362     return (FIB_WALK_ADVANCE_DONE);
363 }
364
365 /**
366  * @breif Enurmerate the times of sleep between walks
367  */
368 typedef enum fib_walk_sleep_type_t_
369 {
370     FIB_WALK_SHORT_SLEEP,
371     FIB_WALK_LONG_SLEEP,
372 } fib_walk_sleep_type_t;
373
374 #define FIB_WALK_N_SLEEP (FIB_WALK_LONG_SLEEP+1)
375
376 /**
377  * @brief Durations for the sleep types
378  */
379 static f64 fib_walk_sleep_duration[] = {
380     [FIB_WALK_LONG_SLEEP] = 1e-3,
381     [FIB_WALK_SHORT_SLEEP] = 1e-8,
382 };
383
384 /**
385  * @brief The time quota for a walk. When more than this amount of time is
386  * spent, the walk process will yield.
387  */
388 static f64 quota = 1e-4;
389
390 /**
391  * Histogram on the amount of work done (in msecs) in each walk
392  */
393 #define N_TIME_BUCKETS 128
394 #define TIME_INCREMENTS (N_TIME_BUCKETS/2)
395 static u64 fib_walk_work_time_taken[N_TIME_BUCKETS];
396
397 /**
398  * Histogram on the number of nodes visted in each quota
399  */
400 #define N_ELTS_BUCKETS 128
401 static u32 fib_walk_work_nodes_visisted_incr = 2;
402 static u64 fib_walk_work_nodes_visited[N_ELTS_BUCKETS];
403
404 /**
405  * Histogram of the sleep lengths
406  */
407 static u64 fib_walk_sleep_lengths[2];
408
409 /**
410  * @brief Service the queues
411  * This is not declared static so that it can be unit tested - i know i know...
412  */
413 f64
414 fib_walk_process_queues (vlib_main_t * vm,
415                          const f64 quota)
416 {
417     f64 start_time, consumed_time;
418     fib_walk_sleep_type_t sleep;
419     fib_walk_priority_t prio;
420     fib_walk_advance_rc_t rc;
421     fib_node_index_t fwi;
422     fib_walk_t *fwalk;
423     u32 n_elts;
424     i32 bucket;
425
426     consumed_time = 0;
427     start_time = vlib_time_now(vm);
428     n_elts = 0;
429
430     FOR_EACH_FIB_WALK_PRIORITY(prio)
431     {
432         while (0 != fib_walk_queue_get_size(prio))
433         {
434             fwi = fib_walk_queue_get_front(prio);
435
436             /*
437              * set this walk as executing
438              */
439             fwalk = fib_walk_get(fwi);
440             fwalk->fw_flags |= FIB_WALK_FLAG_EXECUTING;
441
442             do
443             {
444                 rc = fib_walk_advance(fwi);
445                 n_elts++;
446                 consumed_time = (vlib_time_now(vm) - start_time);
447             } while ((consumed_time < quota) &&
448                      (FIB_WALK_ADVANCE_MORE == rc));
449
450             /*
451              * if this walk has no more work then pop it from the queue
452              * and move on to the next.
453              */
454             if (FIB_WALK_ADVANCE_MORE != rc)
455             {
456                 fwalk = fib_walk_get(fwi);
457                 fib_walk_destroy(fwalk);
458                 fib_walk_queues.fwqs_queues[prio].fwq_stats[FIB_WALK_COMPLETED]++;
459             }
460             else
461             {
462                 /*
463                  * passed our work quota. sleep time.
464                  */
465                 fwalk = fib_walk_get(fwi);
466                 fwalk->fw_flags &= ~FIB_WALK_FLAG_EXECUTING;
467                 sleep = FIB_WALK_SHORT_SLEEP;
468                 goto that_will_do_for_now;
469             }
470         }
471     }
472     /*
473      * got to the end of all the work
474      */
475     sleep = FIB_WALK_LONG_SLEEP;
476
477 that_will_do_for_now:
478
479     /*
480      * collect the stats:
481      *  - for the number of nodes visisted we store 128 increments
482      *  - for the time consumed we store quota/TIME_INCREMENTS increments.
483      */
484     bucket = ((n_elts/fib_walk_work_nodes_visisted_incr) > N_ELTS_BUCKETS ?
485               N_ELTS_BUCKETS-1 :
486               n_elts/fib_walk_work_nodes_visisted_incr);
487     ++fib_walk_work_nodes_visited[bucket];
488
489     bucket = (consumed_time - quota) / (quota / TIME_INCREMENTS);
490     bucket += N_TIME_BUCKETS/2;
491     bucket = (bucket < 0 ? 0 : bucket);
492     bucket = (bucket > N_TIME_BUCKETS-1 ? N_TIME_BUCKETS-1 : bucket);
493     ++fib_walk_work_time_taken[bucket];
494
495     ++fib_walk_sleep_lengths[sleep];
496
497     return (fib_walk_sleep_duration[sleep]);
498 }
499
500 /**
501  * @brief The 'fib-walk' process's main loop.
502  */
503 static uword
504 fib_walk_process (vlib_main_t * vm,
505                   vlib_node_runtime_t * node,
506                   vlib_frame_t * f)
507 {
508     f64 sleep_time;
509
510     sleep_time = fib_walk_sleep_duration[FIB_WALK_SHORT_SLEEP];
511
512     while (1)
513     {
514         vlib_process_wait_for_event_or_clock(vm, sleep_time);
515
516         /*
517          * there may be lots of event queued between the processes,
518          * but the walks we want to schedule are in the priority queues,
519          * so we ignore the process events.
520          */
521         vlib_process_get_events(vm, NULL);
522
523         sleep_time = fib_walk_process_queues(vm, quota);
524     }
525
526     /*
527      * Unreached
528      */
529     ASSERT(!"WTF");
530     return 0;
531 }
532
533 /* *INDENT-OFF* */
534 VLIB_REGISTER_NODE (fib_walk_process_node,static) = {
535     .function = fib_walk_process,
536     .type = VLIB_NODE_TYPE_PROCESS,
537     .name = "fib-walk",
538 };
539 /* *INDENT-ON* */
540
541 /**
542  * @brief Allocate a new walk object
543  */
544 static fib_walk_t *
545 fib_walk_alloc (fib_node_type_t parent_type,
546                 fib_node_index_t parent_index,
547                 fib_walk_flags_t flags,
548                 fib_node_back_walk_ctx_t *ctx)
549 {
550     fib_walk_t *fwalk;
551
552     pool_get(fib_walk_pool, fwalk);
553
554     fib_node_init(&fwalk->fw_node, FIB_NODE_TYPE_WALK);
555
556     fwalk->fw_flags = flags;
557     fwalk->fw_dep_sibling  = FIB_NODE_INDEX_INVALID;
558     fwalk->fw_prio_sibling = FIB_NODE_INDEX_INVALID;
559     fwalk->fw_parent.fnp_index = parent_index;
560     fwalk->fw_parent.fnp_type = parent_type;
561     fwalk->fw_ctx = NULL;
562     fwalk->fw_start_time = vlib_time_now(vlib_get_main());
563     fwalk->fw_n_visits = 0;
564
565     /*
566      * make a copy of the backwalk context so the depth count remains
567      * the same for each sibling visitsed. This is important in the case
568      * where a parents has a loop via one child, but all the others are not.
569      * if the looped child were visited first, the depth count would exceed, the
570      * max and the walk would terminate before it reached the other siblings.
571      */
572     vec_add1(fwalk->fw_ctx, *ctx);
573
574     return (fwalk);
575 }
576
577 /**
578  * @brief Enqueue a walk onto the appropriate priority queue. Then signal
579  * the background process there is work to do.
580  */
581 static index_t
582 fib_walk_prio_queue_enquue (fib_walk_priority_t prio,
583                             fib_walk_t *fwalk)
584 {
585     index_t sibling;
586
587     sibling = fib_node_list_push_front(fib_walk_queues.fwqs_queues[prio].fwq_queue,
588                                        0,
589                                        FIB_NODE_TYPE_WALK,
590                                        fib_walk_get_index(fwalk));
591     fib_walk_queues.fwqs_queues[prio].fwq_stats[FIB_WALK_SCHEDULED]++;
592
593     /*
594      * poke the fib-walk process to perform the async walk.
595      * we are not passing it specific data, hence the last two args,
596      * the process will drain the queues
597      */
598     vlib_process_signal_event(vlib_get_main(),
599                               fib_walk_process_node.index,
600                               FIB_WALK_EVENT,
601                               FIB_WALK_EVENT);
602
603     return (sibling);
604 }
605
606 void
607 fib_walk_async (fib_node_type_t parent_type,
608                 fib_node_index_t parent_index,
609                 fib_walk_priority_t prio,
610                 fib_node_back_walk_ctx_t *ctx)
611 {
612     fib_walk_t *fwalk;
613
614     if (FIB_NODE_GRAPH_MAX_DEPTH < ++ctx->fnbw_depth)
615     {
616         /*
617          * The walk has reached the maximum depth. there is a loop in the graph.
618          * bail.
619          */
620         return;
621     }
622     if (0 == fib_node_get_n_children(parent_type,
623                                      parent_index))
624     {
625         /*
626          * no children to walk - quit now
627          */
628         return;
629     }
630     if (ctx->fnbw_flags & FIB_NODE_BW_FLAG_FORCE_SYNC)
631     {
632         /*
633          * the originator of the walk wanted it to be synchronous, but the
634          * parent object chose async - denied.
635          */
636         return (fib_walk_sync(parent_type, parent_index, ctx));
637     }
638
639
640     fwalk = fib_walk_alloc(parent_type,
641                            parent_index,
642                            FIB_WALK_FLAG_ASYNC,
643                            ctx);
644
645     fwalk->fw_dep_sibling = fib_node_child_add(parent_type,
646                                                parent_index,
647                                                FIB_NODE_TYPE_WALK,
648                                                fib_walk_get_index(fwalk));
649
650     fwalk->fw_prio_sibling = fib_walk_prio_queue_enquue(prio, fwalk);
651 }
652
653 /**
654  * @brief Back walk all the children of a FIB node.
655  *
656  * note this is a synchronous depth first walk. Children visited may propagate
657  * the walk to thier children. Other children node types may not propagate,
658  * synchronously but instead queue the walk for later async completion.
659  */
660 void
661 fib_walk_sync (fib_node_type_t parent_type,
662                fib_node_index_t parent_index,
663                fib_node_back_walk_ctx_t *ctx)
664 {
665     fib_walk_advance_rc_t rc;
666     fib_node_index_t fwi;
667     fib_walk_t *fwalk;
668
669     if (FIB_NODE_GRAPH_MAX_DEPTH < ++ctx->fnbw_depth)
670     {
671         /*
672          * The walk has reached the maximum depth. there is a loop in the graph.
673          * bail.
674          */
675         return;
676     }
677     if (0 == fib_node_get_n_children(parent_type,
678                                      parent_index))
679     {
680         /*
681          * no children to walk - quit now
682          */
683         return;
684     }
685
686     fwalk = fib_walk_alloc(parent_type,
687                            parent_index,
688                            FIB_WALK_FLAG_SYNC,
689                            ctx);
690
691     fwalk->fw_dep_sibling = fib_node_child_add(parent_type,
692                                                parent_index,
693                                                FIB_NODE_TYPE_WALK,
694                                                fib_walk_get_index(fwalk));
695     fwi = fib_walk_get_index(fwalk);
696
697     while (1)
698     {
699         /*
700          * set this walk as executing
701          */
702         fwalk->fw_flags |= FIB_WALK_FLAG_EXECUTING;
703
704         do
705         {
706             rc = fib_walk_advance(fwi);
707         } while (FIB_WALK_ADVANCE_MORE == rc);
708
709
710         /*
711          * this walk function is re-entrant - walks can spawn walks.
712          * fib_walk_t objects come from a pool, so they can realloc. we need
713          * to re-fetch from said pool at the appropriate times.
714          */
715         fwalk = fib_walk_get(fwi);
716
717         if (FIB_WALK_ADVANCE_MERGE == rc)
718         {
719             /*
720              * this sync walk merged with an walk in front.
721              * by reqeusting a sync walk the client wanted all children walked,
722              * so we ditch the walk object in hand and continue with the one
723              * we merged into
724              */
725             fib_node_ptr_t merged_walk;
726
727             fib_node_list_elt_get_next(fwalk->fw_dep_sibling, &merged_walk);
728
729             ASSERT(FIB_NODE_INDEX_INVALID != merged_walk.fnp_index);
730             ASSERT(FIB_NODE_TYPE_WALK == merged_walk.fnp_type);
731
732             fib_walk_destroy(fwalk);
733
734             fwi = merged_walk.fnp_index;
735             fwalk = fib_walk_get(fwi);
736
737             if (FIB_WALK_FLAG_EXECUTING & fwalk->fw_flags)
738             {
739                 /*
740                  * we are executing a sync walk, and we have met with another
741                  * walk that is also executing. since only one walk executs at once
742                  * (there is no multi-threading) this implies we have met ourselves
743                  * and hence the is a loop in the graph.
744                  * This function is re-entrant, so the walk object we met is being
745                  * acted on in a stack frame below this one. We must therefore not
746                  * continue with it now, but let the stack unwind and along the
747                  * appropriate frame to read the depth count and bail.
748                  */
749                 fwalk = NULL;
750                 break;
751             }
752         }
753         else
754         {
755             /*
756              * the walk reached the end of the depdency list.
757              */
758             break;
759         }
760     }
761
762     if (NULL != fwalk)
763     {
764         fib_walk_destroy(fwalk);
765     }
766 }
767
768 static fib_node_t *
769 fib_walk_get_node (fib_node_index_t index)
770 {
771     fib_walk_t *fwalk;
772
773     fwalk = fib_walk_get(index);
774
775     return (&(fwalk->fw_node));
776 }
777
778 /**
779  * Walk objects are not parents, nor are they locked.
780  * are no-ops
781  */
782 static void
783 fib_walk_last_lock_gone (fib_node_t *node)
784 {
785     ASSERT(0);
786 }
787
788 static fib_walk_t*
789 fib_walk_get_from_node (fib_node_t *node)
790 {
791     return ((fib_walk_t*)(((char*)node) -
792                           STRUCT_OFFSET_OF(fib_walk_t, fw_node)));
793 }
794
795 /**
796  * @brief Another back walk has reach this walk.
797  * Megre them so there is only one left. It is this node being
798  * visited that will remain, so copy or merge the context onto it.
799  */
800 static fib_node_back_walk_rc_t
801 fib_walk_back_walk_notify (fib_node_t *node,
802                            fib_node_back_walk_ctx_t *ctx)
803 {
804     fib_node_back_walk_ctx_t *old;
805     fib_walk_t *fwalk;
806
807     fwalk = fib_walk_get_from_node(node);
808
809     /*
810      * check whether the walk context can be merge with another,
811      * or whether it needs to be appended.
812      */
813     vec_foreach(old, fwalk->fw_ctx)
814     {
815         /*
816          * we can merge walks if the reason for the walk is the same.
817          */
818         if (old->fnbw_reason == ctx->fnbw_reason)
819         {
820             /*
821              * copy the largest of the depth values. in the presence of a loop,
822              * the same walk will merge with itself. if we take the smaller depth
823              * then it will never end.
824              */
825             old->fnbw_depth = ((old->fnbw_depth >= ctx->fnbw_depth) ?
826                                 old->fnbw_depth :
827                                 ctx->fnbw_depth);
828             goto out;
829         }
830     }
831
832     /*
833      * walks could not be merged, this means that the walk infront needs to
834      * perform different action to this one that has caught up. the one in front
835      * was scheduled first so append the new walk context to the back of the list.
836      */
837     vec_add1(fwalk->fw_ctx, *ctx);
838
839 out:
840     return (FIB_NODE_BACK_WALK_MERGE);
841 }
842
843 /**
844  * The FIB walk's graph node virtual function table
845  */
846 static const fib_node_vft_t fib_walk_vft = {
847     .fnv_get = fib_walk_get_node,
848     .fnv_last_lock = fib_walk_last_lock_gone,
849     .fnv_back_walk = fib_walk_back_walk_notify,
850 };
851
852 void
853 fib_walk_module_init (void)
854 {
855     fib_walk_priority_t prio;
856
857     FOR_EACH_FIB_WALK_PRIORITY(prio)
858     {
859         fib_walk_queues.fwqs_queues[prio].fwq_queue = fib_node_list_create();
860     }
861
862     fib_node_register_type(FIB_NODE_TYPE_WALK, &fib_walk_vft);
863 }
864
865 static u8*
866 format_fib_walk (u8* s, va_list ap)
867 {
868     fib_node_index_t fwi = va_arg(ap, fib_node_index_t);
869     fib_walk_t *fwalk;
870
871     fwalk = fib_walk_get(fwi);
872
873     return (format(s, "  parent:{%s:%d} visits:%d flags:%d",
874                    fib_node_type_get_name(fwalk->fw_parent.fnp_type),
875                    fwalk->fw_parent.fnp_index,
876                    fwalk->fw_n_visits,
877                    fwalk->fw_flags));
878 }
879
880 static clib_error_t *
881 fib_walk_show (vlib_main_t * vm,
882                unformat_input_t * input,
883                vlib_cli_command_t * cmd)
884 {
885     fib_walk_queue_stats_t wqs;
886     fib_walk_priority_t prio;
887     fib_node_ptr_t sibling;
888     fib_node_index_t fwi;
889     fib_walk_t *fwalk;
890     int more_elts, ii;
891     u8 *s = NULL;
892
893 #define USEC 1000000
894     vlib_cli_output(vm, "FIB Walk Quota = %.2fusec:", quota * USEC);
895     vlib_cli_output(vm, "FIB Walk queues:");
896
897     FOR_EACH_FIB_WALK_PRIORITY(prio)
898     {
899         vlib_cli_output(vm, " %U priority queue:",
900                         format_fib_walk_priority, prio);
901         vlib_cli_output(vm, "  Stats: ");
902
903         FOR_EACH_FIB_WALK_QUEUE_STATS(wqs)
904         {
905             vlib_cli_output(vm, "    %U:%d",
906                             format_fib_walk_queue_stats, wqs,
907                             fib_walk_queues.fwqs_queues[prio].fwq_stats[wqs]);
908         }
909         vlib_cli_output(vm, "  Occupancy:%d",
910                         fib_node_list_get_size(
911                             fib_walk_queues.fwqs_queues[prio].fwq_queue));
912
913         more_elts = fib_node_list_get_front(
914                         fib_walk_queues.fwqs_queues[prio].fwq_queue,
915                         &sibling);
916
917         while (more_elts)
918         {
919             ASSERT(FIB_NODE_INDEX_INVALID != sibling.fnp_index);
920             ASSERT(FIB_NODE_TYPE_WALK == sibling.fnp_type);
921
922             fwi = sibling.fnp_index;
923             fwalk = fib_walk_get(fwi);
924
925             vlib_cli_output(vm, "  %U", format_fib_walk, fwi);
926
927             more_elts = fib_node_list_elt_get_next(fwalk->fw_prio_sibling,
928                                                    &sibling);
929         }
930     }
931
932     vlib_cli_output(vm, "Histogram Statistics:");
933     vlib_cli_output(vm, " Number of Elements visit per-quota:");
934     for (ii = 0; ii < N_ELTS_BUCKETS; ii++)
935     {
936         if (0 != fib_walk_work_nodes_visited[ii])
937             s = format(s, "%d:%d ",
938                        (ii * fib_walk_work_nodes_visisted_incr),
939                        fib_walk_work_nodes_visited[ii]);
940     }
941     vlib_cli_output(vm, "  %v", s);
942     vec_free(s);
943
944     vlib_cli_output(vm, " Time consumed per-quota (Quota=%f usec):", quota*USEC);
945     s = format(s, "0:%d ", fib_walk_work_time_taken[0]);
946     for (ii = 1; ii < N_TIME_BUCKETS; ii++)
947     {
948         if (0 != fib_walk_work_time_taken[ii])
949             s = format(s, "%d:%d ", (u32)((((ii - N_TIME_BUCKETS/2) *
950                                            (quota / TIME_INCREMENTS)) + quota) *
951                                          USEC),
952                        fib_walk_work_time_taken[ii]);
953     }
954     vlib_cli_output(vm, "  %v", s);
955     vec_free(s);
956
957     vlib_cli_output(vm, " Sleep Types:");
958     vlib_cli_output(vm, "  Short  Long:");
959     vlib_cli_output(vm, "  %d %d:",
960                     fib_walk_sleep_lengths[FIB_WALK_SHORT_SLEEP],
961                     fib_walk_sleep_lengths[FIB_WALK_LONG_SLEEP]);
962
963     vlib_cli_output(vm, " Number of Elements visited per-walk:");
964     for (ii = 0; ii < HISTOGRAM_VISITS_PER_WALK_N_BUCKETS; ii++)
965     {
966         if (0 != fib_walk_hist_vists_per_walk[ii])
967             s = format(s, "%d:%d ",
968                        ii*HISTOGRAM_VISITS_PER_WALK_INCR,
969                        fib_walk_hist_vists_per_walk[ii]);
970     }
971     vlib_cli_output(vm, "  %v", s);
972     vec_free(s);
973
974
975     vlib_cli_output(vm, "Brief History (last %d walks):", HISTORY_N_WALKS);
976     ii = history_last_walk_pos - 1;
977     if (ii < 0)
978         ii = HISTORY_N_WALKS - 1;
979
980     while (ii != history_last_walk_pos)
981     {
982         if (0 != fib_walk_history[ii].fwh_reason)
983         {
984             fib_node_back_walk_reason_t reason;
985             u8 *s = NULL;
986
987             s = format(s, "[@%d]: %s:%d visits:%d duration:%.2f completed:%.2f ",
988                        ii, fib_node_type_get_name(fib_walk_history[ii].fwh_parent.fnp_type),
989                        fib_walk_history[ii].fwh_parent.fnp_index,
990                        fib_walk_history[ii].fwh_n_visits,
991                        fib_walk_history[ii].fwh_duration,
992                        fib_walk_history[ii].fwh_completed);
993             if (FIB_WALK_FLAG_SYNC & fib_walk_history[ii].fwh_flags)
994                 s = format(s, "sync, ");
995             if (FIB_WALK_FLAG_ASYNC & fib_walk_history[ii].fwh_flags)
996                 s = format(s, "async, ");
997
998             s = format(s, "reason:");
999             FOR_EACH_FIB_NODE_BW_REASON(reason) {
1000                 if ((1<<reason) & fib_walk_history[ii].fwh_reason) {
1001                     s = format (s, "%s,", fib_node_bw_reason_names[reason]);
1002                 }
1003             }
1004             vlib_cli_output(vm, "%v", s);
1005         }
1006
1007         ii--;
1008         if (ii < 0)
1009             ii = HISTORY_N_WALKS - 1;
1010     }
1011
1012     return (NULL);
1013 }
1014
1015 VLIB_CLI_COMMAND (fib_walk_show_command, static) = {
1016     .path = "show fib walk",
1017     .short_help = "show fib walk",
1018     .function = fib_walk_show,
1019 };
1020
1021 static clib_error_t *
1022 fib_walk_set_quota (vlib_main_t * vm,
1023                     unformat_input_t * input,
1024                     vlib_cli_command_t * cmd)
1025 {
1026     clib_error_t * error = NULL;
1027     f64 new_quota;
1028
1029     if (unformat (input, "%f", &new_quota))
1030     {
1031         quota = new_quota;
1032     }
1033     else
1034     {
1035         error = clib_error_return(0 , "Pass a float value");
1036     }
1037
1038     return (error);
1039 }
1040
1041 VLIB_CLI_COMMAND (fib_walk_set_quota_command, static) = {
1042     .path = "set fib walk quota",
1043     .short_help = "set fib walk quota",
1044     .function = fib_walk_set_quota,
1045 };
1046
1047 static clib_error_t *
1048 fib_walk_set_histogram_elements_size (vlib_main_t * vm,
1049                                       unformat_input_t * input,
1050                                       vlib_cli_command_t * cmd)
1051 {
1052     clib_error_t * error = NULL;
1053     u32 new;
1054
1055     if (unformat (input, "%d", &new))
1056     {
1057         fib_walk_work_nodes_visisted_incr = new;
1058     }
1059     else
1060     {
1061         error = clib_error_return(0 , "Pass an int value");
1062     }
1063
1064     return (error);
1065 }
1066
1067 VLIB_CLI_COMMAND (fib_walk_set_histogram_elements_size_command, static) = {
1068     .path = "set fib walk histogram elements size",
1069     .short_help = "set fib walk histogram elements size",
1070     .function = fib_walk_set_histogram_elements_size,
1071 };
1072
1073 static clib_error_t *
1074 fib_walk_clear (vlib_main_t * vm,
1075                 unformat_input_t * input,
1076                 vlib_cli_command_t * cmd)
1077 {
1078     memset(fib_walk_hist_vists_per_walk, 0, sizeof(fib_walk_hist_vists_per_walk));
1079     memset(fib_walk_history, 0, sizeof(fib_walk_history));
1080     memset(fib_walk_work_time_taken, 0, sizeof(fib_walk_work_time_taken));
1081     memset(fib_walk_work_nodes_visited, 0, sizeof(fib_walk_work_nodes_visited));
1082     memset(fib_walk_sleep_lengths, 0, sizeof(fib_walk_sleep_lengths));
1083
1084     return (NULL);
1085 }
1086
1087 VLIB_CLI_COMMAND (fib_walk_clear_command, static) = {
1088     .path = "clear fib walk",
1089     .short_help = "clear fib walk",
1090     .function = fib_walk_clear,
1091 };