BenoƮt Ganne | 56eccdb | 2021-08-20 09:18:31 +0200 | [diff] [blame] | 1 | /* |
| 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 | |
| 28 | static f64 vlib_time_virtual_stop; |
| 29 | |
| 30 | static uword |
| 31 | vlib_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 | |
| 44 | VLIB_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 | |
| 51 | static clib_error_t * |
| 52 | vlib_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 | |
| 80 | VLIB_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 | }; |