From 8973b07eecbbfe23a5267975f8052a5c6339c756 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Tue, 1 Mar 2022 15:51:18 +0100 Subject: stats: refactor Type: refactor Change-Id: Ifd533a095d979dc55bfbe5fac7e0b7510a4d900c Signed-off-by: Damjan Marion --- src/plugins/lacp/input.c | 14 +- src/plugins/lacp/lacp.c | 26 +- src/plugins/mactime/mactime_top.c | 2 +- src/plugins/nat/nat44-ed/nat44_ed.c | 14 +- src/plugins/nat/pnat/tests/pnat_test_stubs.h | 6 - src/plugins/prom/prom.c | 4 +- src/plugins/unittest/counter_test.c | 6 +- src/vlib/CMakeLists.txt | 8 + src/vlib/buffer.c | 59 +- src/vlib/cli.c | 14 +- src/vlib/counter.c | 25 +- src/vlib/error.c | 18 +- src/vlib/main.c | 4 +- src/vlib/node_cli.c | 28 +- src/vlib/stat_weak_inlines.h | 72 -- src/vlib/stats/cli.c | 125 +++ src/vlib/stats/collector.c | 192 +++++ src/vlib/stats/format.c | 24 + src/vlib/stats/init.c | 246 ++++++ src/vlib/stats/provider_mem.c | 68 ++ src/vlib/stats/shared.h | 52 ++ src/vlib/stats/stats.c | 514 ++++++++++++ src/vlib/stats/stats.h | 160 ++++ src/vlib/threads.c | 26 +- src/vnet/CMakeLists.txt | 1 + src/vnet/bfd/bfd_udp.c | 17 +- src/vnet/bonding/cli.c | 39 +- src/vnet/devices/devices.c | 25 + src/vnet/interface/stats.c | 95 +++ src/vpp-api/client/stat_client.c | 23 +- src/vpp-api/client/stat_client.h | 16 +- src/vpp/CMakeLists.txt | 7 - src/vpp/stats/stat_segment.c | 1149 -------------------------- src/vpp/stats/stat_segment.h | 123 --- src/vpp/stats/stat_segment_provider.c | 198 ----- src/vpp/stats/stat_segment_shared.h | 66 -- 36 files changed, 1666 insertions(+), 1800 deletions(-) delete mode 100644 src/vlib/stat_weak_inlines.h create mode 100644 src/vlib/stats/cli.c create mode 100644 src/vlib/stats/collector.c create mode 100644 src/vlib/stats/format.c create mode 100644 src/vlib/stats/init.c create mode 100644 src/vlib/stats/provider_mem.c create mode 100644 src/vlib/stats/shared.h create mode 100644 src/vlib/stats/stats.c create mode 100644 src/vlib/stats/stats.h create mode 100644 src/vnet/interface/stats.c delete mode 100644 src/vpp/stats/stat_segment.c delete mode 100644 src/vpp/stats/stat_segment.h delete mode 100644 src/vpp/stats/stat_segment_provider.c delete mode 100644 src/vpp/stats/stat_segment_shared.h diff --git a/src/plugins/lacp/input.c b/src/plugins/lacp/input.c index ebca2ad9185..3997af897eb 100644 --- a/src/plugins/lacp/input.c +++ b/src/plugins/lacp/input.c @@ -16,7 +16,7 @@ #define _GNU_SOURCE #include #include -#include +#include static int lacp_packet_scan (vlib_main_t * vm, member_if_t * mif) @@ -213,12 +213,12 @@ lacp_input (vlib_main_t * vm, vlib_buffer_t * b0, u32 bi0) /* Actually scan the packet */ e = lacp_packet_scan (vm, mif); bif = bond_get_bond_if_by_dev_instance (mif->bif_dev_instance); - stat_segment_set_state_counter (bm->stats[bif->sw_if_index] - [mif->sw_if_index].actor_state, - mif->actor.state); - stat_segment_set_state_counter (bm->stats[bif->sw_if_index] - [mif->sw_if_index].partner_state, - mif->partner.state); + vlib_stats_set_gauge ( + bm->stats[bif->sw_if_index][mif->sw_if_index].actor_state, + mif->actor.state); + vlib_stats_set_gauge ( + bm->stats[bif->sw_if_index][mif->sw_if_index].partner_state, + mif->partner.state); mif->last_packet_signature_valid = 1; mif->last_packet_signature = last_packet_signature; } diff --git a/src/plugins/lacp/lacp.c b/src/plugins/lacp/lacp.c index 44a32aa23a9..ad7710266f9 100644 --- a/src/plugins/lacp/lacp.c +++ b/src/plugins/lacp/lacp.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include lacp_main_t lacp_main; @@ -173,16 +173,16 @@ lacp_periodic (vlib_main_t * vm) if (actor_state != mif->actor.state) { bif = bond_get_bond_if_by_dev_instance (mif->bif_dev_instance); - stat_segment_set_state_counter (bm->stats[bif->sw_if_index] - [mif->sw_if_index].actor_state, - mif->actor.state); + vlib_stats_set_gauge ( + bm->stats[bif->sw_if_index][mif->sw_if_index].actor_state, + mif->actor.state); } if (partner_state != mif->partner.state) { bif = bond_get_bond_if_by_dev_instance (mif->bif_dev_instance); - stat_segment_set_state_counter (bm->stats[bif->sw_if_index] - [mif->sw_if_index].partner_state, - mif->partner.state); + vlib_stats_set_gauge ( + bm->stats[bif->sw_if_index][mif->sw_if_index].partner_state, + mif->partner.state); } } /* *INDENT-ON* */ @@ -380,12 +380,12 @@ lacp_init_state_machines (vlib_main_t * vm, member_if_t * mif) lacp_init_mux_machine (vm, mif); lacp_init_ptx_machine (vm, mif); lacp_init_rx_machine (vm, mif); - stat_segment_set_state_counter (bm->stats[bif->sw_if_index] - [mif->sw_if_index].actor_state, - mif->actor.state); - stat_segment_set_state_counter (bm->stats[bif->sw_if_index] - [mif->sw_if_index].partner_state, - mif->partner.state); + vlib_stats_set_gauge ( + bm->stats[bif->sw_if_index][mif->sw_if_index].actor_state, + mif->actor.state); + vlib_stats_set_gauge ( + bm->stats[bif->sw_if_index][mif->sw_if_index].partner_state, + mif->partner.state); } VLIB_INIT_FUNCTION (lacp_periodic_init); diff --git a/src/plugins/mactime/mactime_top.c b/src/plugins/mactime/mactime_top.c index ed4c7345721..abcb530608c 100644 --- a/src/plugins/mactime/mactime_top.c +++ b/src/plugins/mactime/mactime_top.c @@ -187,7 +187,7 @@ scrape_stats_segment (mt_main_t * mm) mactime_device_t *dev; stat_segment_access_t sa; stat_client_main_t *sm = mm->stat_client_main; - stat_segment_directory_entry_t *ep; + vlib_stats_entry_t *ep; int need_update2 = 0; static u32 *pool_indices; int i, j; diff --git a/src/plugins/nat/nat44-ed/nat44_ed.c b/src/plugins/nat/nat44-ed/nat44_ed.c index c29ee54a6c4..b67a247358a 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed.c +++ b/src/plugins/nat/nat44-ed/nat44_ed.c @@ -38,7 +38,7 @@ #include #include -#include +#include snat_main_t snat_main; @@ -2290,8 +2290,8 @@ nat_init (vlib_main_t * vm) nat_init_simple_counter (sm->total_sessions, "total-sessions", "/nat44-ed/total-sessions"); - sm->max_cfg_sessions_gauge = stat_segment_new_entry ( - (u8 *) "/nat44-ed/max-cfg-sessions", STAT_DIR_TYPE_SCALAR_INDEX); + sm->max_cfg_sessions_gauge = + vlib_stats_add_gauge ("/nat44-ed/max-cfg-sessions"); #define _(x) \ nat_init_simple_counter (sm->counters.fastpath.in2out.x, #x, \ @@ -2373,8 +2373,8 @@ nat44_plugin_enable (nat44_config_t c) c.sessions = 63 * 1024; sm->max_translations_per_thread = c.sessions; - stat_segment_set_state_counter (sm->max_cfg_sessions_gauge, - sm->max_translations_per_thread); + vlib_stats_set_gauge (sm->max_cfg_sessions_gauge, + sm->max_translations_per_thread); sm->translation_buckets = nat_calc_bihash_buckets (c.sessions); vec_add1 (sm->max_translations_per_fib, sm->max_translations_per_thread); @@ -3145,8 +3145,8 @@ nat44_update_session_limit (u32 session_limit, u32 vrf_id) return 1; sm->max_translations_per_thread = nat44_get_max_session_limit (); - stat_segment_set_state_counter (sm->max_cfg_sessions_gauge, - sm->max_translations_per_thread); + vlib_stats_set_gauge (sm->max_cfg_sessions_gauge, + sm->max_translations_per_thread); sm->translation_buckets = nat_calc_bihash_buckets (sm->max_translations_per_thread); diff --git a/src/plugins/nat/pnat/tests/pnat_test_stubs.h b/src/plugins/nat/pnat/tests/pnat_test_stubs.h index bfe1838ffa4..2dc59ac8586 100644 --- a/src/plugins/nat/pnat/tests/pnat_test_stubs.h +++ b/src/plugins/nat/pnat/tests/pnat_test_stubs.h @@ -19,12 +19,6 @@ void os_panic(void) {} void os_exit(int code) {} u32 ip4_fib_table_get_index_for_sw_if_index(u32 sw_if_index) { return 0; } -#include -clib_error_t *stat_segment_register_gauge(u8 *names, - stat_segment_update_fn update_fn, - u32 index) { - return 0; -}; #include vnet_feature_main_t feature_main; void classify_get_trace_chain(void){}; diff --git a/src/plugins/prom/prom.c b/src/plugins/prom/prom.c index 3fb3704a9a0..0b03a7adb0b 100644 --- a/src/plugins/prom/prom.c +++ b/src/plugins/prom/prom.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include static prom_main_t prom_main; @@ -388,7 +388,7 @@ static void prom_stat_segment_client_init (void) { stat_client_main_t *scm = &stat_client_main; - stat_segment_main_t *sm = &stat_segment_main; + vlib_stats_segment_t *sm = vlib_stats_get_segment (); uword size; size = sm->memory_size ? sm->memory_size : STAT_SEGMENT_DEFAULT_SIZE; diff --git a/src/plugins/unittest/counter_test.c b/src/plugins/unittest/counter_test.c index 24b9e1e386e..e7090f0a860 100644 --- a/src/plugins/unittest/counter_test.c +++ b/src/plugins/unittest/counter_test.c @@ -19,7 +19,7 @@ #include #include -#include +#include enum { @@ -38,7 +38,7 @@ enum static uint64_t get_stats_epoch () { - stat_segment_main_t *sm = &stat_segment_main; + vlib_stats_segment_t *sm = vlib_stats_get_segment (); return sm->shared_header->epoch; } @@ -49,7 +49,7 @@ get_stats_epoch () static int get_vec_mem_size (void *v, uword data_size) { - stat_segment_main_t *sm = &stat_segment_main; + vlib_stats_segment_t *sm = vlib_stats_get_segment (); if (v == 0) return 0; diff --git a/src/vlib/CMakeLists.txt b/src/vlib/CMakeLists.txt index a74c8cefb98..bb43abdc63e 100644 --- a/src/vlib/CMakeLists.txt +++ b/src/vlib/CMakeLists.txt @@ -88,6 +88,12 @@ add_vpp_library(vlib physmem.c punt.c punt_node.c + stats/cli.c + stats/collector.c + stats/format.c + stats/init.c + stats/provider_mem.c + stats/stats.c threads.c threads_cli.c time.c @@ -130,6 +136,8 @@ add_vpp_library(vlib physmem_funcs.h physmem.h punt.h + stats/shared.h + stats/stats.h threads.h time.h trace_funcs.h diff --git a/src/vlib/buffer.c b/src/vlib/buffer.c index 7a70a8abdd3..fbded6e8300 100644 --- a/src/vlib/buffer.c +++ b/src/vlib/buffer.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #define VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA 16384 #define VLIB_BUFFER_DEFAULT_BUFFERS_PER_NUMA_UNPRIV 8192 @@ -63,17 +63,6 @@ STATIC_ASSERT_OFFSET_OF (vlib_buffer_t, template_end, 64); u16 __vlib_buffer_external_hdr_size = 0; -static void -buffer_gauges_update_cached_fn (stat_segment_directory_entry_t * e, - u32 index); - -static void -buffer_gauges_update_available_fn (stat_segment_directory_entry_t * e, - u32 index); - -static void -buffer_gauges_update_used_fn (stat_segment_directory_entry_t * e, u32 index); - uword vlib_buffer_length_in_chain_slow_path (vlib_main_t * vm, vlib_buffer_t * b_first) @@ -798,37 +787,39 @@ buffer_get_by_index (vlib_buffer_main_t * bm, u32 index) } static void -buffer_gauges_update_used_fn (stat_segment_directory_entry_t * e, u32 index) +buffer_gauges_collect_used_fn (vlib_stats_collector_data_t *d) { vlib_main_t *vm = vlib_get_main (); - vlib_buffer_pool_t *bp = buffer_get_by_index (vm->buffer_main, index); + vlib_buffer_pool_t *bp = + buffer_get_by_index (vm->buffer_main, d->private_data); if (!bp) return; - e->value = bp->n_buffers - bp->n_avail - buffer_get_cached (bp); + d->entry->value = bp->n_buffers - bp->n_avail - buffer_get_cached (bp); } static void -buffer_gauges_update_available_fn (stat_segment_directory_entry_t * e, - u32 index) +buffer_gauges_collect_available_fn (vlib_stats_collector_data_t *d) { vlib_main_t *vm = vlib_get_main (); - vlib_buffer_pool_t *bp = buffer_get_by_index (vm->buffer_main, index); + vlib_buffer_pool_t *bp = + buffer_get_by_index (vm->buffer_main, d->private_data); if (!bp) return; - e->value = bp->n_avail; + d->entry->value = bp->n_avail; } static void -buffer_gauges_update_cached_fn (stat_segment_directory_entry_t * e, u32 index) +buffer_gauges_collect_cached_fn (vlib_stats_collector_data_t *d) { vlib_main_t *vm = vlib_get_main (); - vlib_buffer_pool_t *bp = buffer_get_by_index (vm->buffer_main, index); + vlib_buffer_pool_t *bp = + buffer_get_by_index (vm->buffer_main, d->private_data); if (!bp) return; - e->value = buffer_get_cached (bp); + d->entry->value = buffer_get_cached (bp); } clib_error_t * @@ -902,23 +893,23 @@ vlib_buffer_main_init (struct vlib_main_t * vm) vec_foreach (bp, bm->buffer_pools) { + vlib_stats_collector_reg_t reg = { .private_data = bp - bm->buffer_pools }; if (bp->n_buffers == 0) continue; - vec_reset_length (name); - name = format (name, "/buffer-pools/%s/cached%c", bp->name, 0); - stat_segment_register_gauge (name, buffer_gauges_update_cached_fn, - bp - bm->buffer_pools); + reg.entry_index = + vlib_stats_add_gauge ("/buffer-pools/%s/cached", bp->name); + reg.collect_fn = buffer_gauges_collect_cached_fn; + vlib_stats_register_collector_fn (®); - vec_reset_length (name); - name = format (name, "/buffer-pools/%s/used%c", bp->name, 0); - stat_segment_register_gauge (name, buffer_gauges_update_used_fn, - bp - bm->buffer_pools); + reg.entry_index = vlib_stats_add_gauge ("/buffer-pools/%s/used", bp->name); + reg.collect_fn = buffer_gauges_collect_used_fn; + vlib_stats_register_collector_fn (®); - vec_reset_length (name); - name = format (name, "/buffer-pools/%s/available%c", bp->name, 0); - stat_segment_register_gauge (name, buffer_gauges_update_available_fn, - bp - bm->buffer_pools); + reg.entry_index = + vlib_stats_add_gauge ("/buffer-pools/%s/available", bp->name); + reg.collect_fn = buffer_gauges_collect_available_fn; + vlib_stats_register_collector_fn (®); } done: diff --git a/src/vlib/cli.c b/src/vlib/cli.c index a00ae6245f9..2187d2886fc 100644 --- a/src/vlib/cli.c +++ b/src/vlib/cli.c @@ -38,6 +38,7 @@ */ #include +#include #include #include #include @@ -753,13 +754,6 @@ vl_msg_pop_heap (void *oldheap) { } -void *vlib_stats_push_heap (void *) __attribute__ ((weak)); -void * -vlib_stats_push_heap (void *notused) -{ - return 0; -} - static clib_error_t * show_memory_usage (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -820,14 +814,14 @@ show_memory_usage (vlib_main_t * vm, } if (stats_segment) { - void *oldheap = vlib_stats_push_heap (0); + void *oldheap = vlib_stats_set_heap (0); was_enabled = clib_mem_trace_enable_disable (0); u8 *s_in_svm = format (0, "%U\n", format_clib_mem_heap, 0, 1); if (oldheap) clib_mem_set_heap (oldheap); u8 *s = vec_dup (s_in_svm); - oldheap = vlib_stats_push_heap (0); + oldheap = vlib_stats_set_heap (0); vec_free (s_in_svm); if (oldheap) { @@ -1038,7 +1032,7 @@ enable_disable_memory_trace (vlib_main_t * vm, /* Stats segment */ if (stats_segment) { - oldheap = vlib_stats_push_heap (0); + oldheap = vlib_stats_set_heap (); current_traced_heap = clib_mem_get_heap (); clib_mem_trace (stats_segment); /* We don't want to call vlib_stats_pop_heap... */ diff --git a/src/vlib/counter.c b/src/vlib/counter.c index 186b48d869e..ceaf0132e64 100644 --- a/src/vlib/counter.c +++ b/src/vlib/counter.c @@ -38,7 +38,7 @@ */ #include -#include +#include void vlib_clear_simple_counters (vlib_simple_counter_main_t * cm) @@ -80,7 +80,7 @@ vlib_validate_simple_counter (vlib_simple_counter_main_t * cm, u32 index) { vlib_thread_main_t *tm = vlib_get_thread_main (); int i, resized = 0; - void *oldheap = vlib_stats_push_heap (cm->counters); + void *oldheap = vlib_stats_set_heap (); vec_validate (cm->counters, tm->n_vlib_mains - 1); for (i = 0; i < tm->n_vlib_mains; i++) @@ -93,12 +93,10 @@ vlib_validate_simple_counter (vlib_simple_counter_main_t * cm, u32 index) vec_validate_aligned (cm->counters[i], index, CLIB_CACHE_LINE_BYTES); } + clib_mem_set_heap (oldheap); /* Avoid the epoch increase when there was no counter vector resize. */ if (resized) - vlib_stats_pop_heap (cm, oldheap, index, - 2 /* STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE */); - else - clib_mem_set_heap (oldheap); + vlib_stats_update_counter (cm, index, STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE); } void @@ -108,7 +106,7 @@ vlib_free_simple_counter (vlib_simple_counter_main_t * cm) vlib_stats_delete_cm (cm); - void *oldheap = vlib_stats_push_heap (cm->counters); + void *oldheap = vlib_stats_set_heap (); for (i = 0; i < vec_len (cm->counters); i++) vec_free (cm->counters[i]); vec_free (cm->counters); @@ -120,7 +118,7 @@ vlib_validate_combined_counter (vlib_combined_counter_main_t * cm, u32 index) { vlib_thread_main_t *tm = vlib_get_thread_main (); int i, resized = 0; - void *oldheap = vlib_stats_push_heap (cm->counters); + void *oldheap = vlib_stats_set_heap (); vec_validate (cm->counters, tm->n_vlib_mains - 1); for (i = 0; i < tm->n_vlib_mains; i++) @@ -133,12 +131,11 @@ vlib_validate_combined_counter (vlib_combined_counter_main_t * cm, u32 index) vec_validate_aligned (cm->counters[i], index, CLIB_CACHE_LINE_BYTES); } + clib_mem_set_heap (oldheap); /* Avoid the epoch increase when there was no counter vector resize. */ if (resized) - vlib_stats_pop_heap (cm, oldheap, index, - 3 /*STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED */); - else - clib_mem_set_heap (oldheap); + vlib_stats_update_counter (cm, index, + STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED); } int @@ -147,7 +144,7 @@ int { vlib_thread_main_t *tm = vlib_get_thread_main (); int i; - void *oldheap = vlib_stats_push_heap (cm->counters); + void *oldheap = vlib_stats_set_heap (); /* Possibly once in recorded history */ if (PREDICT_FALSE (vec_len (cm->counters) == 0)) @@ -180,7 +177,7 @@ vlib_free_combined_counter (vlib_combined_counter_main_t * cm) vlib_stats_delete_cm (cm); - void *oldheap = vlib_stats_push_heap (cm->counters); + void *oldheap = vlib_stats_set_heap (); for (i = 0; i < vec_len (cm->counters); i++) vec_free (cm->counters[i]); vec_free (cm->counters); diff --git a/src/vlib/error.c b/src/vlib/error.c index 9a9ca4a535d..e513a7d673b 100644 --- a/src/vlib/error.c +++ b/src/vlib/error.c @@ -39,7 +39,7 @@ #include #include -#include +#include uword vlib_error_drop_buffers (vlib_main_t * vm, @@ -158,7 +158,7 @@ vlib_register_errors (vlib_main_t *vm, u32 node_index, u32 n_errors, vec_validate (vm->error_elog_event_types, l - 1); /* Switch to the stats segment ... */ - oldheap = vlib_stats_push_heap (0); + oldheap = vlib_stats_set_heap (); /* Allocate a counter/elog type for each error. */ vec_validate (em->counters, l - 1); @@ -172,25 +172,23 @@ vlib_register_errors (vlib_main_t *vm, u32 node_index, u32 n_errors, clib_memset (em->counters + n->error_heap_index, 0, n_errors * sizeof (em->counters[0])); + oldheap = clib_mem_set_heap (oldheap); + /* Register counter indices in the stat segment directory */ { int i; - u8 *error_name = 0; for (i = 0; i < n_errors; i++) { - vec_reset_length (error_name); - error_name = - format (error_name, "/err/%v/%s%c", n->name, counters[i].name, 0); - vlib_stats_register_error_index (oldheap, error_name, em->counters, - n->error_heap_index + i); + vlib_stats_register_error_index (em->counters, n->error_heap_index + i, + "/err/%v/%s", n->name, + counters[i].name); } - vec_free (error_name); } /* (re)register the em->counters base address, switch back to main heap */ - vlib_stats_pop_heap2 (em->counters, vm->thread_index, oldheap, 1); + vlib_stats_update_error_vector (em->counters, vm->thread_index, 1); { elog_event_type_t t; diff --git a/src/vlib/main.c b/src/vlib/main.c index 189884a97f9..6d241bd18bb 100644 --- a/src/vlib/main.c +++ b/src/vlib/main.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -1826,7 +1827,6 @@ placeholder_queue_signal_callback (vlib_main_t * vm) } #define foreach_weak_reference_stub \ -_(vlib_map_stat_segment_init) \ _(vpe_api_init) \ _(vlibmemory_init) \ _(map_api_segment_init) @@ -1887,7 +1887,7 @@ vlib_main (vlib_main_t * volatile vm, unformat_input_t * input) goto done; } - if ((error = vlib_map_stat_segment_init (vm))) + if ((error = vlib_stats_init (vm))) { clib_error_report (error); goto done; diff --git a/src/vlib/node_cli.c b/src/vlib/node_cli.c index 77fef775a2f..075430e4761 100644 --- a/src/vlib/node_cli.c +++ b/src/vlib/node_cli.c @@ -42,6 +42,7 @@ #include #include #include +#include #include static int @@ -465,13 +466,6 @@ format_vlib_node_stats (u8 * s, va_list * va) return s; } -f64 vlib_get_stat_segment_update_rate (void) __attribute__ ((weak)); -f64 -vlib_get_stat_segment_update_rate (void) -{ - return 1e70; -} - static clib_error_t * show_node_runtime (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -598,16 +592,14 @@ show_node_runtime (vlib_main_t * vm, } dt = time_now - nm->time_last_runtime_stats_clear; - vlib_cli_output - (vm, - "Time %.1f, %f sec internal node vector rate %.2f loops/sec %.2f\n" - " vector rates in %.4e, out %.4e, drop %.4e, punt %.4e", - dt, - vlib_get_stat_segment_update_rate (), - internal_node_vector_rates[j], - stat_vm->loops_per_second, - (f64) n_input / dt, - (f64) n_output / dt, (f64) n_drop / dt, (f64) n_punt / dt); + vlib_cli_output ( + vm, + "Time %.1f, %f sec internal node vector rate %.2f loops/sec %.2f\n" + " vector rates in %.4e, out %.4e, drop %.4e, punt %.4e", + dt, vlib_stats_get_segment_update_rate (), + internal_node_vector_rates[j], stat_vm->loops_per_second, + (f64) n_input / dt, (f64) n_output / dt, (f64) n_drop / dt, + (f64) n_punt / dt); if (summary == 0) { @@ -684,6 +676,8 @@ clear_node_runtime (vlib_main_t * vm, nm->time_last_runtime_stats_clear = vlib_time_now (vm); } + vlib_stats_set_timestamp (STAT_COUNTER_LAST_STATS_CLEAR, + vm->node_main.time_last_runtime_stats_clear); vlib_worker_thread_barrier_release (vm); vec_free (stat_vms); diff --git a/src/vlib/stat_weak_inlines.h b/src/vlib/stat_weak_inlines.h deleted file mode 100644 index a68566d0fdd..00000000000 --- a/src/vlib/stat_weak_inlines.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * NOTE: Only include this file from external components that require - * a loose coupling to the stats component. - */ - -#ifndef included_stat_weak_inlines_h -#define included_stat_weak_inlines_h -void *vlib_stats_push_heap (void *) __attribute__ ((weak)); -void * -vlib_stats_push_heap (void *unused) -{ - return 0; -}; - -void vlib_stats_pop_heap (void *, void *, u32, int) __attribute__ ((weak)); -void -vlib_stats_pop_heap (void *notused, void *notused2, u32 i, int type) -{ -}; -void vlib_stats_register_error_index (void *, u8 *, u64 *, u64) - __attribute__ ((weak)); -void -vlib_stats_register_error_index (void * notused, u8 * notused2, u64 * notused3, u64 notused4) -{ -}; - -void vlib_stats_pop_heap2 (void *, u32, void *, int) __attribute__ ((weak)); -void -vlib_stats_pop_heap2 (void *notused, u32 notused2, void *notused3, - int notused4) -{ -}; - -void vlib_stat_segment_lock (void) __attribute__ ((weak)); -void -vlib_stat_segment_lock (void) -{ -} - -void vlib_stat_segment_unlock (void) __attribute__ ((weak)); -void -vlib_stat_segment_unlock (void) -{ -} -void vlib_stats_delete_cm (void *) __attribute__ ((weak)); -void -vlib_stats_delete_cm (void *notused) -{ -} - -void vlib_stats_register_mem_heap (void *) __attribute__ ((weak)); -void -vlib_stats_register_mem_heap (void *notused) -{ -} - -#endif diff --git a/src/vlib/stats/cli.c b/src/vlib/stats/cli.c new file mode 100644 index 00000000000..4d7026b5995 --- /dev/null +++ b/src/vlib/stats/cli.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include +#include + +static int +name_sort_cmp (void *a1, void *a2) +{ + vlib_stats_entry_t *n1 = a1; + vlib_stats_entry_t *n2 = a2; + + return strcmp ((char *) n1->name, (char *) n2->name); +} + +static u8 * +format_stat_dir_entry (u8 *s, va_list *args) +{ + vlib_stats_entry_t *ep = va_arg (*args, vlib_stats_entry_t *); + char *type_name; + char *format_string; + + format_string = "%-74s %-10s %10lld"; + + switch (ep->type) + { + case STAT_DIR_TYPE_SCALAR_INDEX: + type_name = "ScalarPtr"; + break; + + case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: + case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: + type_name = "CMainPtr"; + break; + + case STAT_DIR_TYPE_ERROR_INDEX: + type_name = "ErrIndex"; + break; + + case STAT_DIR_TYPE_NAME_VECTOR: + type_name = "NameVector"; + break; + + case STAT_DIR_TYPE_EMPTY: + type_name = "empty"; + break; + + case STAT_DIR_TYPE_SYMLINK: + type_name = "Symlink"; + break; + + default: + type_name = "illegal!"; + break; + } + + return format (s, format_string, ep->name, type_name, 0); +} +static clib_error_t * +show_stat_segment_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_entry_t *show_data; + int i; + + int verbose = 0; + + if (unformat (input, "verbose")) + verbose = 1; + + /* Lock even as reader, as this command doesn't handle epoch changes */ + vlib_stats_segment_lock (); + show_data = vec_dup (sm->directory_vector); + vlib_stats_segment_unlock (); + + vec_sort_with_function (show_data, name_sort_cmp); + + vlib_cli_output (vm, "%-74s %10s %10s", "Name", "Type", "Value"); + + for (i = 0; i < vec_len (show_data); i++) + { + vlib_stats_entry_t *ep = vec_elt_at_index (show_data, i); + + if (ep->type == STAT_DIR_TYPE_EMPTY) + continue; + + vlib_cli_output (vm, "%-100U", format_stat_dir_entry, + vec_elt_at_index (show_data, i)); + } + + if (verbose) + { + ASSERT (sm->heap); + vlib_cli_output (vm, "%U", format_clib_mem_heap, sm->heap, + 0 /* verbose */); + } + + return 0; +} + +static clib_error_t * +show_stat_segment_hash_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + char *name; + u32 i; + hash_foreach_mem (name, i, sm->directory_vector_by_name, + ({ vlib_cli_output (vm, "%d: %s\n", i, name); })); + return 0; +} + +VLIB_CLI_COMMAND (show_stat_segment_hash_command, static) = { + .path = "show statistics hash", + .short_help = "show statistics hash", + .function = show_stat_segment_hash_command_fn, +}; + +VLIB_CLI_COMMAND (show_stat_segment_command, static) = { + .path = "show statistics segment", + .short_help = "show statistics segment [verbose]", + .function = show_stat_segment_command_fn, +}; diff --git a/src/vlib/stats/collector.c b/src/vlib/stats/collector.c new file mode 100644 index 00000000000..cc348bbf3ad --- /dev/null +++ b/src/vlib/stats/collector.c @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include +#include +#include + +static void +stat_validate_counter_vector2 (vlib_stats_entry_t *ep, u32 max1, u32 max2) +{ + counter_t **counters = ep->data; + int i; + vec_validate_aligned (counters, max1, CLIB_CACHE_LINE_BYTES); + for (i = 0; i <= max1; i++) + vec_validate_aligned (counters[i], max2, CLIB_CACHE_LINE_BYTES); + + ep->data = counters; +} + +static void +stat_validate_counter_vector (vlib_stats_entry_t *ep, u32 max) +{ + vlib_thread_main_t *tm = vlib_get_thread_main (); + ASSERT (tm->n_vlib_mains > 0); + stat_validate_counter_vector2 (ep, tm->n_vlib_mains, max); +} + +static inline void +update_node_counters (vlib_stats_segment_t *sm) +{ + vlib_main_t **stat_vms = 0; + vlib_node_t ***node_dups = 0; + int i, j; + static u32 no_max_nodes = 0; + + vlib_node_get_nodes (0 /* vm, for barrier sync */, + (u32) ~0 /* all threads */, 1 /* include stats */, + 0 /* barrier sync */, &node_dups, &stat_vms); + + u32 l = vec_len (node_dups[0]); + u8 *symlink_name = 0; + + /* + * Extend performance nodes if necessary + */ + if (l > no_max_nodes) + { + void *oldheap = clib_mem_set_heap (sm->heap); + vlib_stats_segment_lock (); + + stat_validate_counter_vector ( + &sm->directory_vector[STAT_COUNTER_NODE_CLOCKS], l - 1); + stat_validate_counter_vector ( + &sm->directory_vector[STAT_COUNTER_NODE_VECTORS], l - 1); + stat_validate_counter_vector ( + &sm->directory_vector[STAT_COUNTER_NODE_CALLS], l - 1); + stat_validate_counter_vector ( + &sm->directory_vector[STAT_COUNTER_NODE_SUSPENDS], l - 1); + + vec_validate (sm->nodes, l - 1); + vlib_stats_entry_t *ep; + ep = &sm->directory_vector[STAT_COUNTER_NODE_NAMES]; + ep->data = sm->nodes; + + /* Update names dictionary */ + vlib_node_t **nodes = node_dups[0]; + int i; + for (i = 0; i < vec_len (nodes); i++) + { + vlib_node_t *n = nodes[i]; + u8 *s = format (0, "%v%c", n->name, 0); + if (sm->nodes[n->index]) + vec_free (sm->nodes[n->index]); + sm->nodes[n->index] = s; + + oldheap = clib_mem_set_heap (oldheap); +#define _(E, t, name, p) \ + vlib_stats_add_symlink (STAT_COUNTER_##E, n->index, "/nodes/%U/" #name, \ + format_vlib_stats_symlink, s); + foreach_stat_segment_node_counter_name +#undef _ + oldheap = clib_mem_set_heap (oldheap); + } + + vlib_stats_segment_unlock (); + clib_mem_set_heap (oldheap); + no_max_nodes = l; + } + + for (j = 0; j < vec_len (node_dups); j++) + { + vlib_node_t **nodes = node_dups[j]; + + for (i = 0; i < vec_len (nodes); i++) + { + counter_t **counters; + counter_t *c; + vlib_node_t *n = nodes[i]; + + if (j == 0) + { + if (strncmp ((char *) sm->nodes[n->index], (char *) n->name, + strlen ((char *) sm->nodes[n->index]))) + { + u32 vector_index; + void *oldheap = clib_mem_set_heap (sm->heap); + vlib_stats_segment_lock (); + u8 *s = format (0, "%v%c", n->name, 0); + clib_mem_set_heap (oldheap); +#define _(E, t, name, p) \ + vec_reset_length (symlink_name); \ + symlink_name = format (symlink_name, "/nodes/%U/" #name, \ + format_vlib_stats_symlink, sm->nodes[n->index]); \ + vector_index = vlib_stats_find_entry_index ("%v", symlink_name); \ + ASSERT (vector_index != -1); \ + vlib_stats_rename_symlink (vector_index, "/nodes/%U/" #name, \ + format_vlib_stats_symlink, s); + foreach_stat_segment_node_counter_name +#undef _ + vec_free (symlink_name); + clib_mem_set_heap (sm->heap); + vec_free (sm->nodes[n->index]); + sm->nodes[n->index] = s; + vlib_stats_segment_unlock (); + clib_mem_set_heap (oldheap); + } + } + + counters = sm->directory_vector[STAT_COUNTER_NODE_CLOCKS].data; + c = counters[j]; + c[n->index] = n->stats_total.clocks - n->stats_last_clear.clocks; + + counters = sm->directory_vector[STAT_COUNTER_NODE_VECTORS].data; + c = counters[j]; + c[n->index] = n->stats_total.vectors - n->stats_last_clear.vectors; + + counters = sm->directory_vector[STAT_COUNTER_NODE_CALLS].data; + c = counters[j]; + c[n->index] = n->stats_total.calls - n->stats_last_clear.calls; + + counters = sm->directory_vector[STAT_COUNTER_NODE_SUSPENDS].data; + c = counters[j]; + c[n->index] = n->stats_total.suspends - n->stats_last_clear.suspends; + } + vec_free (node_dups[j]); + } + vec_free (node_dups); + vec_free (stat_vms); +} + +static void +do_stat_segment_updates (vlib_main_t *vm, vlib_stats_segment_t *sm) +{ + if (sm->node_counters_enabled) + update_node_counters (sm); + + vlib_stats_collector_t *c; + pool_foreach (c, sm->collectors) + { + vlib_stats_collector_data_t data = { + .entry_index = c->entry_index, + .vector_index = c->vector_index, + .private_data = c->private_data, + .entry = sm->directory_vector + c->entry_index, + }; + c->fn (&data); + } + + /* Heartbeat, so clients detect we're still here */ + sm->directory_vector[STAT_COUNTER_HEARTBEAT].value++; +} + +static uword +stat_segment_collector_process (vlib_main_t *vm, vlib_node_runtime_t *rt, + vlib_frame_t *f) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + + while (1) + { + do_stat_segment_updates (vm, sm); + vlib_process_suspend (vm, sm->update_interval); + } + return 0; /* or not */ +} + +VLIB_REGISTER_NODE (stat_segment_collector, static) = { + .function = stat_segment_collector_process, + .name = "statseg-collector-process", + .type = VLIB_NODE_TYPE_PROCESS, +}; diff --git a/src/vlib/stats/format.c b/src/vlib/stats/format.c new file mode 100644 index 00000000000..419ff6b4657 --- /dev/null +++ b/src/vlib/stats/format.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include +#include +#include + +u8 * +format_vlib_stats_symlink (u8 *s, va_list *args) +{ + char *input = va_arg (*args, char *); + char *modified_input = vec_dup (input); + int i; + u8 *result; + + for (i = 0; i < strlen (modified_input); i++) + if (modified_input[i] == '/') + modified_input[i] = '_'; + + result = format (s, "%s", modified_input); + vec_free (modified_input); + return result; +} diff --git a/src/vlib/stats/init.c b/src/vlib/stats/init.c new file mode 100644 index 00000000000..d81916a288d --- /dev/null +++ b/src/vlib/stats/init.c @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include +#include +#include + +#define STAT_SEGMENT_SOCKET_FILENAME "stats.sock" + +static void +vector_rate_collector_fn (vlib_stats_collector_data_t *d) +{ + vlib_main_t *this_vlib_main; + counter_t **counters = d->entry->data; + counter_t *cb = counters[0]; + f64 vector_rate = 0.0; + u32 i, n_threads = vlib_get_n_threads (); + + vlib_stats_validate_counter_vector (d->entry_index, n_threads - 1); + + for (i = 0; i < n_threads; i++) + { + f64 this_vector_rate; + this_vlib_main = vlib_get_main_by_index (i); + + this_vector_rate = vlib_internal_node_vector_rate (this_vlib_main); + vlib_clear_internal_node_vector_rate (this_vlib_main); + + cb[i] = this_vector_rate; + vector_rate += this_vector_rate; + } + + /* And set the system average rate */ + vector_rate /= (f64) (i > 1 ? i - 1 : 1); + vlib_stats_set_gauge (d->private_data, vector_rate); +} + +clib_error_t * +vlib_stats_init (vlib_main_t *vm) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_shared_header_t *shared_header; + vlib_stats_collector_reg_t reg = {}; + + void *oldheap; + uword memory_size, sys_page_sz; + int mfd; + char *mem_name = "stat segment"; + void *heap, *memaddr; + + memory_size = sm->memory_size; + if (memory_size == 0) + memory_size = STAT_SEGMENT_DEFAULT_SIZE; + + if (sm->log2_page_sz == CLIB_MEM_PAGE_SZ_UNKNOWN) + sm->log2_page_sz = CLIB_MEM_PAGE_SZ_DEFAULT; + + mfd = clib_mem_vm_create_fd (sm->log2_page_sz, mem_name); + + if (mfd == -1) + return clib_error_return (0, "stat segment memory fd failure: %U", + format_clib_error, clib_mem_get_last_error ()); + /* Set size */ + if ((ftruncate (mfd, memory_size)) == -1) + { + close (mfd); + return clib_error_return (0, "stat segment ftruncate failure"); + } + + memaddr = clib_mem_vm_map_shared (0, memory_size, mfd, 0, mem_name); + + if (memaddr == CLIB_MEM_VM_MAP_FAILED) + return clib_error_return (0, "stat segment mmap failure"); + + sys_page_sz = clib_mem_get_page_size (); + + heap = + clib_mem_create_heap (((u8 *) memaddr) + sys_page_sz, + memory_size - sys_page_sz, 1 /* locked */, mem_name); + sm->heap = heap; + sm->memfd = mfd; + + sm->directory_vector_by_name = hash_create_string (0, sizeof (uword)); + sm->shared_header = shared_header = memaddr; + + shared_header->version = STAT_SEGMENT_VERSION; + shared_header->base = memaddr; + + sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t)); + clib_spinlock_init (sm->stat_segment_lockp); + + oldheap = clib_mem_set_heap (sm->heap); + + /* Set up the name to counter-vector hash table */ + sm->directory_vector = 0; + + shared_header->epoch = 1; + + /* Scalar stats and node counters */ + vec_validate (sm->directory_vector, STAT_COUNTERS - 1); +#define _(E, t, n, p) \ + strcpy (sm->directory_vector[STAT_COUNTER_##E].name, p "/" #n); \ + sm->directory_vector[STAT_COUNTER_##E].type = STAT_DIR_TYPE_##t; + foreach_stat_segment_counter_name +#undef _ + /* Save the vector in the shared segment, for clients */ + shared_header->directory_vector = sm->directory_vector; + + clib_mem_set_heap (oldheap); + + vlib_stats_register_mem_heap (heap); + + reg.collect_fn = vector_rate_collector_fn; + reg.private_data = vlib_stats_add_gauge ("/sys/vector_rate"); + reg.entry_index = + vlib_stats_add_counter_vector ("/sys/vector_rate_per_worker"); + vlib_stats_register_collector_fn (®); + vlib_stats_validate_counter_vector (reg.entry_index, vlib_get_n_threads ()); + + return 0; +} + +static clib_error_t * +statseg_config (vlib_main_t *vm, unformat_input_t *input) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + sm->update_interval = 10.0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "socket-name %s", &sm->socket_name)) + ; + /* DEPRECATE: default (does nothing) */ + else if (unformat (input, "default")) + ; + else if (unformat (input, "size %U", unformat_memory_size, + &sm->memory_size)) + ; + else if (unformat (input, "page-size %U", unformat_log2_page_size, + &sm->log2_page_sz)) + ; + else if (unformat (input, "per-node-counters on")) + sm->node_counters_enabled = 1; + else if (unformat (input, "per-node-counters off")) + sm->node_counters_enabled = 0; + else if (unformat (input, "update-interval %f", &sm->update_interval)) + ; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + + /* + * NULL-terminate socket name string + * clib_socket_init()->socket_config() use C str* + */ + if (vec_len (sm->socket_name)) + vec_terminate_c_string (sm->socket_name); + + return 0; +} + +VLIB_EARLY_CONFIG_FUNCTION (statseg_config, "statseg"); + +/* + * Accept connection on the socket and exchange the fd for the shared + * memory segment. + */ +static clib_error_t * +stats_socket_accept_ready (clib_file_t *uf) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + clib_error_t *err; + clib_socket_t client = { 0 }; + + err = clib_socket_accept (sm->socket, &client); + if (err) + { + clib_error_report (err); + return err; + } + + /* Send the fd across and close */ + err = clib_socket_sendmsg (&client, 0, 0, &sm->memfd, 1); + if (err) + clib_error_report (err); + clib_socket_close (&client); + + return 0; +} + +static clib_error_t * +stats_segment_socket_init (void) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + clib_error_t *error; + clib_socket_t *s = clib_mem_alloc (sizeof (clib_socket_t)); + + memset (s, 0, sizeof (clib_socket_t)); + s->config = (char *) sm->socket_name; + s->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_SEQPACKET | + CLIB_SOCKET_F_ALLOW_GROUP_WRITE | CLIB_SOCKET_F_PASSCRED; + + if ((error = clib_socket_init (s))) + return error; + + clib_file_t template = { 0 }; + template.read_function = stats_socket_accept_ready; + template.file_descriptor = s->fd; + template.description = format (0, "stats segment listener %s", s->config); + clib_file_add (&file_main, &template); + + sm->socket = s; + + return 0; +} + +static clib_error_t * +stats_segment_socket_exit (vlib_main_t *vm) +{ + /* + * cleanup the listener socket on exit. + */ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + unlink ((char *) sm->socket_name); + return 0; +} + +VLIB_MAIN_LOOP_EXIT_FUNCTION (stats_segment_socket_exit); + +static clib_error_t * +statseg_init (vlib_main_t *vm) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + + /* set default socket file name when statseg config stanza is empty. */ + if (!vec_len (sm->socket_name)) + sm->socket_name = format (0, "%s/%s%c", vlib_unix_get_runtime_dir (), + STAT_SEGMENT_SOCKET_FILENAME, 0); + return stats_segment_socket_init (); +} + +VLIB_INIT_FUNCTION (statseg_init) = { + .runs_after = VLIB_INITS ("unix_input_init", "linux_epoll_input_init"), +}; diff --git a/src/vlib/stats/provider_mem.c b/src/vlib/stats/provider_mem.c new file mode 100644 index 00000000000..d52ea4aa0e5 --- /dev/null +++ b/src/vlib/stats/provider_mem.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include +#include +#include + +static clib_mem_heap_t **memory_heaps_vec; + +enum +{ + STAT_MEM_TOTAL = 0, + STAT_MEM_USED, + STAT_MEM_FREE, + STAT_MEM_USED_MMAP, + STAT_MEM_TOTAL_ALLOC, + STAT_MEM_FREE_CHUNKS, + STAT_MEM_RELEASABLE, +} stat_mem_usage_e; + +/* + * Called from the stats periodic process to update memory counters. + */ +static void +stat_provider_mem_usage_update_fn (vlib_stats_collector_data_t *d) +{ + clib_mem_usage_t usage; + clib_mem_heap_t *heap; + counter_t **counters = d->entry->data; + counter_t *cb; + + heap = vec_elt (memory_heaps_vec, d->private_data); + clib_mem_get_heap_usage (heap, &usage); + cb = counters[0]; + cb[STAT_MEM_TOTAL] = usage.bytes_total; + cb[STAT_MEM_USED] = usage.bytes_used; + cb[STAT_MEM_FREE] = usage.bytes_free; + cb[STAT_MEM_USED_MMAP] = usage.bytes_used_mmap; + cb[STAT_MEM_TOTAL_ALLOC] = usage.bytes_max; + cb[STAT_MEM_FREE_CHUNKS] = usage.bytes_free_reclaimed; + cb[STAT_MEM_RELEASABLE] = usage.bytes_overhead; +} + +/* + * Provide memory heap counters. + * Two dimensional array of heap index and per-heap gauges. + */ +void +vlib_stats_register_mem_heap (clib_mem_heap_t *heap) +{ + vlib_stats_collector_reg_t r = {}; + u32 idx; + + vec_add1 (memory_heaps_vec, heap); + + r.entry_index = idx = vlib_stats_add_counter_vector ("/mem/%s", heap->name); + vlib_stats_validate_counter_vector (idx, STAT_MEM_RELEASABLE); + + /* Create symlink */ + vlib_stats_add_symlink (idx, STAT_MEM_TOTAL, "/mem/%s/used", heap->name); + vlib_stats_add_symlink (idx, STAT_MEM_USED, "/mem/%s/total", heap->name); + vlib_stats_add_symlink (idx, STAT_MEM_FREE, "/mem/%s/free", heap->name); + + r.private_data = vec_len (memory_heaps_vec) - 1; + r.collect_fn = stat_provider_mem_usage_update_fn; + vlib_stats_register_collector_fn (&r); +} diff --git a/src/vlib/stats/shared.h b/src/vlib/stats/shared.h new file mode 100644 index 00000000000..d2d5d49cc54 --- /dev/null +++ b/src/vlib/stats/shared.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#ifndef included_stat_segment_shared_h +#define included_stat_segment_shared_h + +typedef enum +{ + STAT_DIR_TYPE_ILLEGAL = 0, + STAT_DIR_TYPE_SCALAR_INDEX, + STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE, + STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED, + STAT_DIR_TYPE_ERROR_INDEX, + STAT_DIR_TYPE_NAME_VECTOR, + STAT_DIR_TYPE_EMPTY, + STAT_DIR_TYPE_SYMLINK, +} stat_directory_type_t; + +typedef struct +{ + stat_directory_type_t type; + union + { + struct + { + uint32_t index1; + uint32_t index2; + }; + uint64_t index; + uint64_t value; + void *data; + uint8_t **string_vector; + }; +#define VLIB_STATS_MAX_NAME_SZ 128 + char name[VLIB_STATS_MAX_NAME_SZ]; +} vlib_stats_entry_t; + +/* + * Shared header first in the shared memory segment. + */ +typedef struct +{ + uint64_t version; + void *base; + volatile uint64_t epoch; + volatile uint64_t in_progress; + volatile vlib_stats_entry_t *directory_vector; + volatile uint64_t **error_vector; +} vlib_stats_shared_header_t; + +#endif /* included_stat_segment_shared_h */ diff --git a/src/vlib/stats/stats.c b/src/vlib/stats/stats.c new file mode 100644 index 00000000000..19edaf0866d --- /dev/null +++ b/src/vlib/stats/stats.c @@ -0,0 +1,514 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include +#include + +vlib_stats_main_t vlib_stats_main; + +/* + * Used only by VPP writers + */ + +void +vlib_stats_segment_lock (void) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + clib_spinlock_lock (sm->stat_segment_lockp); + sm->shared_header->in_progress = 1; +} + +void +vlib_stats_segment_unlock (void) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + sm->shared_header->epoch++; + sm->shared_header->in_progress = 0; + clib_spinlock_unlock (sm->stat_segment_lockp); +} + +/* + * Change heap to the stats shared memory segment + */ +void * +vlib_stats_set_heap () +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + + ASSERT (sm && sm->shared_header); + return clib_mem_set_heap (sm->heap); +} + +u32 +vlib_stats_find_entry_index (char *fmt, ...) +{ + u8 *name; + va_list va; + + va_start (va, fmt); + name = va_format (0, fmt, &va); + va_end (va); + vec_add1 (name, 0); + + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + hash_pair_t *hp = hash_get_pair (sm->directory_vector_by_name, name); + vec_free (name); + return hp ? hp->value[0] : STAT_SEGMENT_INDEX_INVALID; +} + +static void +hash_set_str_key_alloc (uword **h, const char *key, uword v) +{ + int size = strlen (key) + 1; + void *copy = clib_mem_alloc (size); + clib_memcpy_fast (copy, key, size); + hash_set_mem (*h, copy, v); +} + +static void +hash_unset_str_key_free (uword **h, const char *key) +{ + hash_pair_t *hp = hash_get_pair_mem (*h, key); + if (hp) + { + void *_k = uword_to_pointer (hp->key, void *); + hash_unset_mem (*h, _k); + clib_mem_free (_k); + } +} + +u32 +vlib_stats_create_counter (vlib_stats_entry_t *e) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + void *oldheap; + u32 index = ~0; + int i; + + oldheap = clib_mem_set_heap (sm->heap); + vec_foreach_index_backwards (i, sm->directory_vector) + if (sm->directory_vector[i].type == STAT_DIR_TYPE_EMPTY) + { + index = i; + break; + } + + index = index == ~0 ? vec_len (sm->directory_vector) : index; + + vec_validate (sm->directory_vector, index); + sm->directory_vector[index] = *e; + + clib_mem_set_heap (oldheap); + hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index); + + return index; +} + +void +vlib_stats_remove_entry (u32 entry_index) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index); + void *oldheap; + counter_t **c; + u32 i; + + if (entry_index >= vec_len (sm->directory_vector)) + return; + + oldheap = clib_mem_set_heap (sm->heap); + + switch (e->type) + { + case STAT_DIR_TYPE_NAME_VECTOR: + for (i = 0; i < vec_len (e->string_vector); i++) + vec_free (e->string_vector[i]); + vec_free (e->string_vector); + break; + + case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: + c = e->data; + e->data = 0; + for (i = 0; i < vec_len (c); i++) + vec_free (c[i]); + vec_free (c); + break; + + case STAT_DIR_TYPE_SCALAR_INDEX: + case STAT_DIR_TYPE_SYMLINK: + break; + default: + ASSERT (0); + } + + clib_mem_set_heap (oldheap); + hash_unset_str_key_free (&sm->directory_vector_by_name, e->name); + + memset (e, 0, sizeof (*e)); + e->type = STAT_DIR_TYPE_EMPTY; +} + +static void +vlib_stats_set_entry_name (vlib_stats_entry_t *e, char *s) +{ + u32 i, len = VLIB_STATS_MAX_NAME_SZ - 1; + + for (i = 0; i < len; i++) + { + e->name[i] = s[i]; + if (s[i] == 0) + return; + } + ASSERT (i < VLIB_STATS_MAX_NAME_SZ - 1); + s[i] = 0; +} + +void +vlib_stats_update_counter (void *cm_arg, u32 cindex, + stat_directory_type_t type) +{ + vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg; + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_shared_header_t *shared_header = sm->shared_header; + char *stat_segment_name; + vlib_stats_entry_t e = { 0 }; + + /* Not all counters have names / hash-table entries */ + if (!cm->name && !cm->stat_segment_name) + return; + + ASSERT (shared_header); + + vlib_stats_segment_lock (); + + /* Lookup hash-table is on the main heap */ + stat_segment_name = cm->stat_segment_name ? cm->stat_segment_name : cm->name; + + u32 vector_index = vlib_stats_find_entry_index ("%s", stat_segment_name); + + /* Update the vector */ + if (vector_index == STAT_SEGMENT_INDEX_INVALID) + { + vlib_stats_set_entry_name (&e, stat_segment_name); + e.type = type; + vector_index = vlib_stats_create_counter (&e); + } + + vlib_stats_entry_t *ep = &sm->directory_vector[vector_index]; + ep->data = cm->counters; + + /* Reset the client hash table pointer, since it WILL change! */ + shared_header->directory_vector = sm->directory_vector; + + vlib_stats_segment_unlock (); +} + +void +vlib_stats_register_error_index (u64 *em_vec, u64 index, char *fmt, ...) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_shared_header_t *shared_header = sm->shared_header; + vlib_stats_entry_t e = {}; + va_list va; + u8 *name; + + va_start (va, fmt); + name = va_format (0, fmt, &va); + va_end (va); + + ASSERT (shared_header); + + vlib_stats_segment_lock (); + u32 vector_index = vlib_stats_find_entry_index ("%v", name); + + if (vector_index == STAT_SEGMENT_INDEX_INVALID) + { + vec_add1 (name, 0); + vlib_stats_set_entry_name (&e, (char *) name); + e.type = STAT_DIR_TYPE_ERROR_INDEX; + e.index = index; + vector_index = vlib_stats_create_counter (&e); + + /* Warn clients to refresh any pointers they might be holding */ + shared_header->directory_vector = sm->directory_vector; + } + + vlib_stats_segment_unlock (); + vec_free (name); +} + +void +vlib_stats_update_error_vector (u64 *error_vector, u32 thread_index, int lock) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_shared_header_t *shared_header = sm->shared_header; + void *oldheap = clib_mem_set_heap (sm->heap); + + ASSERT (shared_header); + + if (lock) + vlib_stats_segment_lock (); + + /* Reset the client hash table pointer, since it WILL change! */ + vec_validate (sm->error_vector, thread_index); + sm->error_vector[thread_index] = error_vector; + + shared_header->error_vector = sm->error_vector; + shared_header->directory_vector = sm->directory_vector; + + if (lock) + vlib_stats_segment_unlock (); + clib_mem_set_heap (oldheap); +} + +void +vlib_stats_delete_cm (void *cm_arg) +{ + vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg; + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_entry_t *e; + + /* Not all counters have names / hash-table entries */ + if (!cm->name && !cm->stat_segment_name) + return; + + vlib_stats_segment_lock (); + + /* Lookup hash-table is on the main heap */ + char *stat_segment_name = + cm->stat_segment_name ? cm->stat_segment_name : cm->name; + u32 index = vlib_stats_find_entry_index ("%s", stat_segment_name); + + e = &sm->directory_vector[index]; + + hash_unset_str_key_free (&sm->directory_vector_by_name, e->name); + + memset (e, 0, sizeof (*e)); + e->type = STAT_DIR_TYPE_EMPTY; + + vlib_stats_segment_unlock (); +} + +static u32 +vlib_stats_new_entry_internal (stat_directory_type_t t, u8 *name) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_shared_header_t *shared_header = sm->shared_header; + vlib_stats_entry_t e = { .type = t }; + + ASSERT (shared_header); + + u32 vector_index = vlib_stats_find_entry_index ("%v", name); + if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */ + { + vector_index = ~0; + goto done; + } + + vec_add1 (name, 0); + vlib_stats_set_entry_name (&e, (char *) name); + + vlib_stats_segment_lock (); + vector_index = vlib_stats_create_counter (&e); + + shared_header->directory_vector = sm->directory_vector; + + vlib_stats_segment_unlock (); + +done: + vec_free (name); + return vector_index; +} + +u32 +vlib_stats_add_gauge (char *fmt, ...) +{ + va_list va; + u8 *name; + + va_start (va, fmt); + name = va_format (0, fmt, &va); + va_end (va); + return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name); +} + +void +vlib_stats_set_gauge (u32 index, u64 value) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + + ASSERT (index < vec_len (sm->directory_vector)); + sm->directory_vector[index].value = value; +} + +u32 +vlib_stats_add_timestamp (char *fmt, ...) +{ + va_list va; + u8 *name; + + va_start (va, fmt); + name = va_format (0, fmt, &va); + va_end (va); + return vlib_stats_new_entry_internal (STAT_DIR_TYPE_SCALAR_INDEX, name); +} + +void +vlib_stats_set_timestamp (u32 entry_index, f64 value) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + + ASSERT (entry_index < vec_len (sm->directory_vector)); + sm->directory_vector[entry_index].value = value; +} + +u32 +vlib_stats_add_string_vector (char *fmt, ...) +{ + va_list va; + u8 *name; + + va_start (va, fmt); + name = va_format (0, fmt, &va); + va_end (va); + return vlib_stats_new_entry_internal (STAT_DIR_TYPE_NAME_VECTOR, name); +} + +void +vlib_stats_set_string_vector (u32 entry_index, u32 vector_index, char *fmt, + ...) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index); + va_list va; + void *oldheap; + + oldheap = clib_mem_set_heap (sm->heap); + vlib_stats_segment_lock (); + + vec_validate (e->string_vector, vector_index); + vec_reset_length (e->string_vector[vector_index]); + + va_start (va, fmt); + e->string_vector[vector_index] = + va_format (e->string_vector[vector_index], fmt, &va); + va_end (va); + + vlib_stats_segment_unlock (); + clib_mem_set_heap (oldheap); +} + +u32 +vlib_stats_add_counter_vector (char *fmt, ...) +{ + va_list va; + u8 *name; + + va_start (va, fmt); + name = va_format (0, fmt, &va); + va_end (va); + return vlib_stats_new_entry_internal (STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE, + name); +} + +void +vlib_stats_validate_counter_vector (u32 entry_index, u32 vector_index) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index); + void *oldheap; + counter_t **c = e->data; + + if (vec_len (c) > 0 && vec_len (c[0]) > vector_index) + return; + + oldheap = clib_mem_set_heap (sm->heap); + vlib_stats_segment_lock (); + + vec_validate_aligned (c, 0, CLIB_CACHE_LINE_BYTES); + vec_validate_aligned (c[0], vector_index, CLIB_CACHE_LINE_BYTES); + e->data = c; + + vlib_stats_segment_unlock (); + clib_mem_set_heap (oldheap); +} + +u32 +vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_shared_header_t *shared_header = sm->shared_header; + vlib_stats_entry_t e; + va_list va; + u8 *name; + + ASSERT (shared_header); + ASSERT (entry_index < vec_len (sm->directory_vector)); + + va_start (va, fmt); + name = va_format (0, fmt, &va); + va_end (va); + + if (vlib_stats_find_entry_index ("%v", name) == STAT_SEGMENT_INDEX_INVALID) + { + vec_add1 (name, 0); + vlib_stats_set_entry_name (&e, (char *) name); + e.type = STAT_DIR_TYPE_SYMLINK; + e.index1 = entry_index; + e.index2 = vector_index; + vector_index = vlib_stats_create_counter (&e); + + /* Warn clients to refresh any pointers they might be holding */ + shared_header->directory_vector = sm->directory_vector; + } + else + vector_index = ~0; + + vec_free (name); + return vector_index; +} + +void +vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_entry_t *e = vlib_stats_get_entry (sm, entry_index); + va_list va; + u8 *new_name; + + hash_unset_str_key_free (&sm->directory_vector_by_name, e->name); + + va_start (va, fmt); + new_name = va_format (0, fmt, &va); + va_end (va); + + vec_add1 (new_name, 0); + vlib_stats_set_entry_name (e, (char *) new_name); + hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, entry_index); + vec_free (new_name); +} + +f64 +vlib_stats_get_segment_update_rate (void) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + return sm->update_interval; +} + +void +vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *reg) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_collector_t *c; + + ASSERT (reg->entry_index != ~0); + + pool_get_zero (sm->collectors, c); + c->fn = reg->collect_fn; + c->entry_index = reg->entry_index; + c->vector_index = reg->vector_index; + c->private_data = reg->private_data; + + return; +} diff --git a/src/vlib/stats/stats.h b/src/vlib/stats/stats.h new file mode 100644 index 00000000000..25d7c4ba484 --- /dev/null +++ b/src/vlib/stats/stats.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#ifndef included_stats_stats_h +#define included_stats_stats_h + +#include +#include +#include + +/* Default stat segment 32m */ +#define STAT_SEGMENT_DEFAULT_SIZE (32 << 20) + +/* Shared segment memory layout version */ +#define STAT_SEGMENT_VERSION 2 + +#define STAT_SEGMENT_INDEX_INVALID UINT32_MAX + +typedef enum +{ + STAT_COUNTER_HEARTBEAT = 0, + STAT_COUNTER_LAST_STATS_CLEAR, + STAT_COUNTER_NODE_CLOCKS, + STAT_COUNTER_NODE_VECTORS, + STAT_COUNTER_NODE_CALLS, + STAT_COUNTER_NODE_SUSPENDS, + STAT_COUNTER_NODE_NAMES, + STAT_COUNTERS +} stat_segment_counter_t; + +#define foreach_stat_segment_node_counter_name \ + _ (NODE_CLOCKS, COUNTER_VECTOR_SIMPLE, clocks, "/sys/node") \ + _ (NODE_VECTORS, COUNTER_VECTOR_SIMPLE, vectors, "/sys/node") \ + _ (NODE_CALLS, COUNTER_VECTOR_SIMPLE, calls, "/sys/node") \ + _ (NODE_SUSPENDS, COUNTER_VECTOR_SIMPLE, suspends, "/sys/node") + +#define foreach_stat_segment_counter_name \ + _ (LAST_STATS_CLEAR, SCALAR_INDEX, last_stats_clear, "/sys") \ + _ (HEARTBEAT, SCALAR_INDEX, heartbeat, "/sys") \ + _ (NODE_NAMES, NAME_VECTOR, names, "/sys/node") \ + foreach_stat_segment_node_counter_name + +typedef struct +{ + u32 entry_index; + u32 vector_index; + u64 private_data; + vlib_stats_entry_t *entry; +} vlib_stats_collector_data_t; + +typedef void (*vlib_stats_collector_fn_t) (vlib_stats_collector_data_t *); + +typedef struct +{ + vlib_stats_collector_fn_t collect_fn; + u32 entry_index; + u32 vector_index; + u64 private_data; +} vlib_stats_collector_reg_t; + +typedef struct +{ + vlib_stats_collector_fn_t fn; + u32 entry_index; + u32 vector_index; + u64 private_data; +} vlib_stats_collector_t; + +typedef struct +{ + /* internal, does not point to shared memory */ + vlib_stats_collector_t *collectors; + + /* statistics segment */ + uword *directory_vector_by_name; + vlib_stats_entry_t *directory_vector; + volatile u64 **error_vector; + u8 **nodes; + + /* Update interval */ + f64 update_interval; + + clib_spinlock_t *stat_segment_lockp; + clib_socket_t *socket; + u8 *socket_name; + ssize_t memory_size; + clib_mem_page_sz_t log2_page_sz; + u8 node_counters_enabled; + void *heap; + vlib_stats_shared_header_t + *shared_header; /* pointer to shared memory segment */ + int memfd; + +} vlib_stats_segment_t; + +typedef struct +{ + vlib_stats_segment_t segment; +} vlib_stats_main_t; + +extern vlib_stats_main_t vlib_stats_main; + +static_always_inline vlib_stats_segment_t * +vlib_stats_get_segment () +{ + return &vlib_stats_main.segment; +} + +static_always_inline vlib_stats_entry_t * +vlib_stats_get_entry (vlib_stats_segment_t *sm, u32 entry_index) +{ + vlib_stats_entry_t *e; + ASSERT (entry_index < vec_len (sm->directory_vector)); + e = sm->directory_vector + entry_index; + ASSERT (e->type != STAT_DIR_TYPE_EMPTY && e->type != STAT_DIR_TYPE_ILLEGAL); + return e; +} + +clib_error_t *vlib_stats_init (vlib_main_t *vm); +void *vlib_stats_set_heap (); +void vlib_stats_update_counter (void *, u32, stat_directory_type_t); +void vlib_stats_register_error_index (u64 *em_vec, u64 index, char *fmt, ...); +void vlib_stats_update_error_vector (u64 *error_vector, u32 thread_index, + int lock); +void vlib_stats_segment_lock (void); +void vlib_stats_segment_unlock (void); +void vlib_stats_delete_cm (void *); +void vlib_stats_register_mem_heap (clib_mem_heap_t *); +f64 vlib_stats_get_segment_update_rate (void); + +/* gauge */ +u32 vlib_stats_add_gauge (char *fmt, ...); +void vlib_stats_set_gauge (u32 entry_index, u64 value); + +/* timestamp */ +u32 vlib_stats_add_timestamp (char *fmt, ...); +void vlib_stats_set_timestamp (u32 entry_index, f64 value); + +/* vector */ +u32 vlib_stats_add_counter_vector (char *fmt, ...); +void vlib_stats_validate_counter_vector (u32 entry_index, u32 vector_index); + +/* string vector */ +u32 vlib_stats_add_string_vector (char *fmt, ...); +void vlib_stats_set_string_vector (u32 entry_index, u32 vector_index, + char *fmt, ...); + +/* symlink */ +u32 vlib_stats_add_symlink (u32 entry_index, u32 vector_index, char *fmt, ...); +void vlib_stats_rename_symlink (u64 entry_index, char *fmt, ...); + +/* common to all types */ +void vlib_stats_remove_entry (u32 entry_index); +u32 vlib_stats_find_entry_index (char *fmt, ...); +void vlib_stats_register_collector_fn (vlib_stats_collector_reg_t *r); + +format_function_t format_vlib_stats_symlink; + +#endif diff --git a/src/vlib/threads.c b/src/vlib/threads.c index f4d95c45059..23e52c68a4f 100644 --- a/src/vlib/threads.c +++ b/src/vlib/threads.c @@ -24,7 +24,7 @@ #include -#include +#include u32 vl (void *p) @@ -180,6 +180,11 @@ vlib_thread_init (vlib_main_t * vm) u32 first_index = 1; u32 i; uword *avail_cpu; + u32 stats_num_worker_threads_dir_index; + + stats_num_worker_threads_dir_index = + vlib_stats_add_gauge ("/sys/num_worker_threads"); + ASSERT (stats_num_worker_threads_dir_index != ~0); /* get bitmaps of active cpu cores and sockets */ tm->cpu_core_bitmap = @@ -319,6 +324,7 @@ vlib_thread_init (vlib_main_t * vm) clib_bitmap_free (avail_cpu); tm->n_vlib_mains = n_vlib_mains; + vlib_stats_set_gauge (stats_num_worker_threads_dir_index, n_vlib_mains - 1); /* * Allocate the remaining worker threads, and thread stack vector slots @@ -729,12 +735,13 @@ start_workers (vlib_main_t * vm) CLIB_CACHE_LINE_BYTES); /* Switch to the stats segment ... */ - void *oldheap = vlib_stats_push_heap (0); + void *oldheap = vlib_stats_set_heap (); vm_clone->error_main.counters = vec_dup_aligned (vlib_get_first_main ()->error_main.counters, CLIB_CACHE_LINE_BYTES); - vlib_stats_pop_heap2 (vm_clone->error_main.counters, - worker_thread_index, oldheap, 1); + clib_mem_set_heap (oldheap); + vlib_stats_update_error_vector (vm_clone->error_main.counters, + worker_thread_index, 1); vm_clone->error_main.counters_last_clear = vec_dup_aligned ( vlib_get_first_main ()->error_main.counters_last_clear, @@ -892,11 +899,12 @@ vlib_worker_thread_node_refork (void) j = vec_len (vm->error_main.counters) - 1; /* Switch to the stats segment ... */ - void *oldheap = vlib_stats_push_heap (0); + void *oldheap = vlib_stats_set_heap (); vec_validate_aligned (old_counters, j, CLIB_CACHE_LINE_BYTES); + clib_mem_set_heap (oldheap); vm_clone->error_main.counters = old_counters; - vlib_stats_pop_heap2 (vm_clone->error_main.counters, vm_clone->thread_index, - oldheap, 0); + vlib_stats_update_error_vector (vm_clone->error_main.counters, + vm_clone->thread_index, 0); vec_validate_aligned (old_counters_all_clear, j, CLIB_CACHE_LINE_BYTES); vm_clone->error_main.counters_last_clear = old_counters_all_clear; @@ -1392,7 +1400,7 @@ vlib_worker_thread_barrier_release (vlib_main_t * vm) * rebuilding the stat segment node clones from the * stat thread... */ - vlib_stat_segment_lock (); + vlib_stats_segment_lock (); /* Do stats elements on main thread */ worker_thread_node_runtime_update_internal (); @@ -1443,7 +1451,7 @@ vlib_worker_thread_barrier_release (vlib_main_t * vm) os_panic (); } } - vlib_stat_segment_unlock (); + vlib_stats_segment_unlock (); } t_closed_total = now - vm->barrier_epoch; diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index 88ef4cca7b9..1cd5b58f85a 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -41,6 +41,7 @@ list(APPEND VNET_SOURCES interface/tx_queue.c interface/runtime.c interface/monitor.c + interface/stats.c interface_stats.c misc.c ) diff --git a/src/vnet/bfd/bfd_udp.c b/src/vnet/bfd/bfd_udp.c index 074d546c37b..c89dd7cfa4d 100644 --- a/src/vnet/bfd/bfd_udp.c +++ b/src/vnet/bfd/bfd_udp.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include #include @@ -85,9 +85,9 @@ bfd_udp_main_t bfd_udp_main; void bfd_udp_update_stat_segment_entry (u32 entry, u64 value) { - vlib_stat_segment_lock (); - stat_segment_set_state_counter (entry, value); - vlib_stat_segment_unlock (); + vlib_stats_segment_lock (); + vlib_stats_set_gauge (entry, value); + vlib_stats_segment_unlock (); } vnet_api_error_t @@ -1694,19 +1694,18 @@ clib_error_t * bfd_udp_stats_init (bfd_udp_main_t *bum) { const char *name4 = "/bfd/udp4/sessions"; - bum->udp4_sessions_count_stat_seg_entry = - stat_segment_new_entry ((u8 *) name4, STAT_DIR_TYPE_SCALAR_INDEX); + bum->udp4_sessions_count_stat_seg_entry = vlib_stats_add_gauge ("%s", name4); - stat_segment_set_state_counter (bum->udp4_sessions_count_stat_seg_entry, 0); + vlib_stats_set_gauge (bum->udp4_sessions_count_stat_seg_entry, 0); if (~0 == bum->udp4_sessions_count_stat_seg_entry) { return clib_error_return ( 0, "Could not create stat segment entry for %s", name4); } const char *name6 = "/bfd/udp6/sessions"; - bum->udp6_sessions_count_stat_seg_entry = - stat_segment_new_entry ((u8 *) name6, STAT_DIR_TYPE_SCALAR_INDEX); + bum->udp6_sessions_count_stat_seg_entry = vlib_stats_add_gauge ("%s", name6); + vlib_stats_set_gauge (bum->udp6_sessions_count_stat_seg_entry, 0); if (~0 == bum->udp6_sessions_count_stat_seg_entry) { return clib_error_return ( diff --git a/src/vnet/bonding/cli.c b/src/vnet/bonding/cli.c index c3593ab663e..b0ded4734dd 100644 --- a/src/vnet/bonding/cli.c +++ b/src/vnet/bonding/cli.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include void bond_disable_collecting_distributing (vlib_main_t * vm, member_if_t * mif) @@ -323,10 +323,10 @@ bond_delete_neighbor (vlib_main_t * vm, bond_if_t * bif, member_if_t * mif) if (bif->mode == BOND_MODE_LACP) { - stat_segment_deregister_state_counter - (bm->stats[bif->sw_if_index][mif->sw_if_index].actor_state); - stat_segment_deregister_state_counter - (bm->stats[bif->sw_if_index][mif->sw_if_index].partner_state); + vlib_stats_remove_entry ( + bm->stats[bif->sw_if_index][mif->sw_if_index].actor_state); + vlib_stats_remove_entry ( + bm->stats[bif->sw_if_index][mif->sw_if_index].partner_state); } pool_put (bm->neighbors, mif); @@ -650,32 +650,29 @@ bond_add_member (vlib_main_t * vm, bond_add_member_args_t * args) } if (bif->mode == BOND_MODE_LACP) { - u8 *name = format (0, "/if/lacp/%u/%u/state%c", bif->sw_if_index, - args->member, 0); + u32 actor_idx, partner_idx; - vec_validate (bm->stats, bif->sw_if_index); - vec_validate (bm->stats[bif->sw_if_index], args->member); - - args->error = stat_segment_register_state_counter - (name, &bm->stats[bif->sw_if_index][args->member].actor_state); - if (args->error != 0) + actor_idx = vlib_stats_add_gauge ("/if/lacp/%u/%u/state", + bif->sw_if_index, args->member); + if (actor_idx == ~0) { args->rv = VNET_API_ERROR_INVALID_INTERFACE; - vec_free (name); return; } - vec_reset_length (name); - name = format (0, "/if/lacp/%u/%u/partner-state%c", bif->sw_if_index, - args->member, 0); - args->error = stat_segment_register_state_counter - (name, &bm->stats[bif->sw_if_index][args->member].partner_state); - vec_free (name); - if (args->error != 0) + partner_idx = vlib_stats_add_gauge ("/if/lacp/%u/%u/partner-state", + bif->sw_if_index, args->member); + if (partner_idx == ~0) { + vlib_stats_remove_entry (actor_idx); args->rv = VNET_API_ERROR_INVALID_INTERFACE; return; } + + vec_validate (bm->stats, bif->sw_if_index); + vec_validate (bm->stats[bif->sw_if_index], args->member); + bm->stats[bif->sw_if_index][args->member].actor_state = actor_idx; + bm->stats[bif->sw_if_index][args->member].partner_state = partner_idx; } pool_get (bm->neighbors, mif); diff --git a/src/vnet/devices/devices.c b/src/vnet/devices/devices.c index 5c28cadc03c..1a4f02df6a8 100644 --- a/src/vnet/devices/devices.c +++ b/src/vnet/devices/devices.c @@ -18,6 +18,7 @@ #include #include #include +#include vnet_device_main_t vnet_device_main; @@ -101,12 +102,30 @@ VNET_FEATURE_INIT (ethernet_input, static) = { }; /* *INDENT-ON* */ +static void +input_rate_collector_fn (vlib_stats_collector_data_t *d) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_entry_t *e2 = sm->directory_vector + d->private_data; + static u64 last_input_packets = 0; + f64 dt, now; + + now = vlib_time_now (vlib_get_main ()); + u64 input_packets = vnet_get_aggregate_rx_packets (); + + dt = now - e2->value; + d->entry->value = (f64) (input_packets - last_input_packets) / dt; + last_input_packets = input_packets; + e2->value = now; +} + static clib_error_t * vnet_device_init (vlib_main_t * vm) { vnet_device_main_t *vdm = &vnet_device_main; vlib_thread_main_t *tm = vlib_get_thread_main (); vlib_thread_registration_t *tr; + vlib_stats_collector_reg_t reg = {}; uword *p; vec_validate_aligned (vdm->workers, tm->n_vlib_mains - 1, @@ -120,6 +139,12 @@ vnet_device_init (vlib_main_t * vm) vdm->next_worker_thread_index = tr->first_index; vdm->last_worker_thread_index = tr->first_index + tr->count - 1; } + + reg.private_data = vlib_stats_add_timestamp ("/sys/last_update"); + reg.entry_index = vlib_stats_add_gauge ("/sys/input_rate"); + reg.collect_fn = input_rate_collector_fn; + vlib_stats_register_collector_fn (®); + return 0; } diff --git a/src/vnet/interface/stats.c b/src/vnet/interface/stats.c new file mode 100644 index 00000000000..55e49eb1bc2 --- /dev/null +++ b/src/vnet/interface/stats.c @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include +#include +#include +#include +#include /* vnet_get_aggregate_rx_packets */ +#include + +static u32 if_names_stats_entry_index = ~0; +static u32 **dir_entry_indices = 0; + +static struct +{ + char *prefix, *name; + u32 index; +} if_counters[] = { +#define _(e, n, p) { .prefix = #p, .name = #n }, + foreach_simple_interface_counter_name foreach_combined_interface_counter_name +#undef _ +}; + +static clib_error_t * +statseg_sw_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_add) +{ + vlib_stats_segment_t *sm = vlib_stats_get_segment (); + vlib_stats_entry_t *e; + void *oldheap; + + if (if_names_stats_entry_index == ~0) + { + if_names_stats_entry_index = vlib_stats_add_string_vector ("/if/names"); + + for (int i = 0; i < ARRAY_LEN (if_counters); i++) + if_counters[i].index = vlib_stats_find_entry_index ( + "/%s/%s", if_counters[i].prefix, if_counters[i].name); + } + + e = sm->directory_vector + if_names_stats_entry_index; + + vec_validate (dir_entry_indices, sw_if_index); + + vlib_stats_segment_lock (); + + if (is_add) + { + vnet_sw_interface_t *si, *si_sup; + vnet_hw_interface_t *hi_sup; + u8 *s; + + si = vnet_get_sw_interface (vnm, sw_if_index); + si_sup = vnet_get_sup_sw_interface (vnm, si->sw_if_index); + ASSERT (si_sup->type == VNET_SW_INTERFACE_TYPE_HARDWARE); + hi_sup = vnet_get_hw_interface (vnm, si_sup->hw_if_index); + + oldheap = clib_mem_set_heap (sm->heap); + s = format (0, "%v", hi_sup->name); + if (si->type != VNET_SW_INTERFACE_TYPE_HARDWARE) + s = format (s, ".%d", si->sub.id); + s = format (s, "%c", 0); + + vec_validate (e->string_vector, sw_if_index); + + ASSERT (e->string_vector[sw_if_index] == 0); + e->string_vector[sw_if_index] = s; + clib_mem_set_heap (oldheap); + + s = format (0, "/interfaces/%U", format_vlib_stats_symlink, s); + for (u32 index, i = 0; i < ARRAY_LEN (if_counters); i++) + { + index = vlib_stats_add_symlink (if_counters[i].index, sw_if_index, + "%v/%s", s, if_counters[i].name); + ASSERT (index != ~0); + vec_add1 (dir_entry_indices[sw_if_index], index); + } + vec_free (s); + } + else + { + oldheap = clib_mem_set_heap (sm->heap); + vec_free (e->string_vector[sw_if_index]); + clib_mem_set_heap (oldheap); + for (u32 i = 0; i < vec_len (dir_entry_indices[sw_if_index]); i++) + vlib_stats_remove_entry (dir_entry_indices[sw_if_index][i]); + vec_free (dir_entry_indices[sw_if_index]); + } + + vlib_stats_segment_unlock (); + + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (statseg_sw_interface_add_del); diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c index b0c83919f03..a5eafaf5f68 100644 --- a/src/vpp-api/client/stat_client.c +++ b/src/vpp-api/client/stat_client.c @@ -29,7 +29,8 @@ #include #include #include -#include +#include +#include #include stat_client_main_t stat_client_main; @@ -81,8 +82,8 @@ recv_fd (int sock) return fd; } -static stat_segment_directory_entry_t * -get_stat_vector_r (stat_client_main_t * sm) +static vlib_stats_entry_t * +get_stat_vector_r (stat_client_main_t *sm) { ASSERT (sm->shared_header); return stat_segment_adjust (sm, @@ -172,7 +173,7 @@ double stat_segment_heartbeat_r (stat_client_main_t * sm) { stat_segment_access_t sa; - stat_segment_directory_entry_t *ep; + vlib_stats_entry_t *ep; /* Has directory been updated? */ if (sm->shared_header->epoch != sm->current_epoch) @@ -223,7 +224,7 @@ stat_vec_combined_init (vlib_counter_t c) * threads), otherwise copy out all values. */ static stat_segment_data_t -copy_data (stat_segment_directory_entry_t *ep, u32 index2, char *name, +copy_data (vlib_stats_entry_t *ep, u32 index2, char *name, stat_client_main_t *sm) { stat_segment_data_t result = { 0 }; @@ -297,7 +298,7 @@ copy_data (stat_segment_directory_entry_t *ep, u32 index2, char *name, case STAT_DIR_TYPE_SYMLINK: /* Gather info from all threads into a vector */ { - stat_segment_directory_entry_t *ep2; + vlib_stats_entry_t *ep2; ep2 = vec_elt_at_index (sm->directory_vector, ep->index1); return copy_data (ep2, ep->index2, ep->name, sm); } @@ -370,7 +371,7 @@ stat_segment_ls_r (uint8_t ** patterns, stat_client_main_t * sm) if (stat_segment_access_start (&sa, sm)) return 0; - stat_segment_directory_entry_t *counter_vec = get_stat_vector_r (sm); + vlib_stats_entry_t *counter_vec = get_stat_vector_r (sm); for (j = 0; j < vec_len (counter_vec); j++) { for (i = 0; i < vec_len (patterns); i++) @@ -413,7 +414,7 @@ stat_segment_data_t * stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm) { int i; - stat_segment_directory_entry_t *ep; + vlib_stats_entry_t *ep; stat_segment_data_t *res = 0; stat_segment_access_t sa; @@ -474,7 +475,7 @@ stat_segment_string_vector (uint8_t ** string_vector, const char *string) stat_segment_data_t * stat_segment_dump_entry_r (uint32_t index, stat_client_main_t * sm) { - stat_segment_directory_entry_t *ep; + vlib_stats_entry_t *ep; stat_segment_data_t *res = 0; stat_segment_access_t sa; @@ -504,9 +505,9 @@ stat_segment_dump_entry (uint32_t index) char * stat_segment_index_to_name_r (uint32_t index, stat_client_main_t * sm) { - stat_segment_directory_entry_t *ep; + vlib_stats_entry_t *ep; stat_segment_access_t sa; - stat_segment_directory_entry_t *vec; + vlib_stats_entry_t *vec; /* Has directory been update? */ if (sm->shared_header->epoch != sm->current_epoch) diff --git a/src/vpp-api/client/stat_client.h b/src/vpp-api/client/stat_client.h index 730badd1728..26752718d90 100644 --- a/src/vpp-api/client/stat_client.h +++ b/src/vpp-api/client/stat_client.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include /* Default socket to exchange segment fd */ /* TODO: Get from runtime directory */ @@ -49,8 +49,8 @@ typedef struct typedef struct { uint64_t current_epoch; - stat_segment_shared_header_t *shared_header; - stat_segment_directory_entry_t *directory_vector; + vlib_stats_shared_header_t *shared_header; + vlib_stats_entry_t *directory_vector; ssize_t memory_size; uint64_t timeout; } stat_client_main_t; @@ -115,7 +115,7 @@ static inline int stat_segment_access_start (stat_segment_access_t * sa, stat_client_main_t * sm) { - stat_segment_shared_header_t *shared_header = sm->shared_header; + vlib_stats_shared_header_t *shared_header = sm->shared_header; uint64_t max_time; sa->epoch = shared_header->epoch; @@ -130,10 +130,8 @@ stat_segment_access_start (stat_segment_access_t * sa, while (shared_header->in_progress != 0) ; } - sm->directory_vector = - (stat_segment_directory_entry_t *) stat_segment_adjust (sm, - (void *) - sm->shared_header->directory_vector); + sm->directory_vector = (vlib_stats_entry_t *) stat_segment_adjust ( + sm, (void *) sm->shared_header->directory_vector); if (sm->timeout) return _time_now_nsec () < max_time ? 0 : -1; return 0; @@ -164,7 +162,7 @@ stat_segment_set_timeout (uint64_t timeout) static inline bool stat_segment_access_end (stat_segment_access_t * sa, stat_client_main_t * sm) { - stat_segment_shared_header_t *shared_header = sm->shared_header; + vlib_stats_shared_header_t *shared_header = sm->shared_header; if (shared_header->epoch != sa->epoch || shared_header->in_progress) return false; diff --git a/src/vpp/CMakeLists.txt b/src/vpp/CMakeLists.txt index 1b43b1299d2..44c1f6d6b11 100644 --- a/src/vpp/CMakeLists.txt +++ b/src/vpp/CMakeLists.txt @@ -66,8 +66,6 @@ set(VPP_SOURCES vnet/main.c app/vpe_cli.c app/version.c - stats/stat_segment.c - stats/stat_segment_provider.c api/api.c api/json_format.c api/types.c @@ -90,11 +88,6 @@ add_vpp_executable(vpp DEPENDS vpp_version_h api_headers ) -add_vpp_headers(vpp - stats/stat_segment.h - stats/stat_segment_shared.h -) - ############################################################################## # vppctl binary ############################################################################## diff --git a/src/vpp/stats/stat_segment.c b/src/vpp/stats/stat_segment.c deleted file mode 100644 index 3ab60b912cb..00000000000 --- a/src/vpp/stats/stat_segment.c +++ /dev/null @@ -1,1149 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "stat_segment.h" -#include -#include /* vnet_get_aggregate_rx_packets */ -#include - -stat_segment_main_t stat_segment_main; -#define STATSEG_MAX_NAMESZ 128 - -/* - * Used only by VPP writers - */ -void -vlib_stat_segment_lock (void) -{ - stat_segment_main_t *sm = &stat_segment_main; - clib_spinlock_lock (sm->stat_segment_lockp); - sm->shared_header->in_progress = 1; -} - -void -vlib_stat_segment_unlock (void) -{ - stat_segment_main_t *sm = &stat_segment_main; - sm->shared_header->epoch++; - sm->shared_header->in_progress = 0; - clib_spinlock_unlock (sm->stat_segment_lockp); -} - -/* - * Change heap to the stats shared memory segment - */ -void * -vlib_stats_push_heap (void *old) -{ - stat_segment_main_t *sm = &stat_segment_main; - - sm->last = old; - ASSERT (sm && sm->shared_header); - return clib_mem_set_heap (sm->heap); -} - -static u32 -lookup_hash_index (u8 * name) -{ - stat_segment_main_t *sm = &stat_segment_main; - u32 index = STAT_SEGMENT_INDEX_INVALID; - hash_pair_t *hp; - - /* Must be called in the context of the main heap */ - ASSERT (clib_mem_get_heap () != sm->heap); - - hp = hash_get_pair (sm->directory_vector_by_name, name); - if (hp) - { - index = hp->value[0]; - } - - return index; -} - -static u32 -vlib_stats_get_next_vector_index () -{ - stat_segment_main_t *sm = &stat_segment_main; - u32 next_vector_index = vec_len (sm->directory_vector); - - ssize_t i; - vec_foreach_index_backwards (i, sm->directory_vector) - { - if (sm->directory_vector[i].type == STAT_DIR_TYPE_EMPTY) - { - next_vector_index = i; - break; - } - } - - return next_vector_index; -} - -/* - * Wrapper functions that copies the key. Hash function is on the main heap. - */ -static void -hash_set_str_key_alloc (uword **h, const char *key, uword v) -{ - int size = strlen (key) + 1; - void *copy = clib_mem_alloc (size); - clib_memcpy_fast (copy, key, size); - hash_set_mem (*h, copy, v); -} - -static void -hash_unset_str_key_free (uword **h, const char *key) -{ - hash_pair_t *hp = hash_get_pair_mem (*h, key); - if (hp) - { - void *_k = uword_to_pointer (hp->key, void *); - hash_unset_mem (*h, _k); - clib_mem_free (_k); - } -} - -static u32 -vlib_stats_create_counter (stat_segment_directory_entry_t * e, void *oldheap) -{ - stat_segment_main_t *sm = &stat_segment_main; - - ASSERT (clib_mem_get_heap () == sm->heap); - - u32 index = vlib_stats_get_next_vector_index (); - vec_validate (sm->directory_vector, index); - sm->directory_vector[index] = *e; - - clib_mem_set_heap (oldheap); - hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index); - clib_mem_set_heap (sm->heap); - - return index; -} - -static void -vlib_stats_delete_counter (u32 index, void *oldheap) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_directory_entry_t *e; - - ASSERT (clib_mem_get_heap () == sm->heap); - - if (index > vec_len (sm->directory_vector)) - return; - - e = &sm->directory_vector[index]; - - clib_mem_set_heap (oldheap); - hash_unset_str_key_free (&sm->directory_vector_by_name, e->name); - clib_mem_set_heap (sm->heap); - - memset (e, 0, sizeof (*e)); - e->type = STAT_DIR_TYPE_EMPTY; -} - -/* - * Called from main heap - */ -void -vlib_stats_delete_cm (void *cm_arg) -{ - vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg; - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_directory_entry_t *e; - - /* Not all counters have names / hash-table entries */ - if (!cm->name && !cm->stat_segment_name) - { - return; - } - vlib_stat_segment_lock (); - - /* Lookup hash-table is on the main heap */ - char *stat_segment_name = - cm->stat_segment_name ? cm->stat_segment_name : cm->name; - u32 index = lookup_hash_index ((u8 *) stat_segment_name); - - e = &sm->directory_vector[index]; - hash_unset_str_key_free (&sm->directory_vector_by_name, e->name); - - void *oldheap = clib_mem_set_heap (sm->heap); /* Enter stats segment */ - clib_mem_set_heap (oldheap); /* Exit stats segment */ - - memset (e, 0, sizeof (*e)); - e->type = STAT_DIR_TYPE_EMPTY; - - vlib_stat_segment_unlock (); -} - -void -vlib_stats_pop_heap (void *cm_arg, void *oldheap, u32 cindex, - stat_directory_type_t type) -{ - vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg; - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_shared_header_t *shared_header = sm->shared_header; - char *stat_segment_name; - stat_segment_directory_entry_t e = { 0 }; - - /* Not all counters have names / hash-table entries */ - if (!cm->name && !cm->stat_segment_name) - { - clib_mem_set_heap (oldheap); - return; - } - - ASSERT (shared_header); - - vlib_stat_segment_lock (); - - /* Lookup hash-table is on the main heap */ - stat_segment_name = - cm->stat_segment_name ? cm->stat_segment_name : cm->name; - - clib_mem_set_heap (oldheap); /* Exit stats segment */ - u32 vector_index = lookup_hash_index ((u8 *) stat_segment_name); - /* Back to stats segment */ - clib_mem_set_heap (sm->heap); /* Re-enter stat segment */ - - - /* Update the vector */ - if (vector_index == STAT_SEGMENT_INDEX_INVALID) - { /* New */ - strncpy_s (e.name, STATSEG_MAX_NAMESZ, stat_segment_name, - STATSEG_MAX_NAMESZ - 1); - e.type = type; - vector_index = vlib_stats_create_counter (&e, oldheap); - } - - stat_segment_directory_entry_t *ep = &sm->directory_vector[vector_index]; - ep->data = cm->counters; - - /* Reset the client hash table pointer, since it WILL change! */ - shared_header->directory_vector = sm->directory_vector; - - vlib_stat_segment_unlock (); - clib_mem_set_heap (oldheap); -} - -u8 * -format_vlib_stats_symlink (u8 *s, va_list *args) -{ - char *input = va_arg (*args, char *); - char *modified_input = vec_dup (input); - int i; - u8 *result; - - for (i = 0; i < strlen (modified_input); i++) - if (modified_input[i] == '/') - modified_input[i] = '_'; - - result = format (s, "%s", modified_input); - vec_free (modified_input); - return result; -} - -void -vlib_stats_register_symlink (void *oldheap, u8 *name, u32 index1, u32 index2, - u8 lock) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_shared_header_t *shared_header = sm->shared_header; - stat_segment_directory_entry_t e; - - ASSERT (shared_header); - - if (lock) - vlib_stat_segment_lock (); - clib_mem_set_heap (oldheap); /* Exit stats segment */ - u32 vector_index = lookup_hash_index (name); - /* Back to stats segment */ - clib_mem_set_heap (sm->heap); /* Re-enter stat segment */ - - if (vector_index == STAT_SEGMENT_INDEX_INVALID) - { - strncpy_s (e.name, STATSEG_MAX_NAMESZ, (char *) name, - STATSEG_MAX_NAMESZ - 1); - e.type = STAT_DIR_TYPE_SYMLINK; - e.index1 = index1; - e.index2 = index2; - vector_index = vlib_stats_create_counter (&e, oldheap); - - /* Warn clients to refresh any pointers they might be holding */ - shared_header->directory_vector = sm->directory_vector; - } - - if (lock) - vlib_stat_segment_unlock (); -} - -void -vlib_stats_rename_symlink (void *oldheap, u64 index, u8 *new_name) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_directory_entry_t *e; - - ASSERT (clib_mem_get_heap () == sm->heap); - ASSERT (index < vec_len (sm->directory_vector)); - if (index > vec_len (sm->directory_vector)) - return; - - e = &sm->directory_vector[index]; - - clib_mem_set_heap (oldheap); - hash_unset_str_key_free (&sm->directory_vector_by_name, e->name); - clib_mem_set_heap (sm->heap); - - strncpy_s (e->name, STATSEG_MAX_NAMESZ, (char *) new_name, - STATSEG_MAX_NAMESZ - 1); - clib_mem_set_heap (oldheap); - hash_set_str_key_alloc (&sm->directory_vector_by_name, e->name, index); - clib_mem_set_heap (sm->heap); -} - -void -vlib_stats_register_error_index (void *oldheap, u8 * name, u64 * em_vec, - u64 index) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_shared_header_t *shared_header = sm->shared_header; - stat_segment_directory_entry_t e; - - ASSERT (shared_header); - - vlib_stat_segment_lock (); - clib_mem_set_heap (oldheap); /* Exit stats segment */ - u32 vector_index = lookup_hash_index (name); - /* Back to stats segment */ - clib_mem_set_heap (sm->heap); /* Re-enter stat segment */ - - if (vector_index == STAT_SEGMENT_INDEX_INVALID) - { - memcpy (e.name, name, vec_len (name)); - e.name[vec_len (name)] = '\0'; - e.type = STAT_DIR_TYPE_ERROR_INDEX; - e.index = index; - vector_index = vlib_stats_create_counter (&e, oldheap); - - /* Warn clients to refresh any pointers they might be holding */ - shared_header->directory_vector = sm->directory_vector; - } - - vlib_stat_segment_unlock (); -} - -/* - * Creates a two dimensional vector with the maximum valid index specified in - * both dimensions as arguments. - * Must be called on the stat segment heap. - */ -static void -stat_validate_counter_vector2 (stat_segment_directory_entry_t *ep, u32 max1, - u32 max2) -{ - counter_t **counters = ep->data; - int i; - vec_validate_aligned (counters, max1, CLIB_CACHE_LINE_BYTES); - for (i = 0; i <= max1; i++) - vec_validate_aligned (counters[i], max2, CLIB_CACHE_LINE_BYTES); - - ep->data = counters; -} - -static void -stat_validate_counter_vector (stat_segment_directory_entry_t *ep, u32 max) -{ - vlib_thread_main_t *tm = vlib_get_thread_main (); - ASSERT (tm->n_vlib_mains > 0); - stat_validate_counter_vector2 (ep, tm->n_vlib_mains, max); -} - -void -vlib_stats_pop_heap2 (u64 * error_vector, u32 thread_index, void *oldheap, - int lock) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_shared_header_t *shared_header = sm->shared_header; - - ASSERT (shared_header); - - if (lock) - vlib_stat_segment_lock (); - - /* Reset the client hash table pointer, since it WILL change! */ - vec_validate (sm->error_vector, thread_index); - sm->error_vector[thread_index] = error_vector; - - shared_header->error_vector = sm->error_vector; - shared_header->directory_vector = sm->directory_vector; - - if (lock) - vlib_stat_segment_unlock (); - clib_mem_set_heap (oldheap); -} - -/* - * Create a new entry and add name to directory hash. - * Returns ~0 if name exists. - * Called from main heap. - * The name is either C-string or nul-terminated vector - */ -u32 -stat_segment_new_entry (u8 *name, stat_directory_type_t t) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_shared_header_t *shared_header = sm->shared_header; - void *oldheap; - stat_segment_directory_entry_t e; - - ASSERT (shared_header); - - u32 vector_index = lookup_hash_index (name); - if (vector_index != STAT_SEGMENT_INDEX_INVALID) /* Already registered */ - return ~0; - - memset (&e, 0, sizeof (e)); - e.type = t; - // TODO, check length - strcpy_s (e.name, sizeof (e.name), (char *) name); - - oldheap = vlib_stats_push_heap (NULL); - vlib_stat_segment_lock (); - vector_index = vlib_stats_create_counter (&e, oldheap); - - shared_header->directory_vector = sm->directory_vector; - - vlib_stat_segment_unlock (); - clib_mem_set_heap (oldheap); - - return vector_index; -} - -clib_error_t * -vlib_map_stat_segment_init (void) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_shared_header_t *shared_header; - void *oldheap; - uword memory_size, sys_page_sz; - int mfd; - char *mem_name = "stat segment"; - void *heap, *memaddr; - - memory_size = sm->memory_size; - if (memory_size == 0) - memory_size = STAT_SEGMENT_DEFAULT_SIZE; - - if (sm->log2_page_sz == CLIB_MEM_PAGE_SZ_UNKNOWN) - sm->log2_page_sz = CLIB_MEM_PAGE_SZ_DEFAULT; - - mfd = clib_mem_vm_create_fd (sm->log2_page_sz, mem_name); - - if (mfd == -1) - return clib_error_return (0, "stat segment memory fd failure: %U", - format_clib_error, clib_mem_get_last_error ()); - /* Set size */ - if ((ftruncate (mfd, memory_size)) == -1) - { - close (mfd); - return clib_error_return (0, "stat segment ftruncate failure"); - } - - memaddr = clib_mem_vm_map_shared (0, memory_size, mfd, 0, mem_name); - - if (memaddr == CLIB_MEM_VM_MAP_FAILED) - return clib_error_return (0, "stat segment mmap failure"); - - sys_page_sz = clib_mem_get_page_size (); - - heap = - clib_mem_create_heap (((u8 *) memaddr) + sys_page_sz, - memory_size - sys_page_sz, 1 /* locked */, mem_name); - sm->heap = heap; - sm->memfd = mfd; - - sm->directory_vector_by_name = hash_create_string (0, sizeof (uword)); - sm->shared_header = shared_header = memaddr; - - shared_header->version = STAT_SEGMENT_VERSION; - shared_header->base = memaddr; - - sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t)); - clib_spinlock_init (sm->stat_segment_lockp); - - oldheap = clib_mem_set_heap (sm->heap); - - /* Set up the name to counter-vector hash table */ - sm->directory_vector = 0; - - shared_header->epoch = 1; - - /* Scalar stats and node counters */ - vec_validate (sm->directory_vector, STAT_COUNTERS - 1); -#define _(E,t,n,p) \ - strcpy(sm->directory_vector[STAT_COUNTER_##E].name, #p "/" #n); \ - sm->directory_vector[STAT_COUNTER_##E].type = STAT_DIR_TYPE_##t; - foreach_stat_segment_counter_name -#undef _ - /* Save the vector in the shared segment, for clients */ - shared_header->directory_vector = sm->directory_vector; - - clib_mem_set_heap (oldheap); - - vlib_stats_register_mem_heap (heap); - - return 0; -} - -static int -name_sort_cmp (void *a1, void *a2) -{ - stat_segment_directory_entry_t *n1 = a1; - stat_segment_directory_entry_t *n2 = a2; - - return strcmp ((char *) n1->name, (char *) n2->name); -} - -static u8 * -format_stat_dir_entry (u8 * s, va_list * args) -{ - stat_segment_directory_entry_t *ep = - va_arg (*args, stat_segment_directory_entry_t *); - char *type_name; - char *format_string; - - format_string = "%-74s %-10s %10lld"; - - switch (ep->type) - { - case STAT_DIR_TYPE_SCALAR_INDEX: - type_name = "ScalarPtr"; - break; - - case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE: - case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED: - type_name = "CMainPtr"; - break; - - case STAT_DIR_TYPE_ERROR_INDEX: - type_name = "ErrIndex"; - break; - - case STAT_DIR_TYPE_NAME_VECTOR: - type_name = "NameVector"; - break; - - case STAT_DIR_TYPE_EMPTY: - type_name = "empty"; - break; - - case STAT_DIR_TYPE_SYMLINK: - type_name = "Symlink"; - break; - - default: - type_name = "illegal!"; - break; - } - - return format (s, format_string, ep->name, type_name, 0); -} - -static clib_error_t * -show_stat_segment_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_directory_entry_t *show_data; - int i; - - int verbose = 0; - - if (unformat (input, "verbose")) - verbose = 1; - - /* Lock even as reader, as this command doesn't handle epoch changes */ - vlib_stat_segment_lock (); - show_data = vec_dup (sm->directory_vector); - vlib_stat_segment_unlock (); - - vec_sort_with_function (show_data, name_sort_cmp); - - vlib_cli_output (vm, "%-74s %10s %10s", "Name", "Type", "Value"); - - for (i = 0; i < vec_len (show_data); i++) - { - stat_segment_directory_entry_t *ep = vec_elt_at_index (show_data, i); - - if (ep->type == STAT_DIR_TYPE_EMPTY) - continue; - - vlib_cli_output (vm, "%-100U", format_stat_dir_entry, - vec_elt_at_index (show_data, i)); - } - - if (verbose) - { - ASSERT (sm->heap); - vlib_cli_output (vm, "%U", format_clib_mem_heap, sm->heap, - 0 /* verbose */ ); - } - - return 0; -} - -static clib_error_t * -show_stat_segment_hash_command_fn (vlib_main_t *vm, unformat_input_t *input, - vlib_cli_command_t *cmd) -{ - stat_segment_main_t *sm = &stat_segment_main; - char *name; - u32 i; - hash_foreach_mem (name, i, sm->directory_vector_by_name, - ({ vlib_cli_output (vm, "%d: %s\n", i, name); })); - return 0; -} - -VLIB_CLI_COMMAND (show_stat_segment_hash_command, static) = { - .path = "show statistics hash", - .short_help = "show statistics hash", - .function = show_stat_segment_hash_command_fn, -}; - -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (show_stat_segment_command, static) = -{ - .path = "show statistics segment", - .short_help = "show statistics segment [verbose]", - .function = show_stat_segment_command_fn, -}; -/* *INDENT-ON* */ - -/* - * Node performance counters: - * total_calls [threads][node-index] - * total_vectors - * total_calls - * total suspends - */ - -static inline void -update_node_counters (stat_segment_main_t * sm) -{ - vlib_main_t **stat_vms = 0; - vlib_node_t ***node_dups = 0; - int i, j; - static u32 no_max_nodes = 0; - - vlib_node_get_nodes (0 /* vm, for barrier sync */, - (u32) ~0 /* all threads */, 1 /* include stats */, - 0 /* barrier sync */, &node_dups, &stat_vms); - - u32 l = vec_len (node_dups[0]); - u8 *symlink_name = 0; - - /* - * Extend performance nodes if necessary - */ - if (l > no_max_nodes) - { - void *oldheap = clib_mem_set_heap (sm->heap); - vlib_stat_segment_lock (); - - stat_validate_counter_vector ( - &sm->directory_vector[STAT_COUNTER_NODE_CLOCKS], l - 1); - stat_validate_counter_vector ( - &sm->directory_vector[STAT_COUNTER_NODE_VECTORS], l - 1); - stat_validate_counter_vector ( - &sm->directory_vector[STAT_COUNTER_NODE_CALLS], l - 1); - stat_validate_counter_vector ( - &sm->directory_vector[STAT_COUNTER_NODE_SUSPENDS], l - 1); - - vec_validate (sm->nodes, l - 1); - stat_segment_directory_entry_t *ep; - ep = &sm->directory_vector[STAT_COUNTER_NODE_NAMES]; - ep->data = sm->nodes; - - /* Update names dictionary */ - vlib_node_t **nodes = node_dups[0]; - int i; - for (i = 0; i < vec_len (nodes); i++) - { - vlib_node_t *n = nodes[i]; - u8 *s = format (0, "%v%c", n->name, 0); - if (sm->nodes[n->index]) - vec_free (sm->nodes[n->index]); - sm->nodes[n->index] = s; - -#define _(E, t, name, p) \ - vec_reset_length (symlink_name); \ - symlink_name = format (symlink_name, "/nodes/%U/" #name "%c", \ - format_vlib_stats_symlink, s, 0); \ - vlib_stats_register_symlink (oldheap, symlink_name, STAT_COUNTER_##E, \ - n->index, 0 /* don't lock */); - foreach_stat_segment_node_counter_name -#undef _ - } - - vec_free (symlink_name); - vlib_stat_segment_unlock (); - clib_mem_set_heap (oldheap); - no_max_nodes = l; - } - - for (j = 0; j < vec_len (node_dups); j++) - { - vlib_node_t **nodes = node_dups[j]; - - for (i = 0; i < vec_len (nodes); i++) - { - counter_t **counters; - counter_t *c; - vlib_node_t *n = nodes[i]; - - if (j == 0) - { - if (strncmp ((char *) sm->nodes[n->index], (char *) n->name, - strlen ((char *) sm->nodes[n->index]))) - { - u32 vector_index; - u8 *symlink_new_name = 0; - void *oldheap = clib_mem_set_heap (sm->heap); - vlib_stat_segment_lock (); - u8 *s = format (0, "%v%c", n->name, 0); -#define _(E, t, name, p) \ - vec_reset_length (symlink_name); \ - symlink_name = format (symlink_name, "/nodes/%U/" #name "%c", \ - format_vlib_stats_symlink, sm->nodes[n->index], 0); \ - clib_mem_set_heap (oldheap); /* Exit stats segment */ \ - vector_index = lookup_hash_index ((u8 *) symlink_name); \ - ASSERT (vector_index != -1); \ - clib_mem_set_heap (sm->heap); /* Re-enter stat segment */ \ - vec_reset_length (symlink_new_name); \ - symlink_new_name = format (symlink_new_name, "/nodes/%U/" #name "%c", \ - format_vlib_stats_symlink, s, 0); \ - vlib_stats_rename_symlink (oldheap, vector_index, symlink_new_name); - foreach_stat_segment_node_counter_name -#undef _ - vec_free (symlink_name); - vec_free (symlink_new_name); - vec_free (sm->nodes[n->index]); - sm->nodes[n->index] = s; - vlib_stat_segment_unlock (); - clib_mem_set_heap (oldheap); - } - } - - counters = sm->directory_vector[STAT_COUNTER_NODE_CLOCKS].data; - c = counters[j]; - c[n->index] = n->stats_total.clocks - n->stats_last_clear.clocks; - - counters = sm->directory_vector[STAT_COUNTER_NODE_VECTORS].data; - c = counters[j]; - c[n->index] = n->stats_total.vectors - n->stats_last_clear.vectors; - - counters = sm->directory_vector[STAT_COUNTER_NODE_CALLS].data; - c = counters[j]; - c[n->index] = n->stats_total.calls - n->stats_last_clear.calls; - - counters = sm->directory_vector[STAT_COUNTER_NODE_SUSPENDS].data; - c = counters[j]; - c[n->index] = n->stats_total.suspends - n->stats_last_clear.suspends; - } - vec_free (node_dups[j]); - } - vec_free (node_dups); - vec_free (stat_vms); -} - -static void -do_stat_segment_updates (vlib_main_t *vm, stat_segment_main_t *sm) -{ - u64 input_packets; - f64 dt, now; - static int num_worker_threads_set; - - /* - * Set once at the beginning of time. - * Can't do this from the init routine, which happens before - * start_workers sets up vlib_mains... - */ - if (PREDICT_FALSE (num_worker_threads_set == 0)) - { - vlib_thread_main_t *tm = vlib_get_thread_main (); - ASSERT (tm->n_vlib_mains > 0); - stat_provider_register_vector_rate (tm->n_vlib_mains - 1); - sm->directory_vector[STAT_COUNTER_NUM_WORKER_THREADS].value = - tm->n_vlib_mains - 1; - num_worker_threads_set = 1; - } - - /* - * Compute the aggregate input rate - */ - now = vlib_time_now (vm); - dt = now - sm->directory_vector[STAT_COUNTER_LAST_UPDATE].value; - input_packets = vnet_get_aggregate_rx_packets (); - sm->directory_vector[STAT_COUNTER_INPUT_RATE].value = - (f64) (input_packets - sm->last_input_packets) / dt; - sm->directory_vector[STAT_COUNTER_LAST_UPDATE].value = now; - sm->last_input_packets = input_packets; - sm->directory_vector[STAT_COUNTER_LAST_STATS_CLEAR].value = - vm->node_main.time_last_runtime_stats_clear; - - if (sm->node_counters_enabled) - update_node_counters (sm); - - /* *INDENT-OFF* */ - stat_segment_gauges_pool_t *g; - pool_foreach (g, sm->gauges) - { - g->fn (&sm->directory_vector[g->directory_index], g->caller_index); - } - /* *INDENT-ON* */ - - /* Heartbeat, so clients detect we're still here */ - sm->directory_vector[STAT_COUNTER_HEARTBEAT].value++; -} - -/* - * Accept connection on the socket and exchange the fd for the shared - * memory segment. - */ -static clib_error_t * -stats_socket_accept_ready (clib_file_t * uf) -{ - stat_segment_main_t *sm = &stat_segment_main; - clib_error_t *err; - clib_socket_t client = { 0 }; - - err = clib_socket_accept (sm->socket, &client); - if (err) - { - clib_error_report (err); - return err; - } - - /* Send the fd across and close */ - err = clib_socket_sendmsg (&client, 0, 0, &sm->memfd, 1); - if (err) - clib_error_report (err); - clib_socket_close (&client); - - return 0; -} - -static clib_error_t * -stats_segment_socket_init (void) -{ - stat_segment_main_t *sm = &stat_segment_main; - clib_error_t *error; - clib_socket_t *s = clib_mem_alloc (sizeof (clib_socket_t)); - - memset (s, 0, sizeof (clib_socket_t)); - s->config = (char *) sm->socket_name; - s->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_SEQPACKET | - CLIB_SOCKET_F_ALLOW_GROUP_WRITE | CLIB_SOCKET_F_PASSCRED; - - if ((error = clib_socket_init (s))) - return error; - - clib_file_t template = { 0 }; - template.read_function = stats_socket_accept_ready; - template.file_descriptor = s->fd; - template.description = format (0, "stats segment listener %s", s->config); - clib_file_add (&file_main, &template); - - sm->socket = s; - - return 0; -} - -static clib_error_t * -stats_segment_socket_exit (vlib_main_t * vm) -{ - /* - * cleanup the listener socket on exit. - */ - stat_segment_main_t *sm = &stat_segment_main; - unlink ((char *) sm->socket_name); - return 0; -} - -VLIB_MAIN_LOOP_EXIT_FUNCTION (stats_segment_socket_exit); - -/* Overrides weak reference in vlib:node_cli.c */ -f64 -vlib_get_stat_segment_update_rate (void) -{ - return stat_segment_main.update_interval; -} - -static uword -stat_segment_collector_process (vlib_main_t * vm, vlib_node_runtime_t * rt, - vlib_frame_t * f) -{ - stat_segment_main_t *sm = &stat_segment_main; - - while (1) - { - do_stat_segment_updates (vm, sm); - vlib_process_suspend (vm, sm->update_interval); - } - return 0; /* or not */ -} - -/* - * Add a data provider (via callback) for a given stats entry. - * TODO: Add support for per-provider interval. - */ -void -stat_segment_poll_add (u32 vector_index, stat_segment_update_fn update_fn, - u32 caller_index, u32 interval) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_gauges_pool_t *gauge; - - pool_get (sm->gauges, gauge); - gauge->fn = update_fn; - gauge->caller_index = caller_index; - gauge->directory_index = vector_index; - - return; -} - -/* - * Create an scalar entry with a data provider. - * Deprecated, replace with stat_segment_new_entry + stat_segment_pool_add - */ -clib_error_t * -stat_segment_register_gauge (u8 *name, stat_segment_update_fn update_fn, - u32 caller_index) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_gauges_pool_t *gauge; - - u32 vector_index = stat_segment_new_entry (name, STAT_DIR_TYPE_SCALAR_INDEX); - if (vector_index == ~0) /* Already registered */ - return clib_error_return (0, "%v is already registered", name); - - pool_get (sm->gauges, gauge); - gauge->fn = update_fn; - gauge->caller_index = caller_index; - gauge->directory_index = vector_index; - - return NULL; -} - -clib_error_t * -stat_segment_register_state_counter (u8 * name, u32 * index) -{ - ASSERT (vlib_get_thread_index () == 0); - - u32 vector_index = stat_segment_new_entry (name, STAT_DIR_TYPE_SCALAR_INDEX); - if (vector_index == ~0) /* Already registered */ - return clib_error_return (0, "%v is already registered", name); - *index = vector_index; - return 0; -} - -clib_error_t * -stat_segment_deregister_state_counter (u32 index) -{ - stat_segment_main_t *sm = &stat_segment_main; - stat_segment_shared_header_t *shared_header = sm->shared_header; - stat_segment_directory_entry_t *e; - void *oldheap; - - ASSERT (shared_header); - - if (index > vec_len (sm->directory_vector)) - return clib_error_return (0, "%u index does not exist", index); - - e = &sm->directory_vector[index]; - if (e->type != STAT_DIR_TYPE_SCALAR_INDEX) - return clib_error_return (0, "%u index cannot be deleted", index); - - oldheap = vlib_stats_push_heap (NULL); - vlib_stat_segment_lock (); - - vlib_stats_delete_counter (index, oldheap); - - vlib_stat_segment_unlock (); - clib_mem_set_heap (oldheap); - - return 0; -} - -void -stat_segment_set_state_counter (u32 index, u64 value) -{ - stat_segment_main_t *sm = &stat_segment_main; - - ASSERT (index < vec_len (sm->directory_vector)); - sm->directory_vector[index].index = value; -} - -static clib_error_t * -statseg_config (vlib_main_t * vm, unformat_input_t * input) -{ - stat_segment_main_t *sm = &stat_segment_main; - sm->update_interval = 10.0; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "socket-name %s", &sm->socket_name)) - ; - /* DEPRECATE: default (does nothing) */ - else if (unformat (input, "default")) - ; - else if (unformat (input, "size %U", - unformat_memory_size, &sm->memory_size)) - ; - else if (unformat (input, "page-size %U", - unformat_log2_page_size, &sm->log2_page_sz)) - ; - else if (unformat (input, "per-node-counters on")) - sm->node_counters_enabled = 1; - else if (unformat (input, "per-node-counters off")) - sm->node_counters_enabled = 0; - else if (unformat (input, "update-interval %f", &sm->update_interval)) - ; - else - return clib_error_return (0, "unknown input `%U'", - format_unformat_error, input); - } - - /* - * NULL-terminate socket name string - * clib_socket_init()->socket_config() use C str* - */ - if (vec_len (sm->socket_name)) - vec_terminate_c_string (sm->socket_name); - - return 0; -} - -VLIB_EARLY_CONFIG_FUNCTION (statseg_config, "statseg"); - -static clib_error_t * -statseg_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add) -{ - stat_segment_main_t *sm = &stat_segment_main; - vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); - vnet_sw_interface_t *si_sup = - vnet_get_sup_sw_interface (vnm, si->sw_if_index); - vnet_hw_interface_t *hi_sup; - u8 *s; - u8 *symlink_name = 0; - u32 vector_index; - - void *oldheap = vlib_stats_push_heap (sm->interfaces); - vlib_stat_segment_lock (); - - vec_validate (sm->interfaces, sw_if_index); - - ASSERT (si_sup->type == VNET_SW_INTERFACE_TYPE_HARDWARE); - hi_sup = vnet_get_hw_interface (vnm, si_sup->hw_if_index); - - s = format (0, "%v", hi_sup->name); - if (si->type != VNET_SW_INTERFACE_TYPE_HARDWARE) - s = format (s, ".%d", si->sub.id); - s = format (s, "%c", 0); - - if (is_add) - { - sm->interfaces[sw_if_index] = s; -#define _(E, n, p) \ - clib_mem_set_heap (oldheap); /* Exit stats segment */ \ - vector_index = lookup_hash_index ((u8 *) "/" #p "/" #n); \ - clib_mem_set_heap (sm->heap); /* Re-enter stat segment */ \ - vec_reset_length (symlink_name); \ - symlink_name = format (symlink_name, "/interfaces/%U/" #n "%c", \ - format_vlib_stats_symlink, s, 0); \ - vlib_stats_register_symlink (oldheap, symlink_name, vector_index, \ - sw_if_index, 0 /* don't lock */); - foreach_simple_interface_counter_name - foreach_combined_interface_counter_name -#undef _ - vec_free (symlink_name); - } - else - { - vec_free (sm->interfaces[sw_if_index]); - sm->interfaces[sw_if_index] = 0; -#define _(E, n, p) \ - vec_reset_length (symlink_name); \ - symlink_name = format (symlink_name, "/interfaces/%U/" #n "%c", \ - format_vlib_stats_symlink, s, 0); \ - clib_mem_set_heap (oldheap); /* Exit stats segment */ \ - vector_index = lookup_hash_index ((u8 *) symlink_name); \ - clib_mem_set_heap (sm->heap); /* Re-enter stat segment */ \ - vlib_stats_delete_counter (vector_index, oldheap); - foreach_simple_interface_counter_name - foreach_combined_interface_counter_name -#undef _ - - vec_free (symlink_name); - vec_free (s); - } - - stat_segment_directory_entry_t *ep; - ep = &sm->directory_vector[STAT_COUNTER_INTERFACE_NAMES]; - ep->data = sm->interfaces; - - vlib_stat_segment_unlock (); - clib_mem_set_heap (oldheap); - - return 0; -} - -VNET_SW_INTERFACE_ADD_DEL_FUNCTION (statseg_sw_interface_add_del); - -VLIB_REGISTER_NODE (stat_segment_collector, static) = -{ -.function = stat_segment_collector_process, -.name = "statseg-collector-process", -.type = VLIB_NODE_TYPE_PROCESS, -}; - -static clib_error_t * -statseg_init (vlib_main_t *vm) -{ - stat_segment_main_t *sm = &stat_segment_main; - - /* set default socket file name when statseg config stanza is empty. */ - if (!vec_len (sm->socket_name)) - sm->socket_name = format (0, "%s/%s%c", vlib_unix_get_runtime_dir (), - STAT_SEGMENT_SOCKET_FILENAME, 0); - return stats_segment_socket_init (); -} - -VLIB_INIT_FUNCTION (statseg_init) = { - .runs_after = VLIB_INITS ("unix_input_init"), -}; - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/vpp/stats/stat_segment.h b/src/vpp/stats/stat_segment.h deleted file mode 100644 index f5862a684e4..00000000000 --- a/src/vpp/stats/stat_segment.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef included_stat_segment_h -#define included_stat_segment_h - -#include -#include -#include - -typedef enum -{ - STAT_COUNTER_NUM_WORKER_THREADS = 0, - STAT_COUNTER_INPUT_RATE, - STAT_COUNTER_LAST_UPDATE, - STAT_COUNTER_LAST_STATS_CLEAR, - STAT_COUNTER_HEARTBEAT, - STAT_COUNTER_NODE_CLOCKS, - STAT_COUNTER_NODE_VECTORS, - STAT_COUNTER_NODE_CALLS, - STAT_COUNTER_NODE_SUSPENDS, - STAT_COUNTER_INTERFACE_NAMES, - STAT_COUNTER_NODE_NAMES, - STAT_COUNTERS -} stat_segment_counter_t; - -/* clang-format off */ -#define foreach_stat_segment_node_counter_name \ - _ (NODE_CLOCKS, COUNTER_VECTOR_SIMPLE, clocks, /sys/node) \ - _ (NODE_VECTORS, COUNTER_VECTOR_SIMPLE, vectors, /sys/node) \ - _ (NODE_CALLS, COUNTER_VECTOR_SIMPLE, calls, /sys/node) \ - _ (NODE_SUSPENDS, COUNTER_VECTOR_SIMPLE, suspends, /sys/node) - -#define foreach_stat_segment_counter_name \ - _ (NUM_WORKER_THREADS, SCALAR_INDEX, num_worker_threads, /sys) \ - _ (INPUT_RATE, SCALAR_INDEX, input_rate, /sys) \ - _ (LAST_UPDATE, SCALAR_INDEX, last_update, /sys) \ - _ (LAST_STATS_CLEAR, SCALAR_INDEX, last_stats_clear, /sys) \ - _ (HEARTBEAT, SCALAR_INDEX, heartbeat, /sys) \ - _ (INTERFACE_NAMES, NAME_VECTOR, names, /if) \ - _ (NODE_NAMES, NAME_VECTOR, names, /sys/node) \ - foreach_stat_segment_node_counter_name -/* clang-format on */ - -/* Default stat segment 32m */ -#define STAT_SEGMENT_DEFAULT_SIZE (32<<20) - -/* Shared segment memory layout version */ -#define STAT_SEGMENT_VERSION 2 - -#define STAT_SEGMENT_INDEX_INVALID UINT32_MAX - -typedef void (*stat_segment_update_fn)(stat_segment_directory_entry_t * e, u32 i); - -typedef struct { - u32 directory_index; - stat_segment_update_fn fn; - u32 caller_index; -} stat_segment_gauges_pool_t; - -typedef struct -{ - /* internal, does not point to shared memory */ - stat_segment_gauges_pool_t *gauges; - - /* statistics segment */ - uword *directory_vector_by_name; - stat_segment_directory_entry_t *directory_vector; - volatile u64 **error_vector; - u8 **interfaces; - u8 **nodes; - - /* Update interval */ - f64 update_interval; - - clib_spinlock_t *stat_segment_lockp; - clib_socket_t *socket; - u8 *socket_name; - ssize_t memory_size; - clib_mem_page_sz_t log2_page_sz; - u8 node_counters_enabled; - void *last; - void *heap; - stat_segment_shared_header_t *shared_header; /* pointer to shared memory segment */ - int memfd; - - u64 last_input_packets; // OLE REMOVE? -} stat_segment_main_t; - -extern stat_segment_main_t stat_segment_main; - -clib_error_t * -stat_segment_register_gauge (u8 *names, stat_segment_update_fn update_fn, u32 index); -clib_error_t * -stat_segment_register_state_counter(u8 *name, u32 *index); -clib_error_t * -stat_segment_deregister_state_counter(u32 index); -void stat_segment_set_state_counter (u32 index, u64 value); -void stat_segment_poll_add (u32 vector_index, stat_segment_update_fn update_fn, - u32 caller_index, u32 interval); - -u32 stat_segment_new_entry (u8 *name, stat_directory_type_t t); -void vlib_stats_register_mem_heap (clib_mem_heap_t *heap); -void vlib_stat_segment_lock (void); -void vlib_stat_segment_unlock (void); -void vlib_stats_register_symlink (void *oldheap, u8 *name, u32 index1, - u32 index2, u8 lock); - -void stat_provider_register_vector_rate (u32 num_workers); - -#endif diff --git a/src/vpp/stats/stat_segment_provider.c b/src/vpp/stats/stat_segment_provider.c deleted file mode 100644 index 766261ce0e0..00000000000 --- a/src/vpp/stats/stat_segment_provider.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2021 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Counters handled by the stats module directly. - */ - -#include -#include -#include -#include -#include -#include "stat_segment.h" - -clib_mem_heap_t **memory_heaps_vec; -u32 mem_vector_index; -bool initialized = false; - -enum -{ - STAT_MEM_TOTAL = 0, - STAT_MEM_USED, - STAT_MEM_FREE, - STAT_MEM_USED_MMAP, - STAT_MEM_TOTAL_ALLOC, - STAT_MEM_FREE_CHUNKS, - STAT_MEM_RELEASABLE, -} stat_mem_usage_e; - -/* - * Called from the stats periodic process to update memory counters. - */ -static void -stat_provider_mem_usage_update_fn (stat_segment_directory_entry_t *e, - u32 index) -{ - clib_mem_usage_t usage; - clib_mem_heap_t *heap; - counter_t **counters = e->data; - counter_t *cb; - - heap = vec_elt (memory_heaps_vec, index); - clib_mem_get_heap_usage (heap, &usage); - cb = counters[0]; - cb[STAT_MEM_TOTAL] = usage.bytes_total; - cb[STAT_MEM_USED] = usage.bytes_used; - cb[STAT_MEM_FREE] = usage.bytes_free; - cb[STAT_MEM_USED_MMAP] = usage.bytes_used_mmap; - cb[STAT_MEM_TOTAL_ALLOC] = usage.bytes_max; - cb[STAT_MEM_FREE_CHUNKS] = usage.bytes_free_reclaimed; - cb[STAT_MEM_RELEASABLE] = usage.bytes_overhead; -} - -static counter_t ** -stat_validate_counter_vector3 (counter_t **counters, u32 max1, u32 max2) -{ - stat_segment_main_t *sm = &stat_segment_main; - int i; - void *oldheap = clib_mem_set_heap (sm->heap); - vec_validate_aligned (counters, max1, CLIB_CACHE_LINE_BYTES); - for (i = 0; i <= max1; i++) - vec_validate_aligned (counters[i], max2, CLIB_CACHE_LINE_BYTES); - clib_mem_set_heap (oldheap); - return counters; -} - -/* - * Provide memory heap counters. - * Two dimensional array of heap index and per-heap gauges. - */ -void -vlib_stats_register_mem_heap (clib_mem_heap_t *heap) -{ - stat_segment_main_t *sm = &stat_segment_main; - vec_add1 (memory_heaps_vec, heap); - u32 heap_index = vec_len (memory_heaps_vec) - 1; - - /* Memory counters provider */ - u8 *s = format (0, "/mem/%s%c", heap->name, 0); - u8 *s_used = format (0, "/mem/%s/used%c", heap->name, 0); - u8 *s_total = format (0, "/mem/%s/total%c", heap->name, 0); - u8 *s_free = format (0, "/mem/%s/free%c", heap->name, 0); - mem_vector_index = - stat_segment_new_entry (s, STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE); - vec_free (s); - if (mem_vector_index == ~0) - ASSERT (0); - - vlib_stat_segment_lock (); - stat_segment_directory_entry_t *ep = &sm->directory_vector[mem_vector_index]; - ep->data = stat_validate_counter_vector3 (ep->data, 0, STAT_MEM_RELEASABLE); - - /* Create symlink */ - void *oldheap = clib_mem_set_heap (sm->heap); - vlib_stats_register_symlink (oldheap, s_total, mem_vector_index, - STAT_MEM_TOTAL, 0); - vlib_stats_register_symlink (oldheap, s_used, mem_vector_index, - STAT_MEM_USED, 0); - vlib_stats_register_symlink (oldheap, s_free, mem_vector_index, - STAT_MEM_FREE, 0); - vlib_stat_segment_unlock (); - clib_mem_set_heap (oldheap); - vec_free (s_used); - vec_free (s_total); - vec_free (s_free); - - stat_segment_poll_add (mem_vector_index, stat_provider_mem_usage_update_fn, - heap_index, 10); -} - -static void -stat_provider_vector_rate_per_thread_update_fn ( - stat_segment_directory_entry_t *e, u32 index) -{ - vlib_main_t *this_vlib_main; - int i; - ASSERT (e->data); - counter_t **counters = e->data; - - for (i = 0; i < vlib_get_n_threads (); i++) - { - - f64 this_vector_rate; - - this_vlib_main = vlib_get_main_by_index (i); - - this_vector_rate = vlib_internal_node_vector_rate (this_vlib_main); - vlib_clear_internal_node_vector_rate (this_vlib_main); - /* Set the per-worker rate */ - counter_t *cb = counters[i]; - cb[0] = this_vector_rate; - } -} - -static void -stat_provider_vector_rate_update_fn (stat_segment_directory_entry_t *e, - u32 index) -{ - vlib_main_t *this_vlib_main; - int i; - f64 vector_rate = 0.0; - for (i = 0; i < vlib_get_n_threads (); i++) - { - - f64 this_vector_rate; - - this_vlib_main = vlib_get_main_by_index (i); - - this_vector_rate = vlib_internal_node_vector_rate (this_vlib_main); - vlib_clear_internal_node_vector_rate (this_vlib_main); - - vector_rate += this_vector_rate; - } - - /* And set the system average rate */ - vector_rate /= (f64) (i > 1 ? i - 1 : 1); - e->value = vector_rate; -} - -void -stat_provider_register_vector_rate (u32 num_workers) -{ - int i; - - u8 *s = format (0, "/sys/vector_rate%c", 0); - - i = stat_segment_new_entry (s, STAT_DIR_TYPE_SCALAR_INDEX); - if (i == ~0) - ASSERT (0); - vec_free (s); - stat_segment_poll_add (i, stat_provider_vector_rate_update_fn, ~0, 10); - - s = format (0, "/sys/vector_rate_per_worker%c", 0); - i = stat_segment_new_entry (s, STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE); - if (i == ~0) - ASSERT (0); - vec_free (s); - stat_segment_poll_add (i, stat_provider_vector_rate_per_thread_update_fn, ~0, - 10); - - stat_segment_main_t *sm = &stat_segment_main; - vlib_stat_segment_lock (); - stat_segment_directory_entry_t *ep = &sm->directory_vector[i]; - ep->data = stat_validate_counter_vector3 (ep->data, num_workers, 0); - vlib_stat_segment_unlock (); -} diff --git a/src/vpp/stats/stat_segment_shared.h b/src/vpp/stats/stat_segment_shared.h deleted file mode 100644 index 6f26d4b9fdf..00000000000 --- a/src/vpp/stats/stat_segment_shared.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef included_stat_segment_shared_h -#define included_stat_segment_shared_h - -typedef enum -{ - STAT_DIR_TYPE_ILLEGAL = 0, - STAT_DIR_TYPE_SCALAR_INDEX, - STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE, - STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED, - STAT_DIR_TYPE_ERROR_INDEX, - STAT_DIR_TYPE_NAME_VECTOR, - STAT_DIR_TYPE_EMPTY, - STAT_DIR_TYPE_SYMLINK, -} stat_directory_type_t; - -typedef struct -{ - stat_directory_type_t type; - union { - struct - { - uint32_t index1; - uint32_t index2; - }; - uint64_t index; - uint64_t value; - void *data; - }; - char name[128]; // TODO change this to pointer to "somewhere" -} stat_segment_directory_entry_t; - -/* - * Shared header first in the shared memory segment. - */ -typedef struct -{ - uint64_t version; - void *base; - volatile uint64_t epoch; - volatile uint64_t in_progress; - volatile stat_segment_directory_entry_t *directory_vector; - volatile uint64_t **error_vector; -} stat_segment_shared_header_t; - -static inline void * -stat_segment_pointer (void *start, uint64_t offset) -{ - return ((char *) start + offset); -} - -#endif /* included_stat_segment_shared_h */ -- cgit 1.2.3-korg