blob: cfe45a0643ca9caee9d9094ce5806df8a2cfda96 [file] [log] [blame]
BenoƮt Ganne56eccdb2021-08-20 09:18:31 +02001/*
2 * SPDX-License-Identifier: Apache-2.0
3 * Copyright(c) 2021 Cisco Systems, Inc.
4 */
5
6/* Virtual time allows to adjust VPP clock by arbitrary amount of time.
7 * It is done such that the order of timer expirations is maintained,
8 * and if a timer expiration callback reschedule another timer, this
9 * timer will also properly expire in the right order. IOW, the order
10 * of events is preserved.
11 *
12 * When moving time forward, each VPP thread (main and workers) runs an
13 * instance of the input node 'virtual-time-input' below. This node is
14 * responsible of advancing its own VPP thread clock to the next timer
15 * expiration. IOW each thread will move its clock independently one
16 * timer at a time. This also means that while moving time forward, each
17 * thread might not have the exact same view of what 'now' means. Once
18 * the main thread has finished moving its time forward, the worker thread
19 * barrier will ensure the timer between main and workers is synchronized.
20 *
21 * Using an input node in poll-mode has several advantages, including
22 * preventing 'unix-epoll-input' to sleep (as it will not sleep if at
23 * least one polling node is active). */
24
25#include <vlib/vlib.h>
26#include <vlib/time.h>
27
28static f64 vlib_time_virtual_stop;
29
30static uword
31vlib_time_virtual_input (vlib_main_t *vm, vlib_node_runtime_t *node,
32 vlib_frame_t *frame)
33{
34 const f64 next = vlib_time_get_next_timer (vm);
35 /* each thread will advance its own time. In case a thread is much faster
36 * than another, we must make sure it does not run away... */
37 if (vlib_time_now (vm) + next > vlib_time_virtual_stop)
38 vlib_node_set_state (vm, node->node_index, VLIB_NODE_STATE_DISABLED);
39 else
40 vlib_time_adjust (vm, next);
41 return 0;
42}
43
44VLIB_REGISTER_NODE (vlib_time_virtual_input_node) = {
45 .function = vlib_time_virtual_input,
46 .type = VLIB_NODE_TYPE_INPUT,
47 .name = "virtual-time-input",
48 .state = VLIB_NODE_STATE_DISABLED,
49};
50
51static clib_error_t *
52vlib_time_virtual_adjust_command_fn (vlib_main_t *vm, unformat_input_t *input,
53 vlib_cli_command_t *cmd)
54{
55 f64 val;
56
57 if (!unformat (input, "%f", &val))
58 return clib_error_create ("unknown input `%U'", format_unformat_error,
59 input);
60
61 vlib_time_virtual_stop = vlib_time_now (vm) + val;
62
63 foreach_vlib_main ()
64 vlib_node_set_state (this_vlib_main, vlib_time_virtual_input_node.index,
65 VLIB_NODE_STATE_POLLING);
66
67 vlib_worker_thread_barrier_release (vm);
68 while ((val = vlib_process_wait_for_event_or_clock (vm, val)) >= 0.001)
69 ;
70 /* this barrier sync will resynchronize all the clocks, so even if the main
71 * thread was faster than some workers, this will make sure the workers will
72 * disable their virtual-time-input node on their next iteration (as stop
73 * time is reached). If a worker is too slow, there is a slight chance
74 * several of its timers expire at the same time at this point. Time will
75 * tell... */
76 vlib_worker_thread_barrier_sync (vm);
77 return 0;
78}
79
80VLIB_CLI_COMMAND (vlib_time_virtual_command) = {
81 .path = "set clock adjust",
82 .short_help = "set clock adjust <nn>",
83 .function = vlib_time_virtual_adjust_command_fn,
84};