L3 cross connect
[vpp.git] / src / vnet / fib / fib_walk.c
index 938f7b8..fca1bfe 100644 (file)
@@ -16,6 +16,8 @@
 #include <vnet/fib/fib_walk.h>
 #include <vnet/fib/fib_node_list.h>
 
+vlib_log_class_t fib_walk_logger;
+
 /**
  * The flags on a walk
  */
@@ -85,7 +87,7 @@ typedef struct fib_walk_t_
     /**
      * The reasons this walk is occuring.
      * This is a vector ordered in time. The reasons and the front were started
-     * first, and so should be acted first when a node is visisted.
+     * first, and so should be acted first when a node is visited.
      */
     fib_node_back_walk_ctx_t *fw_ctx;
 } fib_walk_t;
@@ -95,11 +97,6 @@ typedef struct fib_walk_t_
  */
 static fib_walk_t *fib_walk_pool;
 
-/**
- * @brief There's only one event type sent to the walk process
- */
-#define FIB_WALK_EVENT 0
-
 /**
  * Statistics maintained per-walk queue
  */
@@ -164,7 +161,7 @@ static fib_walk_queues_t fib_walk_queues;
 static const char * const fib_walk_priority_names[] = FIB_WALK_PRIORITIES;
 
 /**
- * @brief Histogram stats on the lenths of each walk in elemenets visisted.
+ * @brief Histogram stats on the lenths of each walk in elemenets visited.
  * Store upto 1<<23 elements in increments of 1<<10
  */
 #define HISTOGRAM_VISITS_PER_WALK_MAX (1<<23)
@@ -189,19 +186,30 @@ typedef struct fib_walk_history_t_ {
 } fib_walk_history_t;
 static fib_walk_history_t fib_walk_history[HISTORY_N_WALKS];
 
