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