diff options
author | Benoît Ganne <bganne@cisco.com> | 2021-08-20 09:18:31 +0200 |
---|---|---|
committer | Damjan Marion <dmarion@me.com> | 2021-11-22 16:44:26 +0000 |
commit | 56eccdbaa982a6aff16fbc2a651fd024ecc589a8 (patch) | |
tree | 9ad2dfae55bf356d6c03b328744d690c64f8e0ef /src | |
parent | f33979ba88111be3b7935ea90172422e4d4a114b (diff) |
vlib: add virtual time support
Type: feature
Change-Id: Iabd76558e9c72ed8286cfeeb1fbaa4fde4832a90
Signed-off-by: Benoît Ganne <bganne@cisco.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/vlib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/vlib/threads.c | 24 | ||||
-rw-r--r-- | src/vlib/time.c | 84 | ||||
-rw-r--r-- | src/vlib/time.h | 26 |
4 files changed, 121 insertions, 15 deletions
diff --git a/src/vlib/CMakeLists.txt b/src/vlib/CMakeLists.txt index df51b2e99eb..a74c8cefb98 100644 --- a/src/vlib/CMakeLists.txt +++ b/src/vlib/CMakeLists.txt @@ -90,6 +90,7 @@ add_vpp_library(vlib punt_node.c threads.c threads_cli.c + time.c trace.c unix/cli.c unix/input.c @@ -130,6 +131,7 @@ add_vpp_library(vlib physmem.h punt.h threads.h + time.h trace_funcs.h trace.h unix/mc_socket.h diff --git a/src/vlib/threads.c b/src/vlib/threads.c index ad677dc32b9..f45e9358a89 100644 --- a/src/vlib/threads.c +++ b/src/vlib/threads.c @@ -1637,7 +1637,6 @@ static clib_error_t * show_clock_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { - int i; int verbose = 0; clib_timebase_t _tb, *tb = &_tb; @@ -1650,24 +1649,19 @@ show_clock_command_fn (vlib_main_t * vm, verbose, format_clib_timebase_time, clib_timebase_now (tb)); - if (vlib_get_n_threads () == 1) - return 0; - vlib_cli_output (vm, "Time last barrier release %.9f", vm->time_last_barrier_release); - for (i = 1; i < vlib_get_n_threads (); i++) + foreach_vlib_main () { - vlib_main_t *ovm = vlib_get_main_by_index (i); - if (ovm == 0) - continue; - - vlib_cli_output (vm, "%d: %U", i, format_clib_time, &ovm->clib_time, - verbose); - - vlib_cli_output ( - vm, "Thread %d offset %.9f error %.9f", i, ovm->time_offset, - vm->time_last_barrier_release - ovm->time_last_barrier_release); + vlib_cli_output (vm, "%d: %U", this_vlib_main->thread_index, + format_clib_time, &this_vlib_main->clib_time, verbose); + + vlib_cli_output (vm, "Thread %d offset %.9f error %.9f", + this_vlib_main->thread_index, + this_vlib_main->time_offset, + vm->time_last_barrier_release - + this_vlib_main->time_last_barrier_release); } return 0; } 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, +}; diff --git a/src/vlib/time.h b/src/vlib/time.h new file mode 100644 index 00000000000..61873bb2ef3 --- /dev/null +++ b/src/vlib/time.h @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2021 Cisco Systems, Inc. + */ + +#ifndef included_vlib_time_h +#define included_vlib_time_h + +#include <vlib/vlib.h> +#include <vppinfra/tw_timer_1t_3w_1024sl_ov.h> + +static inline f64 +vlib_time_get_next_timer (vlib_main_t *vm) +{ + vlib_node_main_t *nm = &vm->node_main; + TWT (tw_timer_wheel) *wheel = nm->timing_wheel; + return TW (tw_timer_first_expires_in_ticks) (wheel) * wheel->timer_interval; +} + +static inline void +vlib_time_adjust (vlib_main_t *vm, f64 offset) +{ + vm->time_offset += offset; +} + +#endif /* included_vlib_time_h */ |