+static u8* format_fib_walk (u8* s, va_list *ap);
+
+#define FIB_WALK_DBG(_walk, _fmt, _args...)                     \
+{                                                               \
+    vlib_log_debug(fib_walk_logger,                             \
+                   "[%U]:" _fmt,                                \
+                   format_fib_walk,                             \
+                   fib_walk_get_index(_walk),                   \
+                   ##_args);                                    \
+}
+
 u8*
-format_fib_walk_priority (u8 *s, va_list ap)
+format_fib_walk_priority (u8 *s, va_list *ap)
 {
-    fib_walk_priority_t prio = va_arg(ap, fib_walk_priority_t);
+    fib_walk_priority_t prio = va_arg(*ap, fib_walk_priority_t);
 
     ASSERT(prio < FIB_WALK_PRIORITY_NUM);
 
     return (format(s, "%s", fib_walk_priority_names[prio]));
 }
 static u8*
-format_fib_walk_queue_stats (u8 *s, va_list ap)
+format_fib_walk_queue_stats (u8 *s, va_list *ap)
 {
-    fib_walk_queue_stats_t wqs = va_arg(ap, fib_walk_queue_stats_t);
+    fib_walk_queue_stats_t wqs = va_arg(*ap, fib_walk_queue_stats_t);
 
     ASSERT(wqs < FIB_WALK_QUEUE_STATS_NUM);
 
@@ -240,10 +248,13 @@ fib_walk_queue_get_front (fib_walk_priority_t prio)
 }
 
 static void
-fib_walk_destroy (fib_walk_t *fwalk)
+fib_walk_destroy (index_t fwi)
 {
+    fib_walk_t *fwalk;
     u32 bucket, ii;
 
+    fwalk = fib_walk_get(fwi);
+
     if (FIB_NODE_INDEX_INVALID != fwalk->fw_prio_sibling)
     {
        fib_node_list_elt_remove(fwalk->fw_prio_sibling);
@@ -252,6 +263,12 @@ fib_walk_destroy (fib_walk_t *fwalk)
                          fwalk->fw_parent.fnp_index,
                          fwalk->fw_dep_sibling);
 
+    /*
+     * refetch the walk object. More walks could have been spawned as a result
+     * of releasing the lock on the parent.
+     */
+    fwalk = fib_walk_get(fwi);
+
     /*
      * add the stats to the continuous histogram collection.
      */
@@ -318,10 +335,10 @@ typedef enum fib_walk_advance_rc_t_
 static fib_walk_advance_rc_t
 fib_walk_advance (fib_node_index_t fwi)
 {
-    fib_node_back_walk_ctx_t *ctx, *old;
     fib_node_back_walk_rc_t wrc;
     fib_node_ptr_t sibling;
     fib_walk_t *fwalk;
+    uint n_ctxs, ii;
     int more_elts;
 
     /*
@@ -335,12 +352,22 @@ fib_walk_advance (fib_node_index_t fwi)
 
     if (more_elts)
     {
-        old = fwalk->fw_ctx;
 
-       vec_foreach(ctx, fwalk->fw_ctx)
-       {
-           wrc = fib_node_back_walk_one(&sibling, ctx);
+        /*
+         * loop through the backwalk contexts. This can grow in length
+         * as walks on the same object meet each other. Order is preserved so the
+         * most recently started walk as at the back of the vector.
+         */
+        ii = 0;
+        n_ctxs = vec_len(fwalk->fw_ctx);
+
+        while (ii < n_ctxs)
+        {
+            fib_node_back_walk_ctx_t ctx = fwalk->fw_ctx[ii];
+
+           wrc = fib_node_back_walk_one(&sibling, &ctx);
 
+            ii++;
            fwalk = fib_walk_get(fwi);
            fwalk->fw_n_visits++;
 
@@ -352,14 +379,11 @@ fib_walk_advance (fib_node_index_t fwi)
                 */
                return (FIB_WALK_ADVANCE_MERGE);
            }
-            if (old != fwalk->fw_ctx)
-            {
-                /*
-                 * nasty re-entrant addition of a walk has realloc'd the vector
-                 * break out
-                 */
-               return (FIB_WALK_ADVANCE_MERGE);
-           }
+
+            /*
+             * re-evaluate the number of backwalk contexts we need to process.
+             */
+            n_ctxs = vec_len(fwalk->fw_ctx);
        }
        /*
         * move foward to the next node to visit
@@ -390,7 +414,17 @@ typedef enum fib_walk_sleep_type_t_
  * @brief Durations for the sleep types
  */
 static f64 fib_walk_sleep_duration[] = {
-    [FIB_WALK_LONG_SLEEP] = 1e-3,
+    /**
+     * Long sleep when there is no more work, i.e. the queues are empty.
+     * This is a sleep (as opposed to a wait for event) just to be sure we
+     * are not missing events by sleeping forever.
+     */
+    [FIB_WALK_LONG_SLEEP] = 2,
+
+    /**
+     * Short sleep. There is work left in the queues. We are yielding the CPU
+     * momentarily.
+     */
     [FIB_WALK_SHORT_SLEEP] = 1e-8,
 };
 
@@ -411,7 +445,7 @@ static u64 fib_walk_work_time_taken[N_TIME_BUCKETS];
  * Histogram on the number of nodes visted in each quota
  */
 #define N_ELTS_BUCKETS 128
-static u32 fib_walk_work_nodes_visisted_incr = 2;
+static u32 fib_walk_work_nodes_visited_incr = 2;
 static u64 fib_walk_work_nodes_visited[N_ELTS_BUCKETS];
 
 /**
@@ -466,8 +500,7 @@ fib_walk_process_queues (vlib_main_t * vm,
             */
            if (FIB_WALK_ADVANCE_MORE != rc)
            {
-               fwalk = fib_walk_get(fwi);
-               fib_walk_destroy(fwalk);
+                fib_walk_destroy(fwi);
                fib_walk_queues.fwqs_queues[prio].fwq_stats[FIB_WALK_COMPLETED]++;
            }
            else
@@ -491,12 +524,12 @@ that_will_do_for_now:
 
     /*
      * collect the stats:
-     *  - for the number of nodes visisted we store 128 increments
+     *  - for the number of nodes visited we store 128 increments
      *  - for the time consumed we store quota/TIME_INCREMENTS increments.
      */
-    bucket = ((n_elts/fib_walk_work_nodes_visisted_incr) > N_ELTS_BUCKETS ?
+    bucket = ((n_elts/fib_walk_work_nodes_visited_incr) > N_ELTS_BUCKETS ?
              N_ELTS_BUCKETS-1 :
-             n_elts/fib_walk_work_nodes_visisted_incr);
+             n_elts/fib_walk_work_nodes_visited_incr);
     ++fib_walk_work_nodes_visited[bucket];
 
     bucket = (consumed_time - quota) / (quota / TIME_INCREMENTS);
@@ -510,6 +543,16 @@ that_will_do_for_now:
     return (fib_walk_sleep_duration[sleep]);
 }
 
+/**
+ * Events sent to the FIB walk process
+ */
+typedef enum fib_walk_process_event_t_
+{
+    FIB_WALK_PROCESS_EVENT_DATA,
+    FIB_WALK_PROCESS_EVENT_ENABLE,
+    FIB_WALK_PROCESS_EVENT_DISABLE,
+} fib_walk_process_event;
+
 /**
  * @brief The 'fib-walk' process's main loop.
  */
@@ -518,22 +561,47 @@ fib_walk_process (vlib_main_t * vm,
                  vlib_node_runtime_t * node,
                  vlib_frame_t * f)
 {
+    uword event_type, *event_data = 0;
     f64 sleep_time;
+    int enabled;
 
+    enabled = 1;
     sleep_time = fib_walk_sleep_duration[FIB_WALK_SHORT_SLEEP];
 
     while (1)
     {
-       vlib_process_wait_for_event_or_clock(vm, sleep_time);
+        /*
+         * the feature to disable/enable this walk process is only
+         * for testing purposes
+         */
+        if (enabled)
+        {
+            vlib_process_wait_for_event_or_clock(vm, sleep_time);
+        }
+        else
+        {
+            vlib_process_wait_for_event(vm);
+        }
 
-       /*
-        * there may be lots of event queued between the processes,
-        * but the walks we want to schedule are in the priority queues,
-        * so we ignore the process events.
-        */
-       vlib_process_get_events(vm, NULL);
+        event_type = vlib_process_get_events(vm, &event_data);
+        vec_reset_length(event_data);
+
+        switch (event_type)
+       {
+       case FIB_WALK_PROCESS_EVENT_ENABLE:
+            enabled = 1;
+            break;
+       case FIB_WALK_PROCESS_EVENT_DISABLE:
+            enabled = 0;
+            break;
+       default:
+            break;
+       }
 
-       sleep_time = fib_walk_process_queues(vm, quota);
+        if (enabled)
+        {
+            sleep_time = fib_walk_process_queues(vm, quota);
+        }
     }
 
     /*
@@ -610,8 +678,8 @@ fib_walk_prio_queue_enquue (fib_walk_priority_t prio,
      */
     vlib_process_signal_event(vlib_get_main(),
                              fib_walk_process_node.index,
-                             FIB_WALK_EVENT,
-                             FIB_WALK_EVENT);
+                             FIB_WALK_PROCESS_EVENT_DATA,
+                             0);
 
     return (sibling);
 }
