summaryrefslogtreecommitdiffstats
path: root/src/vlib/time.c
diff options
context:
space:
mode:
authorBenoît Ganne <bganne@cisco.com>2021-08-20 09:18:31 +0200
committerDamjan Marion <dmarion@me.com>2021-11-22 16:44:26 +0000
commit56eccdbaa982a6aff16fbc2a651fd024ecc589a8 (patch)
tree9ad2dfae55bf356d6c03b328744d690c64f8e0ef /src/vlib/time.c
parentf33979ba88111be3b7935ea90172422e4d4a114b (diff)
vlib: add virtual time support
Type: feature Change-Id: Iabd76558e9c72ed8286cfeeb1fbaa4fde4832a90 Signed-off-by: Benoît Ganne <bganne@cisco.com>
Diffstat (limited to 'src/vlib/time.c')
-rw-r--r--src/vlib/time.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/src/vlib/time.c b/src/vlib/time.c
new file mode 100644
index 00000000000..cfe45a0643c
--- /dev/null
+++ b/src/vlib/time.c
@@ -0,0 +1,84 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2021 Cisco Systems, Inc.
+ */
+
+/* Virtual time allows to adjust VPP clock by arbitrary amount of time.
+ * It is done such that the order of timer expirations is maintained,
+ * and if a timer expiration callback reschedule another timer, this
+ * timer will also properly expire in the right order. IOW, the order
+ * of events is preserved.
+ *
+ * When moving time forward, each VPP thread (main and workers) runs an
+ * instance of the input node 'virtual-time-input' below. This node is
+ * responsible of advancing its own VPP thread clock to the next timer
+ * expiration. IOW each thread will move its clock independently one
+ * timer at a time. This also means that while moving time forward, each
+ * thread might not have the exact same view of what 'now' means. Once
+ * the main thread has finished moving its time forward, the worker thread
+ * barrier will ensure the timer between main and workers is synchronized.
+ *
+ * Using an input node in poll-mode has several advantages, including
+ * preventing 'unix-epoll-input' to sleep (as it will not sleep if at
+ * least one polling node is active). */
+
+#include <vlib/vlib.h>
+#include <vlib/time.h>
+
+static f64 vlib_time_virtual_stop;
+
+static uword
+vlib_time_virtual_input (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vlib_frame_t *frame)
+{
+ const f64 next = vlib_time_get_next_timer (vm);
+ /* each thread will advance its own time. In case a thread is much faster
+ * than another, we must make sure it does not run away... */
+ if (vlib_time_now (vm) + next > vlib_time_virtual_stop)
+ vlib_node_set_state (vm, node->node_index, VLIB_NODE_STATE_DISABLED);
+ else
+ vlib_time_adjust (vm, next);
+ return 0;
+}
+
+VLIB_REGISTER_NODE (vlib_time_virtual_input_node) = {
+ .function = vlib_time_virtual_input,
+ .type = VLIB_NODE_TYPE_INPUT,
+ .name = "virtual-time-input",
+ .state = VLIB_NODE_STATE_DISABLED,
+};
+
+static clib_error_t *
+vlib_time_virtual_adjust_command_fn (vlib_main_t *vm, unformat_input_t *input,
+ vlib_cli_command_t *cmd)
+{
+ f64 val;
+
+ if (!unformat (input, "%f", &val))
+ return clib_error_create ("unknown input `%U'", format_unformat_error,
+ input);
+
+ vlib_time_virtual_stop = vlib_time_now (vm) + val;
+
+ foreach_vlib_main ()
+ vlib_node_set_state (this_vlib_main, vlib_time_virtual_input_node.index,
+ VLIB_NODE_STATE_POLLING);
+
+ vlib_worker_thread_barrier_release (vm);
+ while ((val = vlib_process_wait_for_event_or_clock (vm, val)) >= 0.001)
+ ;
+ /* this barrier sync will resynchronize all the clocks, so even if the main
+ * thread was faster than some workers, this will make sure the workers will
+ * disable their virtual-time-input node on their next iteration (as stop
+ * time is reached). If a worker is too slow, there is a slight chance
+ * several of its timers expire at the same time at this point. Time will
+ * tell... */
+ vlib_worker_thread_barrier_sync (vm);
+ return 0;
+}
+
+VLIB_CLI_COMMAND (vlib_time_virtual_command) = {
+ .path = "set clock adjust",
+ .short_help = "set clock adjust <nn>",
+ .function = vlib_time_virtual_adjust_command_fn,
+};