1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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,
};
|