#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
*/
*/
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
*/
} 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);
}
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);
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.
*/
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;
/*
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)
+ {
+ wrc = fib_node_back_walk_one(&sibling, &fwalk->fw_ctx[ii]);
+
+ ii++;
fwalk = fib_walk_get(fwi);
fwalk->fw_n_visits++;
*/
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
* @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,
};
*/
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
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.
*/
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);
+ }
}
/*
*/
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);
}
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);
}
/**
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)
{
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);
* 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;
}
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);
}
}
}
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,
{
if (0 != fib_walk_history[ii].fwh_reason[0])
{
- fib_node_back_walk_reason_t reason;
u8 *s = NULL;
u32 jj;
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);
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);
}
.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,
+};