aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/vat/api_format.c169
-rw-r--r--src/vat/vat.h3
-rw-r--r--src/vcl.am1
-rw-r--r--src/vlib.am9
-rw-r--r--src/vlib/counter.c20
-rw-r--r--src/vlib/counter.h2
-rw-r--r--src/vlib/error.c37
-rw-r--r--src/vlib/main.c24
-rw-r--r--src/vlibmemory/memory_api.c16
-rw-r--r--src/vlibmemory/vlib_api.c6
-rw-r--r--src/vnet/interface.c4
-rw-r--r--src/vpp.am19
-rw-r--r--src/vpp/app/stat_client.c343
-rw-r--r--src/vpp/app/stat_client.h70
-rw-r--r--src/vpp/stats/stat_segment.c425
-rw-r--r--src/vpp/stats/stats.api9
-rw-r--r--src/vpp/stats/stats.c67
-rw-r--r--src/vpp/stats/stats.h29
18 files changed, 1221 insertions, 32 deletions
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index f17802d778f..161e309d1fe 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -5884,7 +5884,8 @@ _(SESSION_RULE_ADD_DEL_REPLY, session_rule_add_del_reply) \
_(SESSION_RULES_DETAILS, session_rules_details) \
_(IP_CONTAINER_PROXY_ADD_DEL_REPLY, ip_container_proxy_add_del_reply) \
_(OUTPUT_ACL_SET_INTERFACE_REPLY, output_acl_set_interface_reply) \
-_(QOS_RECORD_ENABLE_DISABLE_REPLY, qos_record_enable_disable_reply)
+_(QOS_RECORD_ENABLE_DISABLE_REPLY, qos_record_enable_disable_reply) \
+_(MAP_STATS_SEGMENT_REPLY, map_stats_segment_reply)
#define foreach_standalone_reply_msg \
_(SW_INTERFACE_EVENT, sw_interface_event) \
@@ -22431,6 +22432,92 @@ api_app_namespace_add_del (vat_main_t * vam)
return ret;
}
+static void vl_api_map_stats_segment_reply_t_handler
+ (vl_api_map_stats_segment_reply_t * mp)
+{
+#if VPP_API_TEST_BUILTIN == 0
+ vat_main_t *vam = &vat_main;
+ ssvm_private_t *ssvmp = &vam->stat_segment;
+ ssvm_shared_header_t *shared_header;
+ socket_client_main_t *scm = vam->socket_client_main;
+ int rv = ntohl (mp->retval);
+ int my_fd, retval;
+ clib_error_t *error;
+
+ vam->retval = rv;
+
+ if (rv != 0)
+ {
+ vam->result_ready = 1;
+ return;
+ }
+
+ /*
+ * Check the socket for the magic fd
+ */
+ error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 5);
+ if (error)
+ {
+ clib_error_report (error);
+ vam->retval = -99;
+ vam->result_ready = 1;
+ return;
+ }
+
+ memset (ssvmp, 0, sizeof (*ssvmp));
+ ssvmp->fd = my_fd;
+
+ /* Note: this closes memfd.fd */
+ retval = ssvm_slave_init_memfd (ssvmp);
+ if (retval)
+ {
+ clib_warning ("WARNING: segment map returned %d", retval);
+ vam->retval = -99;
+ vam->result_ready = 1;
+ return;
+ }
+ else
+ errmsg ("stat segment mapped OK...");
+
+ ASSERT (ssvmp && ssvmp->sh);
+
+ /* Pick up the segment lock from the shared memory header */
+ shared_header = ssvmp->sh;
+ vam->stat_segment_lockp = (clib_spinlock_t *) (shared_header->opaque[0]);
+ vam->retval = 0;
+ vam->result_ready = 1;
+#endif
+}
+
+static void vl_api_map_stats_segment_reply_t_handler_json
+ (vl_api_map_stats_segment_reply_t * mp)
+{
+#if VPP_API_TEST_BUILTIN == 0
+ vat_main_t *vam = &vat_main;
+ clib_warning ("not implemented");
+ vam->retval = -99;
+ vam->result_ready = 1;
+#endif
+}
+
+static int
+api_map_stats_segment (vat_main_t * vam)
+{
+#if VPP_API_TEST_BUILTIN == 0
+ vl_api_map_stats_segment_t *mp;
+ int ret;
+
+ M (MAP_STATS_SEGMENT, mp);
+ S (mp);
+ W (ret);
+
+ return ret;
+#else
+ errmsg ("api unavailable");
+ return -99;
+#endif
+}
+
static int
api_sock_init_shm (vat_main_t * vam)
{
@@ -22956,6 +23043,7 @@ api_qos_record_enable_disable (vat_main_t * vam)
return ret;
}
+
static int
q_or_quit (vat_main_t * vam)
{
@@ -22984,6 +23072,80 @@ comment (vat_main_t * vam)
}
static int
+statseg (vat_main_t * vam)
+{
+ ssvm_private_t *ssvmp = &vam->stat_segment;
+ ssvm_shared_header_t *shared_header = ssvmp->sh;
+ vlib_counter_t **counters;
+ u64 thread0_index1_packets;
+ u64 thread0_index1_bytes;
+ f64 vector_rate, input_rate;
+ uword *p;
+
+ uword *counter_vector_by_name;
+ if (vam->stat_segment_lockp == 0)
+ {
+ errmsg ("Stat segment not mapped...");
+ return -99;
+ }
+
+ /* look up "/if/rx for sw_if_index 1 as a test */
+
+ clib_spinlock_lock (vam->stat_segment_lockp);
+
+ counter_vector_by_name = (uword *) shared_header->opaque[1];
+
+ p = hash_get_mem (counter_vector_by_name, "/if/rx");
+ if (p == 0)
+ {
+ clib_spinlock_unlock (vam->stat_segment_lockp);
+ errmsg ("/if/tx not found?");
+ return -99;
+ }
+
+ /* Fish per-thread vector of combined counters from shared memory */
+ counters = (vlib_counter_t **) p[0];
+
+ if (vec_len (counters[0]) < 2)
+ {
+ clib_spinlock_unlock (vam->stat_segment_lockp);
+ errmsg ("/if/tx vector length %d", vec_len (counters[0]));
+ return -99;
+ }
+
+ /* Read thread 0 sw_if_index 1 counter */
+ thread0_index1_packets = counters[0][1].packets;
+ thread0_index1_bytes = counters[0][1].bytes;
+
+ p = hash_get_mem (counter_vector_by_name, "vector_rate");
+ if (p == 0)
+ {
+ clib_spinlock_unlock (vam->stat_segment_lockp);
+ errmsg ("vector_rate not found?");
+ return -99;
+ }
+
+ vector_rate = *(f64 *) (p[0]);
+ p = hash_get_mem (counter_vector_by_name, "input_rate");
+ if (p == 0)
+ {
+ clib_spinlock_unlock (vam->stat_segment_lockp);
+ errmsg ("input_rate not found?");
+ return -99;
+ }
+ input_rate = *(f64 *) (p[0]);
+
+ clib_spinlock_unlock (vam->stat_segment_lockp);
+
+ print (vam->ofp, "vector_rate %.2f input_rate %.2f",
+ vector_rate, input_rate);
+ print (vam->ofp, "thread 0 sw_if_index 1 rx pkts %lld, bytes %lld",
+ thread0_index1_packets, thread0_index1_bytes);
+
+ return 0;
+}
+
+static int
cmd_cmp (void *a1, void *a2)
{
u8 **c1 = a1;
@@ -23799,7 +23961,8 @@ _(ip_container_proxy_add_del, "[add|del] <address> <sw_if_index>") \
_(output_acl_set_interface, \
"<intfc> | sw_if_index <nn> [ip4-table <nn>] [ip6-table <nn>]\n" \
" [l2-table <nn>] [del]") \
-_(qos_record_enable_disable, "<record-source> <intfc> | sw_if_index <id> [disable]")
+_(qos_record_enable_disable, "<record-source> <intfc> | sw_if_index <id> [disable]") \
+_(map_stats_segment, "<no-args>")
/* List of command functions, CLI names map directly to functions */
#define foreach_cli_function \
@@ -23822,7 +23985,9 @@ _(quit, "usage: quit") \
_(search_node_table, "usage: search_node_table <name>...") \
_(set, "usage: set <variable-name> <value>") \
_(script, "usage: script <file-name>") \
+_(statseg, "usage: statseg"); \
_(unset, "usage: unset <variable-name>")
+
#define _(N,n) \
static void vl_api_##n##_t_handler_uni \
(vl_api_##n##_t * mp) \
diff --git a/src/vat/vat.h b/src/vat/vat.h
index beeccd5dc67..19796b92ef3 100644
--- a/src/vat/vat.h
+++ b/src/vat/vat.h
@@ -209,6 +209,9 @@ typedef struct
ip4_nbr_counter_t **ip4_nbr_counters;
ip6_nbr_counter_t **ip6_nbr_counters;
+ ssvm_private_t stat_segment;
+ clib_spinlock_t *stat_segment_lockp;
+
socket_client_main_t *socket_client_main;
u8 *socket_name;
diff --git a/src/vcl.am b/src/vcl.am
index 89e18416b1e..ccb323a033b 100644
--- a/src/vcl.am
+++ b/src/vcl.am
@@ -25,7 +25,6 @@ libvppcom_la_SOURCES += \
vcl/vcl_event.c \
vcl/vppcom.c \
$(libvppinfra_la_SOURCES) \
- $(libvlib_la_SOURCES) \
$(libsvm_la_SOURCES) \
$(libvlibmemoryclient_la_SOURCES)
diff --git a/src/vlib.am b/src/vlib.am
index b1e6bbe844a..8756f9b74c8 100644
--- a/src/vlib.am
+++ b/src/vlib.am
@@ -87,13 +87,4 @@ nobase_include_HEADERS += \
vlib/unix/plugin.h \
vlib/unix/unix.h
-noinst_PROGRAMS += vlib_unix
-
-vlib_unix_SOURCES = \
- examples/vlib/main_stub.c \
- examples/vlib/mc_test.c
-
-vlib_unix_LDADD = libvlib.la \
- libvppinfra.la -lpthread -lm -ldl -lrt
-
# vi:syntax=automake
diff --git a/src/vlib/counter.c b/src/vlib/counter.c
index 62f4bd66ddc..29cd004fc3e 100644
--- a/src/vlib/counter.c
+++ b/src/vlib/counter.c
@@ -74,15 +74,32 @@ vlib_clear_combined_counters (vlib_combined_counter_main_t * cm)
}
}
+void *vlib_stats_push_heap (void) __attribute__ ((weak));
+void *
+vlib_stats_push_heap (void)
+{
+ return 0;
+};
+
+void vlib_stats_pop_heap (void *, void *) __attribute__ ((weak));
+void
+vlib_stats_pop_heap (void *notused, void *notused2)
+{
+};
+
+
void
vlib_validate_simple_counter (vlib_simple_counter_main_t * cm, u32 index)
{
vlib_thread_main_t *tm = vlib_get_thread_main ();
int i;
+ void *oldheap = vlib_stats_push_heap ();
vec_validate (cm->counters, tm->n_vlib_mains - 1);
for (i = 0; i < tm->n_vlib_mains; i++)
vec_validate_aligned (cm->counters[i], index, CLIB_CACHE_LINE_BYTES);
+
+ vlib_stats_pop_heap (cm, oldheap);
}
void
@@ -90,10 +107,13 @@ vlib_validate_combined_counter (vlib_combined_counter_main_t * cm, u32 index)
{
vlib_thread_main_t *tm = vlib_get_thread_main ();
int i;
+ void *oldheap = vlib_stats_push_heap ();
vec_validate (cm->counters, tm->n_vlib_mains - 1);
for (i = 0; i < tm->n_vlib_mains; i++)
vec_validate_aligned (cm->counters[i], index, CLIB_CACHE_LINE_BYTES);
+
+ vlib_stats_pop_heap (cm, oldheap);
}
u32
diff --git a/src/vlib/counter.h b/src/vlib/counter.h
index 60e2055d232..fe5279a5e28 100644
--- a/src/vlib/counter.h
+++ b/src/vlib/counter.h
@@ -63,6 +63,7 @@ typedef struct
serialized incrementally. */
char *name; /**< The counter collection's name. */
+ char *stat_segment_name; /**< Name in stat segment directory */
} vlib_simple_counter_main_t;
/** The number of counters (not the number of per-thread counters) */
@@ -183,6 +184,7 @@ typedef struct
vlib_counter_t *value_at_last_serialize; /**< Counter values as of last serialize. */
u32 last_incremental_serialize_index; /**< Last counter index serialized incrementally. */
char *name; /**< The counter collection's name. */
+ char *stat_segment_name; /**< Name in stat segment directory */
} vlib_combined_counter_main_t;
/** The number of counters (not the number of per-thread counters) */
diff --git a/src/vlib/error.c b/src/vlib/error.c
index dec90bbe440..368118210e6 100644
--- a/src/vlib/error.c
+++ b/src/vlib/error.c
@@ -140,6 +140,19 @@ VLIB_REGISTER_NODE (misc_drop_buffers_node,static) = {
};
/* *INDENT-ON* */
+void vlib_stats_register_error_index (u8 *, u64) __attribute__ ((weak));
+void
+vlib_stats_register_error_index (u8 * notused, u64 notused2)
+{
+};
+
+void vlib_stats_pop_heap2 (void *, u32, void *) __attribute__ ((weak));
+void
+vlib_stats_pop_heap2 (void *notused, u32 notused2, void *notused3)
+{
+};
+
+
/* Reserves given number of error codes for given node. */
void
vlib_register_errors (vlib_main_t * vm,
@@ -148,6 +161,8 @@ vlib_register_errors (vlib_main_t * vm,
vlib_error_main_t *em = &vm->error_main;
vlib_node_t *n = vlib_get_node (vm, node_index);
uword l;
+ void *oldheap;
+ void *vlib_stats_push_heap (void) __attribute__ ((weak));
ASSERT (vlib_get_thread_index () == 0);
@@ -169,9 +184,13 @@ vlib_register_errors (vlib_main_t * vm,
clib_memcpy (vec_elt_at_index (em->error_strings_heap, n->error_heap_index),
error_strings, n_errors * sizeof (error_strings[0]));
+ vec_validate (vm->error_elog_event_types, l - 1);
+
+ /* Switch to the stats segment ... */
+ oldheap = vlib_stats_push_heap ();
+
/* Allocate a counter/elog type for each error. */
vec_validate (em->counters, l - 1);
- vec_validate (vm->error_elog_event_types, l - 1);
/* Zero counters for re-registrations of errors. */
if (n->error_heap_index + n_errors <= vec_len (em->counters_last_clear))
@@ -182,6 +201,22 @@ vlib_register_errors (vlib_main_t * vm,
memset (em->counters + n->error_heap_index,
0, n_errors * sizeof (em->counters[0]));
+ /* Register counter indices in the stat segment directory */
+ {
+ int i;
+ u8 *error_name;
+
+ for (i = 0; i < n_errors; i++)
+ {
+ error_name = format (0, "/err/%s%c", error_strings[i], 0);
+ /* Note: error_name consumed by the following call */
+ vlib_stats_register_error_index (error_name, n->error_heap_index + i);
+ }
+ }
+
+ /* (re)register the em->counters base address, switch back to main heap */
+ vlib_stats_pop_heap2 (em->counters, vm->thread_index, oldheap);
+
{
elog_event_type_t t;
uword i;
diff --git a/src/vlib/main.c b/src/vlib/main.c
index 7da519241bb..e4c4438b5aa 100644
--- a/src/vlib/main.c
+++ b/src/vlib/main.c
@@ -1717,6 +1717,12 @@ vlib_main (vlib_main_t * volatile vm, unformat_input_t * input)
goto done;
}
+ if ((error = vlib_call_init_function (vm, map_stat_segment_init)))
+ {
+ clib_error_report (error);
+ goto done;
+ }
+
/* Register static nodes so that init functions may use them. */
vlib_register_all_static_nodes (vm);
@@ -1736,6 +1742,24 @@ vlib_main (vlib_main_t * volatile vm, unformat_input_t * input)
goto done;
}
+ if ((error = vlib_call_init_function (vm, vpe_api_init)))
+ {
+ clib_error_report (error);
+ goto done;
+ }
+
+ if ((error = vlib_call_init_function (vm, vlibmemory_init)))
+ {
+ clib_error_report (error);
+ goto done;
+ }
+
+ if ((error = vlib_call_init_function (vm, map_api_segment_init)))
+ {
+ clib_error_report (error);
+ goto done;
+ }
+
/* See unix/main.c; most likely already set up */
if (vm->init_functions_called == 0)
vm->init_functions_called = hash_create (0, /* value bytes */ 0);
diff --git a/src/vlibmemory/memory_api.c b/src/vlibmemory/memory_api.c
index 205bea57532..4d31b35f132 100644
--- a/src/vlibmemory/memory_api.c
+++ b/src/vlibmemory/memory_api.c
@@ -459,6 +459,22 @@ vl_mem_api_init (const char *region_name)
return 0;
}
+static clib_error_t *
+map_api_segment_init (vlib_main_t * vm)
+{
+ api_main_t *am = &api_main;
+ int rv;
+
+ if ((rv = vl_mem_api_init (am->region_name)) < 0)
+ {
+ return clib_error_return (0, "vl_mem_api_init (%s) failed",
+ am->region_name);
+ }
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (map_api_segment_init);
+
static void
send_memclnt_keepalive (vl_api_registration_t * regp, f64 now)
{
diff --git a/src/vlibmemory/vlib_api.c b/src/vlibmemory/vlib_api.c
index 1067bf3b4ba..eb7655ce1a7 100644
--- a/src/vlibmemory/vlib_api.c
+++ b/src/vlibmemory/vlib_api.c
@@ -287,12 +287,6 @@ vl_api_clnt_process (vlib_main_t * vm, vlib_node_runtime_t * node,
uword *event_data = 0;
f64 now;
- if ((rv = vl_mem_api_init (am->region_name)) < 0)
- {
- clib_warning ("memory_api_init returned %d, quitting...", rv);
- return 0;
- }
-
if ((error = vl_sock_api_init (vm)))
{
clib_error_report (error);
diff --git a/src/vnet/interface.c b/src/vnet/interface.c
index 9300074927d..797fe44419a 100644
--- a/src/vnet/interface.c
+++ b/src/vnet/interface.c
@@ -1250,6 +1250,10 @@ vnet_interface_init (vlib_main_t * vm)
CLIB_CACHE_LINE_BYTES);
im->sw_if_counter_lock[0] = 1; /* should be no need */
+ /*
+ * $$$$ add stat segment name(s) if desired
+ * set xxx.stat_segment_name = "whatever"...
+ */
vec_validate (im->sw_if_counters, VNET_N_SIMPLE_INTERFACE_COUNTER - 1);
im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP].name = "drops";
im->sw_if_counters[VNET_INTERFACE_COUNTER_PUNT].name = "punts";
diff --git a/src/vpp.am b/src/vpp.am
index 0e9ddb045c6..e8c01557656 100644
--- a/src/vpp.am
+++ b/src/vpp.am
@@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-bin_PROGRAMS += bin/vpp
+bin_PROGRAMS += bin/vpp
bin_vpp_SOURCES = \
vpp/vnet/main.c \
@@ -19,7 +19,8 @@ bin_vpp_SOURCES = \
vpp/app/version.c \
vpp/oam/oam.c \
vpp/oam/oam_api.c \
- vpp/stats/stats.c
+ vpp/stats/stats.c \
+ vpp/stats/stat_segment.c
bin_vpp_SOURCES += \
vpp/api/api.c \
@@ -130,6 +131,20 @@ bin_summary_stats_client_LDADD = \
libvppinfra.la \
-lpthread -lm -lrt
+noinst_PROGRAMS += bin/stat_client
+
+bin_stat_client_SOURCES = \
+ vpp/app/stat_client.c \
+ vpp/app/stat_client.h
+
+bin_stat_client_LDADD = \
+ libvlibmemoryclient.la \
+ libsvm.la \
+ libvppinfra.la \
+ -lpthread -lm -lrt
+
+
+
bin_PROGRAMS += bin/vpp_get_metrics
bin_vpp_get_metrics_SOURCES = \
diff --git a/src/vpp/app/stat_client.c b/src/vpp/app/stat_client.c
new file mode 100644
index 00000000000..610a6a5b98d
--- /dev/null
+++ b/src/vpp/app/stat_client.c
@@ -0,0 +1,343 @@
+/*
+ *------------------------------------------------------------------
+ * api_format.c
+ *
+ * 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 <vpp/app/stat_client.h>
+
+#include <vpp/api/vpe_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun /* define endian fcns */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) fformat (handle, __VA_ARGS__)
+#define vl_printfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_printfun
+
+stat_client_main_t stat_client_main;
+
+static void vl_api_map_stats_segment_reply_t_handler
+ (vl_api_map_stats_segment_reply_t * mp)
+{
+ stat_client_main_t *sm = &stat_client_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ ssvm_shared_header_t *shared_header;
+ socket_client_main_t *scm = sm->socket_client_main;
+ int rv = ntohl (mp->retval);
+ int my_fd, retval;
+ clib_error_t *error;
+
+ if (rv != 0)
+ {
+ fformat (stderr, "ERROR mapping stats segment: %d", rv);
+ exit (1);
+ }
+
+ /*
+ * Check the socket for the magic fd
+ */
+ error = vl_sock_api_recv_fd_msg (scm->socket_fd, &my_fd, 5);
+ if (error)
+ {
+ clib_error_report (error);
+ exit (1);
+ }
+
+ memset (ssvmp, 0, sizeof (*ssvmp));
+ ssvmp->fd = my_fd;
+
+ /* Note: this closes memfd.fd */
+ retval = ssvm_slave_init_memfd (ssvmp);
+ if (retval)
+ {
+ clib_warning ("WARNING: segment map returned %d", retval);
+ exit (1);
+ }
+
+ fformat (stdout, "Stat segment mapped OK...\n");
+
+ ASSERT (ssvmp && ssvmp->sh);
+
+ /* Pick up the segment lock from the shared memory header */
+ shared_header = ssvmp->sh;
+ sm->stat_segment_lockp = (clib_spinlock_t *) (shared_header->opaque[0]);
+ sm->segment_ready = 1;
+
+ /* No need to keep the socket API connection open */
+ close (sm->socket_client_main->socket_fd);
+}
+
+#define foreach_api_reply_msg \
+_(MAP_STATS_SEGMENT_REPLY, map_stats_segment_reply)
+
+static void
+vpp_api_hookup (void)
+{
+#define _(N,n) \
+ vl_msg_api_set_handlers(VL_API_##N, #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_api_reply_msg;
+#undef _
+}
+
+static int
+connect_to_vpp (stat_client_main_t * sm)
+{
+ int rv;
+ vl_api_map_stats_segment_t *mp;
+ api_main_t *am = &api_main;
+
+ sm->socket_client_main = &socket_client_main;
+
+ rv = vl_socket_client_connect ((char *) sm->socket_name,
+ "stat_client",
+ 0 /* default socket rx, tx buffer */ );
+ if (rv)
+ {
+ fformat (stderr, "Error connecting to vpp...\n");
+ exit (1);
+ }
+
+ /* Hook up reply handler */
+ vpp_api_hookup ();
+
+ /* Map the stats segment */
+ mp = vl_socket_client_msg_alloc (sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_MAP_STATS_SEGMENT);
+ mp->client_index = am->my_client_index;
+ mp->context = 0xdeaddabe;
+
+ /* Send the message */
+ vl_socket_client_write ();
+
+ /* Wait for a reply, process it.. */
+ vl_socket_client_read (5 /* timeout in seconds */ );
+
+ return 0;
+}
+
+#define foreach_cached_pointer \
+_(vector_rate, SCALAR_POINTER, &stat_client_main.vector_rate_ptr) \
+_(input_rate, SCALAR_POINTER, &stat_client_main.input_rate_ptr) \
+_(rx, COUNTER_VECTOR, &stat_client_main.intfc_rx_counters) \
+_(tx, COUNTER_VECTOR, &stat_client_main.intfc_tx_counters) \
+_(/err/0/counter_vector, VECTOR_POINTER, \
+ &stat_client_main.thread_0_error_counts) \
+_(/err/IP4 source address matches local interface, ERROR_INDEX, \
+ &stat_client_main.source_address_match_error_index)
+
+typedef struct
+{
+ char *name;
+ stat_directory_type_t type;
+ void *valuep;
+} cached_pointer_t;
+
+cached_pointer_t cached_pointers[] = {
+#define _(n,t,p) {#n, STAT_DIR_TYPE_##t, (void *)p},
+ foreach_cached_pointer
+#undef _
+};
+
+static void
+maybe_update_cached_pointers (stat_client_main_t * sm,
+ ssvm_shared_header_t * shared_header)
+{
+ uword *p, *counter_vector_by_name;
+ int i;
+ stat_segment_directory_entry_t *ep;
+ cached_pointer_t *cp;
+ u64 *valuep;
+
+ /* Cached pointers OK? */
+ if (sm->current_epoch ==
+ (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH])
+ return;
+
+ fformat (stdout, "Updating cached pointers...\n");
+
+ /* Nope, fix them... */
+ counter_vector_by_name = (uword *)
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR];
+
+ for (i = 0; i < ARRAY_LEN (cached_pointers); i++)
+ {
+ cp = &cached_pointers[i];
+
+ p = hash_get_mem (counter_vector_by_name, cp->name);
+
+ if (p == 0)
+ {
+ clib_warning ("WARN: %s not in directory!", cp->name);
+ continue;
+ }
+ ep = (stat_segment_directory_entry_t *) (p[0]);
+ ASSERT (ep->type == cp->type);
+ valuep = (u64 *) cp->valuep;
+ *valuep = (u64) ep->value;
+ }
+
+ /* And remember that we did... */
+ sm->current_epoch = (u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH];
+}
+
+static void
+stat_poll_loop (stat_client_main_t * sm)
+{
+ struct timespec ts, tsrem;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ ssvm_shared_header_t *shared_header;
+ vlib_counter_t *thread0_rx_counters = 0, *thread0_tx_counters = 0;
+ f64 vector_rate, input_rate;
+ u32 len;
+ int i;
+ u32 source_address_match_errors;
+
+ /* Wait until the stats segment is mapped */
+ while (!sm->segment_ready)
+ {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100000000;
+ while (nanosleep (&ts, &tsrem) < 0)
+ ts = tsrem;
+ }
+
+ shared_header = ssvmp->sh;
+ ASSERT (ssvmp->sh);
+
+ while (1)
+ {
+ /* Scrape stats every 5 seconds */
+ ts.tv_sec = 5;
+ ts.tv_nsec = 0;
+ while (nanosleep (&ts, &tsrem) < 0)
+ ts = tsrem;
+
+ vec_reset_length (thread0_rx_counters);
+ vec_reset_length (thread0_tx_counters);
+
+ /* Grab the stats segment lock */
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ /* see if we need to update cached pointers */
+ maybe_update_cached_pointers (sm, shared_header);
+
+ ASSERT (sm->vector_rate_ptr);
+ ASSERT (sm->intfc_rx_counters);
+ ASSERT (sm->intfc_tx_counters);
+
+ /* Read data from the segment */
+ vector_rate = *sm->vector_rate_ptr;
+ input_rate = *sm->input_rate_ptr;
+
+ len = vec_len (sm->intfc_rx_counters[0]);
+
+ ASSERT (len);
+
+ vec_validate (thread0_rx_counters, len - 1);
+ vec_validate (thread0_tx_counters, len - 1);
+
+ clib_memcpy (thread0_rx_counters, sm->intfc_rx_counters[0],
+ len * sizeof (vlib_counter_t));
+ clib_memcpy (thread0_tx_counters, sm->intfc_tx_counters[0],
+ len * sizeof (vlib_counter_t));
+
+ source_address_match_errors =
+ sm->thread_0_error_counts[sm->source_address_match_error_index];
+
+ /* Drop the lock */
+ clib_spinlock_unlock (sm->stat_segment_lockp);
+
+ /* And print results... */
+
+ fformat (stdout, "vector_rate %.2f input_rate %.2f\n",
+ vector_rate, input_rate);
+
+ for (i = 0; i < vec_len (thread0_rx_counters); i++)
+ {
+ fformat (stdout, "[%d]: %lld rx packets, %lld rx bytes\n",
+ i, thread0_rx_counters[i].packets,
+ thread0_rx_counters[i].bytes);
+ fformat (stdout, "[%d]: %lld tx packets, %lld tx bytes\n",
+ i, thread0_tx_counters[i].packets,
+ thread0_tx_counters[i].bytes);
+ }
+
+ fformat (stdout, "%lld source address match errors\n",
+ source_address_match_errors);
+ }
+}
+
+
+int
+main (int argc, char **argv)
+{
+ unformat_input_t _argv, *a = &_argv;
+ stat_client_main_t *sm = &stat_client_main;
+ u8 *socket_name;
+ int rv;
+
+ clib_mem_init (0, 128 << 20);
+
+ unformat_init_command_line (a, argv);
+
+ socket_name = (u8 *) API_SOCKET_FILE;
+
+ while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (a, "socket-name %s", &socket_name))
+ ;
+ else
+ {
+ fformat (stderr, "%s: usage [socket-name <name>]\n", argv[0]);
+ exit (1);
+ }
+ }
+
+ sm->socket_name = socket_name;
+
+ rv = connect_to_vpp (sm);
+
+ if (rv)
+ {
+ fformat (stderr, "Couldn't connect to vpp, does %s exist?\n",
+ socket_name);
+ exit (1);
+ }
+
+ stat_poll_loop (sm);
+ exit (0);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp/app/stat_client.h b/src/vpp/app/stat_client.h
new file mode 100644
index 00000000000..9cec1ee46f5
--- /dev/null
+++ b/src/vpp/app/stat_client.h
@@ -0,0 +1,70 @@
+/*
+ * 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_client_h__
+#define __included_stat_client_h__
+
+#include <vlib/vlib.h>
+#include <vppinfra/socket.h>
+#include <svm/ssvm.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vlibmemory/socket_api.h>
+#include <vlibmemory/socket_client.h>
+#include <vpp/stats/stats.h>
+
+typedef struct
+{
+ u64 current_epoch;
+
+ /* Cached pointers to scalar quantities, these wont change */
+ f64 *vector_rate_ptr;
+ f64 *input_rate_ptr;
+
+ volatile int segment_ready;
+
+ /*
+ * Cached pointers to vector quantities,
+ * MUST invalidate when the epoch changes
+ */
+ vlib_counter_t **intfc_rx_counters;
+ vlib_counter_t **intfc_tx_counters;
+
+ u64 *thread_0_error_counts;
+ u64 source_address_match_error_index;
+
+ /* mapped stats segment object */
+ ssvm_private_t stat_segment;
+
+ /* Socket client object */
+ socket_client_main_t *socket_client_main;
+
+ /* Spinlock for the stats segment */
+ clib_spinlock_t *stat_segment_lockp;
+
+ u8 *socket_name;
+} stat_client_main_t;
+
+extern stat_client_main_t stat_client_main;
+
+#endif /* __included_stat_client_h__ */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp/stats/stat_segment.c b/src/vpp/stats/stat_segment.c
new file mode 100644
index 00000000000..795e347ee3e
--- /dev/null
+++ b/src/vpp/stats/stat_segment.c
@@ -0,0 +1,425 @@
+/*
+ * 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 <vpp/stats/stats.h>
+
+void *
+vlib_stats_push_heap (void)
+{
+ stats_main_t *sm = &stats_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ ssvm_shared_header_t *shared_header;
+
+ ASSERT (ssvmp && ssvmp->sh);
+
+ shared_header = ssvmp->sh;
+
+ return ssvm_push_heap (shared_header);
+}
+
+void
+vlib_stats_pop_heap (void *cm_arg, void *oldheap)
+{
+ vlib_simple_counter_main_t *cm = (vlib_simple_counter_main_t *) cm_arg;
+ stats_main_t *sm = &stats_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ ssvm_shared_header_t *shared_header;
+ char *stat_segment_name;
+ stat_segment_directory_entry_t *ep;
+ uword *p;
+
+ ASSERT (ssvmp && ssvmp->sh);
+
+ shared_header = ssvmp->sh;
+
+ /* Not all counters have names / hash-table entries */
+ if (cm->name || cm->stat_segment_name)
+ {
+ hash_pair_t *hp;
+ u8 *name_copy;
+
+ stat_segment_name = cm->stat_segment_name ?
+ cm->stat_segment_name : cm->name;
+
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ /* Update hash table. The name must be copied into the segment */
+ hp = hash_get_pair (sm->counter_vector_by_name, stat_segment_name);
+ if (hp)
+ {
+ name_copy = (u8 *) hp->key;
+ ep = (stat_segment_directory_entry_t *) (hp->value[0]);
+ hash_unset_mem (sm->counter_vector_by_name, stat_segment_name);
+ vec_free (name_copy);
+ clib_mem_free (ep);
+ }
+ name_copy = format (0, "%s%c", stat_segment_name, 0);
+ ep = clib_mem_alloc (sizeof (*ep));
+ ep->type = STAT_DIR_TYPE_COUNTER_VECTOR;
+ ep->value = cm->counters;
+ hash_set_mem (sm->counter_vector_by_name, name_copy, ep);
+
+ /* Reset the client hash table pointer, since it WILL change! */
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR]
+ = sm->counter_vector_by_name;
+
+ /* Warn clients to refresh any pointers they might be holding */
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
+ ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
+ clib_spinlock_unlock (sm->stat_segment_lockp);
+ }
+ ssvm_pop_heap (oldheap);
+}
+
+void
+vlib_stats_register_error_index (u8 * name, u64 index)
+{
+ stats_main_t *sm = &stats_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ ssvm_shared_header_t *shared_header;
+ stat_segment_directory_entry_t *ep;
+ hash_pair_t *hp;
+ u8 *name_copy;
+ uword *p;
+
+ ASSERT (ssvmp && ssvmp->sh);
+
+ shared_header = ssvmp->sh;
+
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ /* Update hash table. The name must be copied into the segment */
+ hp = hash_get_pair (sm->counter_vector_by_name, name);
+ if (hp)
+ {
+ name_copy = (u8 *) hp->key;
+ ep = (stat_segment_directory_entry_t *) (hp->value[0]);
+ hash_unset_mem (sm->counter_vector_by_name, name);
+ vec_free (name_copy);
+ clib_mem_free (ep);
+ }
+
+ ep = clib_mem_alloc (sizeof (*ep));
+ ep->type = STAT_DIR_TYPE_ERROR_INDEX;
+ ep->value = (void *) index;
+
+ hash_set_mem (sm->counter_vector_by_name, name, ep);
+
+ /* Reset the client hash table pointer, since it WILL change! */
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
+
+ /* Warn clients to refresh any pointers they might be holding */
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
+ ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
+ clib_spinlock_unlock (sm->stat_segment_lockp);
+}
+
+void
+vlib_stats_pop_heap2 (u64 * counter_vector, u32 thread_index, void *oldheap)
+{
+ stats_main_t *sm = &stats_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ ssvm_shared_header_t *shared_header;
+ stat_segment_directory_entry_t *ep;
+ hash_pair_t *hp;
+ u8 *error_vector_name;
+ u8 *name_copy;
+ uword *p;
+
+ ASSERT (ssvmp && ssvmp->sh);
+
+ shared_header = ssvmp->sh;
+
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ error_vector_name = format (0, "/err/%d/counter_vector%c", thread_index, 0);
+
+ /* Update hash table. The name must be copied into the segment */
+ hp = hash_get_pair (sm->counter_vector_by_name, error_vector_name);
+ if (hp)
+ {
+ name_copy = (u8 *) hp->key;
+ ep = (stat_segment_directory_entry_t *) (hp->value[0]);
+ hash_unset_mem (sm->counter_vector_by_name, error_vector_name);
+ vec_free (name_copy);
+ clib_mem_free (ep);
+ }
+
+ ep = clib_mem_alloc (sizeof (*ep));
+ ep->type = STAT_DIR_TYPE_VECTOR_POINTER;
+ ep->value = counter_vector;
+
+ hash_set_mem (sm->counter_vector_by_name, error_vector_name, ep);
+
+ /* Reset the client hash table pointer, since it WILL change! */
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
+
+ /* Warn clients to refresh any pointers they might be holding */
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *)
+ ((u64) shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] + 1);
+ clib_spinlock_unlock (sm->stat_segment_lockp);
+ ssvm_pop_heap (oldheap);
+}
+
+static clib_error_t *
+map_stat_segment_init (vlib_main_t * vm)
+{
+ stats_main_t *sm = &stats_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ ssvm_shared_header_t *shared_header;
+ stat_segment_directory_entry_t *ep;
+ f64 *scalar_data;
+ u8 *name;
+ void *oldheap;
+ u32 *lock;
+ int rv;
+
+ ssvmp->ssvm_size = 32 << 20; /*$$$$$ CONFIG PARAM */
+ ssvmp->i_am_master = 1;
+ ssvmp->my_pid = getpid ();
+ ssvmp->name = format (0, "/stats%c", 0);
+ ssvmp->requested_va = 0;
+
+ rv = ssvm_master_init (ssvmp, SSVM_SEGMENT_MEMFD);
+
+ if (rv)
+ return clib_error_return (0, "stat segment ssvm init failure");
+ shared_header = ssvmp->sh;
+
+ oldheap = ssvm_push_heap (shared_header);
+
+ /* Set up the name to counter-vector hash table */
+ sm->counter_vector_by_name = hash_create_string (0, sizeof (uword));
+
+ sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
+
+ /* Save the hash table address in the shared segment, for clients */
+ clib_spinlock_init (sm->stat_segment_lockp);
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_LOCK] = sm->stat_segment_lockp;
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_EPOCH] = (void *) 1;
+
+ /* Set up a few scalar stats */
+
+ scalar_data = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
+ CLIB_CACHE_LINE_BYTES);
+ sm->vector_rate_ptr = (scalar_data + 0);
+ sm->input_rate_ptr = (scalar_data + 1);
+
+ name = format (0, "vector_rate%c", 0);
+ ep = clib_mem_alloc (sizeof (*ep));
+ ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
+ ep->value = sm->vector_rate_ptr;
+
+ hash_set_mem (sm->counter_vector_by_name, name, ep);
+
+ name = format (0, "input_rate%c", 0);
+ ep = clib_mem_alloc (sizeof (*ep));
+ ep->type = STAT_DIR_TYPE_SCALAR_POINTER;
+ ep->value = sm->input_rate_ptr;
+
+ hash_set_mem (sm->counter_vector_by_name, name, ep);
+
+ /* Publish the hash table */
+ shared_header->opaque[STAT_SEGMENT_OPAQUE_DIR] = sm->counter_vector_by_name;
+
+ ssvm_pop_heap (oldheap);
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (map_stat_segment_init);
+
+typedef struct
+{
+ u8 *name;
+ stat_segment_directory_entry_t *dir_entry;
+} show_stat_segment_t;
+
+static int
+name_sort_cmp (void *a1, void *a2)
+{
+ show_stat_segment_t *n1 = a1;
+ show_stat_segment_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 = "%-10s %20llx";
+
+ switch (ep->type)
+ {
+ case STAT_DIR_TYPE_SCALAR_POINTER:
+ type_name = "ScalarPtr";
+ break;
+
+ case STAT_DIR_TYPE_VECTOR_POINTER:
+ type_name = "VectorPtr";
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR:
+ type_name = "CMainPtr";
+ break;
+
+ case STAT_DIR_TYPE_ERROR_INDEX:
+ type_name = "ErrIndex";
+ format_string = "%-10s %20lld";
+ break;
+
+ default:
+ type_name = "illegal!";
+ break;
+ }
+
+ return format (s, format_string, type_name, ep->value);
+}
+
+
+
+static clib_error_t *
+show_stat_segment_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ stats_main_t *sm = &stats_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ ssvm_shared_header_t *shared_header;
+ counter_t *counter;
+ hash_pair_t *p;
+ show_stat_segment_t *show_data = 0;
+ show_stat_segment_t *this;
+ int i;
+
+ int verbose = 0;
+ u8 *s;
+
+ if (unformat (input, "verbose"))
+ verbose = 1;
+
+ clib_spinlock_lock (sm->stat_segment_lockp);
+
+ /* *INDENT-OFF* */
+ hash_foreach_pair (p, sm->counter_vector_by_name,
+ ({
+ vec_add2 (show_data, this, 1);
+
+ this->name = (u8 *) (p->key);
+ this->dir_entry = (stat_segment_directory_entry_t *)(p->value[0]);
+ }));
+ /* *INDENT-ON* */
+
+ clib_spinlock_unlock (sm->stat_segment_lockp);
+
+ vec_sort_with_function (show_data, name_sort_cmp);
+
+ vlib_cli_output (vm, "%-60s %10s %20s", "Name", "Type", "Value");
+
+ for (i = 0; i < vec_len (show_data); i++)
+ {
+ this = vec_elt_at_index (show_data, i);
+
+ vlib_cli_output (vm, "%-60s %31U",
+ this->name, format_stat_dir_entry, this->dir_entry);
+ }
+
+ if (verbose)
+ {
+ ASSERT (ssvmp && ssvmp->sh);
+
+ shared_header = ssvmp->sh;
+
+ vlib_cli_output (vm, "%U", format_mheap,
+ shared_header->heap, 0 /* verbose */ );
+ }
+
+ return 0;
+}
+
+/* *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* */
+
+static uword
+stat_segment_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
+ vlib_frame_t * f)
+{
+ f64 vector_rate;
+ u64 input_packets, last_input_packets;
+ f64 last_runtime, dt, now;
+ vlib_main_t *this_vlib_main;
+ stats_main_t *sm = &stats_main;
+
+ last_runtime = 0.0;
+ last_input_packets = 0;
+
+ last_runtime = 0.0;
+ last_input_packets = 0;
+
+ while (1)
+ {
+ vlib_process_suspend (vm, 5.0);
+
+ /*
+ * Compute the average vector rate across all workers
+ */
+ vector_rate = 0.0;
+
+ /* *INDENT-OFF* */
+ foreach_vlib_main
+ ({
+ vector_rate += vlib_last_vector_length_per_node (vm);
+ });
+ /* *INDENT-ON* */
+
+ *sm->vector_rate_ptr = vector_rate / ((f64) vec_len (vlib_mains));
+ now = vlib_time_now (vm);
+ dt = now - last_runtime;
+ input_packets = vnet_get_aggregate_rx_packets ();
+ *sm->input_rate_ptr = (f64) (input_packets - last_input_packets) / dt;
+ last_runtime = now;
+ last_input_packets = input_packets;
+ }
+
+ return 0; /* not so much */
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (stat_segment_node,static) =
+{
+ .function = stat_segment_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "stat-segment-process",
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vpp/stats/stats.api b/src/vpp/stats/stats.api
index 86eaa62d4e6..bd1c3f634fe 100644
--- a/src/vpp/stats/stats.api
+++ b/src/vpp/stats/stats.api
@@ -58,6 +58,8 @@ service {
rpc want_udp_encap_stats
returns want_udp_encap_stats_reply
events vnet_udp_encap_counters;
+ rpc map_stats_segment
+ returns map_stats_segment_reply;
};
/** \brief Want Stats, enable/disable ALL stats updates
@@ -472,6 +474,13 @@ manual_print manual_endian define vnet_udp_encap_counters
vl_api_udp_encap_counter_t c[count];
};
+autoreply define map_stats_segment
+{
+ u32 client_index;
+ u32 context;
+};
+
+
/*
* Local Variables:
* eval: (c-set-style "gnu")
diff --git a/src/vpp/stats/stats.c b/src/vpp/stats/stats.c
index fc4b905f982..bf0bc50cafc 100644
--- a/src/vpp/stats/stats.c
+++ b/src/vpp/stats/stats.c
@@ -48,24 +48,25 @@ stats_main_t stats_main;
#define foreach_stats_msg \
_(WANT_STATS, want_stats) \
_(VNET_INTERFACE_SIMPLE_COUNTERS, vnet_interface_simple_counters) \
-_(WANT_INTERFACE_SIMPLE_STATS, want_interface_simple_stats) \
+_(WANT_INTERFACE_SIMPLE_STATS, want_interface_simple_stats) \
_(VNET_INTERFACE_COMBINED_COUNTERS, vnet_interface_combined_counters) \
-_(WANT_INTERFACE_COMBINED_STATS, want_interface_combined_stats) \
+_(WANT_INTERFACE_COMBINED_STATS, want_interface_combined_stats) \
_(WANT_PER_INTERFACE_COMBINED_STATS, want_per_interface_combined_stats) \
-_(WANT_PER_INTERFACE_SIMPLE_STATS, want_per_interface_simple_stats) \
+_(WANT_PER_INTERFACE_SIMPLE_STATS, want_per_interface_simple_stats) \
_(VNET_IP4_FIB_COUNTERS, vnet_ip4_fib_counters) \
-_(WANT_IP4_FIB_STATS, want_ip4_fib_stats) \
+_(WANT_IP4_FIB_STATS, want_ip4_fib_stats) \
_(VNET_IP6_FIB_COUNTERS, vnet_ip6_fib_counters) \
-_(WANT_IP6_FIB_STATS, want_ip6_fib_stats) \
+_(WANT_IP6_FIB_STATS, want_ip6_fib_stats) \
_(WANT_IP4_MFIB_STATS, want_ip4_mfib_stats) \
_(WANT_IP6_MFIB_STATS, want_ip6_mfib_stats) \
_(VNET_IP4_NBR_COUNTERS, vnet_ip4_nbr_counters) \
-_(WANT_IP4_NBR_STATS, want_ip4_nbr_stats) \
-_(VNET_IP6_NBR_COUNTERS, vnet_ip6_nbr_counters) \
-_(WANT_IP6_NBR_STATS, want_ip6_nbr_stats) \
-_(VNET_GET_SUMMARY_STATS, vnet_get_summary_stats) \
-_(STATS_GET_POLLER_DELAY, stats_get_poller_delay) \
-_(WANT_UDP_ENCAP_STATS, want_udp_encap_stats)
+_(WANT_IP4_NBR_STATS, want_ip4_nbr_stats) \
+_(VNET_IP6_NBR_COUNTERS, vnet_ip6_nbr_counters) \
+_(WANT_IP6_NBR_STATS, want_ip6_nbr_stats) \
+_(VNET_GET_SUMMARY_STATS, vnet_get_summary_stats) \
+_(STATS_GET_POLLER_DELAY, stats_get_poller_delay) \
+_(WANT_UDP_ENCAP_STATS, want_udp_encap_stats) \
+_(MAP_STATS_SEGMENT, map_stats_segment)
#define vl_msg_name_crc_list
#include <vpp/stats/stats.api.h>
@@ -2934,6 +2935,50 @@ stats_memclnt_delete_callback (u32 client_index)
#define vl_api_vnet_ip4_nbr_counters_t_print vl_noop_handler
#define vl_api_vnet_ip6_nbr_counters_t_endian vl_noop_handler
#define vl_api_vnet_ip6_nbr_counters_t_print vl_noop_handler
+#define vl_api_map_stats_segment_t_print vl_noop_handler
+
+static void
+vl_api_map_stats_segment_t_handler (vl_api_map_stats_segment_t * mp)
+{
+ vl_api_map_stats_segment_reply_t *rmp;
+ stats_main_t *sm = &stats_main;
+ ssvm_private_t *ssvmp = &sm->stat_segment;
+ vl_api_registration_t *regp;
+ api_main_t *am = &api_main;
+ clib_file_t *cf;
+ vl_api_shm_elem_config_t *config = 0;
+ vl_shmem_hdr_t *shmem_hdr;
+ int rv = 0;
+
+ regp = vl_api_client_index_to_registration (mp->client_index);
+ if (regp == 0)
+ {
+ clib_warning ("API client disconnected");
+ return;
+ }
+ if (regp->registration_type != REGISTRATION_TYPE_SOCKET_SERVER)
+ rv = VNET_API_ERROR_INVALID_REGISTRATION;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ rmp->_vl_msg_id = htons (VL_API_MAP_STATS_SEGMENT_REPLY);
+ rmp->context = mp->context;
+ rmp->retval = htonl (rv);
+
+ vl_api_send_msg (regp, (u8 *) rmp);
+
+ if (rv != 0)
+ return;
+
+ /*
+ * We need the reply message to make it out the back door
+ * before we send the magic fd message so force a flush
+ */
+ cf = vl_api_registration_file (regp);
+ cf->write_function (cf);
+
+ /* Send the magic "here's your sign (aka fd)" socket message */
+ vl_sock_api_send_fd_msg (cf->file_descriptor, ssvmp->fd);
+}
static clib_error_t *
stats_init (vlib_main_t * vm)
diff --git a/src/vpp/stats/stats.h b/src/vpp/stats/stats.h
index 629ac987151..fd1ab271f78 100644
--- a/src/vpp/stats/stats.h
+++ b/src/vpp/stats/stats.h
@@ -27,6 +27,7 @@
#include <vlibmemory/api.h>
#include <vlibapi/api_helper_macros.h>
#include <svm/queue.h>
+#include <svm/ssvm.h>
typedef struct
{
@@ -157,6 +158,15 @@ typedef struct
vpe_client_stats_registration_t **regs_tmp;
vpe_client_registration_t **clients_tmp;
+ /* statistics segment */
+ ssvm_private_t stat_segment;
+ uword *counter_vector_by_name;
+ clib_spinlock_t *stat_segment_lockp;
+
+ /* Pointers to scalar stats maintained by the stat segment process */
+ f64 *input_rate_ptr;
+ f64 *vector_rate_ptr;
+
/* convenience */
vlib_main_t *vlib_main;
vnet_main_t *vnet_main;
@@ -166,6 +176,25 @@ typedef struct
extern stats_main_t stats_main;
+#define STAT_SEGMENT_OPAQUE_LOCK 0
+#define STAT_SEGMENT_OPAQUE_DIR 1
+#define STAT_SEGMENT_OPAQUE_EPOCH 2
+
+typedef enum
+{
+ STAT_DIR_TYPE_ILLEGAL = 0,
+ STAT_DIR_TYPE_SCALAR_POINTER,
+ STAT_DIR_TYPE_VECTOR_POINTER,
+ STAT_DIR_TYPE_COUNTER_VECTOR,
+ STAT_DIR_TYPE_ERROR_INDEX,
+} stat_directory_type_t;
+
+typedef struct
+{
+ stat_directory_type_t type;
+ void *value;
+} stat_segment_directory_entry_t;
+
#endif /* __included_stats_h__ */
/*