Line data Source code
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 31037 : vlib_time_virtual_input (vlib_main_t *vm, vlib_node_runtime_t *node, 32 : vlib_frame_t *frame) 33 : { 34 31037 : 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 31040 : if (vlib_time_now (vm) + next > vlib_time_virtual_stop) 38 125 : vlib_node_set_state (vm, node->node_index, VLIB_NODE_STATE_DISABLED); 39 : else 40 30920 : vlib_time_adjust (vm, next); 41 31045 : return 0; 42 : } 43 : 44 178120 : 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 82 : 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 82 : if (!unformat (input, "%f", &val)) 58 0 : return clib_error_create ("unknown input `%U'", format_unformat_error, 59 : input); 60 : 61 82 : vlib_time_virtual_stop = vlib_time_now (vm) + val; 62 : 63 222 : foreach_vlib_main () 64 140 : vlib_node_set_state (this_vlib_main, vlib_time_virtual_input_node.index, 65 : VLIB_NODE_STATE_POLLING); 66 : 67 82 : vlib_worker_thread_barrier_release (vm); 68 82 : 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 82 : vlib_worker_thread_barrier_sync (vm); 77 82 : return 0; 78 : } 79 : 80 272887 : 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 : };