
Type: feature Change-Id: Iabd76558e9c72ed8286cfeeb1fbaa4fde4832a90 Signed-off-by: Benoît Ganne <bganne@cisco.com>
85 lines
3.1 KiB
C
85 lines
3.1 KiB
C
/*
|
|
* 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,
|
|
};
|