@@ -661,6 +729,9 @@ fib_walk_async (fib_node_type_t parent_type,
                                               fib_walk_get_index(fwalk));
 
     fwalk->fw_prio_sibling = fib_walk_prio_queue_enquue(prio, fwalk);
+
+    FIB_WALK_DBG(fwalk, "async-start: %U",
+                 format_fib_node_bw_reason, ctx->fnbw_reason);
 }
 
 /**
@@ -706,6 +777,8 @@ fib_walk_sync (fib_node_type_t parent_type,
                                               FIB_NODE_TYPE_WALK,
                                               fib_walk_get_index(fwalk));
     fwi = fib_walk_get_index(fwalk);
+    FIB_WALK_DBG(fwalk, "sync-start: %U",
+                 format_fib_node_bw_reason, ctx->fnbw_reason);
 
     while (1)
     {
@@ -742,7 +815,7 @@ fib_walk_sync (fib_node_type_t parent_type,
            ASSERT(FIB_NODE_INDEX_INVALID != merged_walk.fnp_index);
            ASSERT(FIB_NODE_TYPE_WALK == merged_walk.fnp_type);
 
-           fib_walk_destroy(fwalk);
+           fib_walk_destroy(fwi);
 
            fwi = merged_walk.fnp_index;
            fwalk = fib_walk_get(fwi);
@@ -759,6 +832,10 @@ fib_walk_sync (fib_node_type_t parent_type,
                 * continue with it now, but let the stack unwind and along the
                 * appropriate frame to read the depth count and bail.
                 */
+                FIB_WALK_DBG(fwalk, "sync-stop: %U",
+                             format_fib_node_bw_reason,
+                             ctx->fnbw_reason);
+
                fwalk = NULL;
                break;
            }
@@ -774,7 +851,10 @@ fib_walk_sync (fib_node_type_t parent_type,
 
     if (NULL != fwalk)
     {
-       fib_walk_destroy(fwalk);
+        FIB_WALK_DBG(fwalk, "sync-stop: %U",
+                     format_fib_node_bw_reason,
+                     ctx->fnbw_reason);
+       fib_walk_destroy(fwi);
     }
 }
 
@@ -871,23 +951,38 @@ fib_walk_module_init (void)
     }
 
     fib_node_register_type(FIB_NODE_TYPE_WALK, &fib_walk_vft);
+    fib_walk_logger = vlib_log_register_class("fib", "walk");
 }
 
 static u8*
-format_fib_walk (u8* s, va_list ap)
+format_fib_walk (u8* s, va_list *ap)
 {
-    fib_node_index_t fwi = va_arg(ap, fib_node_index_t);
+    fib_node_index_t fwi = va_arg(*ap, fib_node_index_t);
     fib_walk_t *fwalk;
 
     fwalk = fib_walk_get(fwi);
 
-    return (format(s, "  parent:{%s:%d} visits:%d flags:%d",
+    return (format(s, "[@%d] parent:{%s:%d} visits:%d flags:%d", fwi,
                   fib_node_type_get_name(fwalk->fw_parent.fnp_type),
                   fwalk->fw_parent.fnp_index,
                   fwalk->fw_n_visits,
                   fwalk->fw_flags));
 }
 
+u8 *
+format_fib_node_bw_reason (u8 *s, va_list *args)
+{
+    fib_node_bw_reason_flag_t flag = va_arg (*args, int);
+    fib_node_back_walk_reason_t reason;
+
+    FOR_EACH_FIB_NODE_BW_REASON(reason) {
+        if ((1<<reason) & flag)
+            s = format(s, "%s", fib_node_bw_reason_names[reason]);
+    }
+
+    return (s);
+}
+
 static clib_error_t *
 fib_walk_show (vlib_main_t * vm,
               unformat_input_t * input,
@@ -946,7 +1041,7 @@ fib_walk_show (vlib_main_t * vm,
     {
        if (0 != fib_walk_work_nodes_visited[ii])
            s = format(s, "%d:%d ",
-                      (ii * fib_walk_work_nodes_visisted_incr),
+                      (ii * fib_walk_work_nodes_visited_incr),
                       fib_walk_work_nodes_visited[ii]);
     }
     vlib_cli_output(vm, "  %v", s);
@@ -992,7 +1087,6 @@ fib_walk_show (vlib_main_t * vm,
     {
        if (0 != fib_walk_history[ii].fwh_reason[0])
        {
-            fib_node_back_walk_reason_t reason;
             u8 *s = NULL;
             u32 jj;
 
@@ -1011,11 +1105,8 @@ fib_walk_show (vlib_main_t * vm,
             jj = 0;
             while (0 != fib_walk_history[ii].fwh_reason[jj])
             {
-                FOR_EACH_FIB_NODE_BW_REASON(reason) {
-                    if ((1<<reason) & fib_walk_history[ii].fwh_reason[jj]) {
-                        s = format (s, "%s,", fib_node_bw_reason_names[reason]);
-                    }
-                }
+                s = format (s, "%U,", format_fib_node_bw_reason,
+                            fib_walk_history[ii].fwh_reason[jj]);
                 jj++;
             }
             vlib_cli_output(vm, "%v", s);
@@ -1071,7 +1162,7 @@ fib_walk_set_histogram_elements_size (vlib_main_t * vm,
 
     if (unformat (input, "%d", &new))
     {
-       fib_walk_work_nodes_visisted_incr = new;
+       fib_walk_work_nodes_visited_incr = new;
     }
     else
     {
@@ -1092,11 +1183,11 @@ fib_walk_clear (vlib_main_t * vm,
                unformat_input_t * input,
                vlib_cli_command_t * cmd)
 {
-    memset(fib_walk_hist_vists_per_walk, 0, sizeof(fib_walk_hist_vists_per_walk));
-    memset(fib_walk_history, 0, sizeof(fib_walk_history));
-    memset(fib_walk_work_time_taken, 0, sizeof(fib_walk_work_time_taken));
-    memset(fib_walk_work_nodes_visited, 0, sizeof(fib_walk_work_nodes_visited));
-    memset(fib_walk_sleep_lengths, 0, sizeof(fib_walk_sleep_lengths));
+    clib_memset(fib_walk_hist_vists_per_walk, 0, sizeof(fib_walk_hist_vists_per_walk));
+    clib_memset(fib_walk_history, 0, sizeof(fib_walk_history));
+    clib_memset(fib_walk_work_time_taken, 0, sizeof(fib_walk_work_time_taken));
+    clib_memset(fib_walk_work_nodes_visited, 0, sizeof(fib_walk_work_nodes_visited));
+    clib_memset(fib_walk_sleep_lengths, 0, sizeof(fib_walk_sleep_lengths));
 
     return (NULL);
 }
@@ -1106,3 +1197,47 @@ VLIB_CLI_COMMAND (fib_walk_clear_command, static) = {
     .short_help = "clear fib walk",
     .function = fib_walk_clear,
 };
+
+void
+fib_walk_process_enable (void)
+{
+    vlib_process_signal_event(vlib_get_main(),
+                              fib_walk_process_node.index,
+                              FIB_WALK_PROCESS_EVENT_ENABLE,
+                              0);
+}
+
+void
+fib_walk_process_disable (void)
+{
+    vlib_process_signal_event(vlib_get_main(),
+                              fib_walk_process_node.index,
+                              FIB_WALK_PROCESS_EVENT_DISABLE,
+                              0);
+}
+
+static clib_error_t *
+fib_walk_process_enable_disable (vlib_main_t * vm,
+                                 unformat_input_t * input,
+                                 vlib_cli_command_t * cmd)
+{
+    if (unformat (input, "enable"))
+    {
+        fib_walk_process_enable();
+    }
+    else if (unformat (input, "disable"))
+    {
+        fib_walk_process_disable();
+    }
+    else
+    {
+        return clib_error_return(0, "choose enable or disable");
+    }
+    return (NULL);
+}
+
+VLIB_CLI_COMMAND (fib_walk_process_command, static) = {
+    .path = "test fib-walk-process",
+    .short_help = "test fib-walk-process [enable|disable]",
+    .function = fib_walk_process_enable_disable,
+};