-
Notifications
You must be signed in to change notification settings - Fork 24
infra: export interface statistics through DPDK telemetry socket #234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -200,17 +200,74 @@ static struct api_out stats_reset(const void * /*request*/, void ** /*response*/ | |||||
| atomic_store(&worker->stats_reset, true); | ||||||
|
|
||||||
| iface = NULL; | ||||||
| while ((iface = iface_next(GR_IFACE_TYPE_PORT, iface)) != NULL) { | ||||||
| struct iface_info_port *port = (struct iface_info_port *)iface->info; | ||||||
| if ((ret = rte_eth_stats_reset(port->port_id)) < 0) | ||||||
| return api_out(-ret, 0); | ||||||
| if ((ret = rte_eth_xstats_reset(port->port_id)) < 0) | ||||||
| return api_out(-ret, 0); | ||||||
|
|
||||||
| while ((iface = iface_next(GR_IFACE_TYPE_UNDEF, iface)) != NULL) { | ||||||
| struct iface_stats *sw_stats = iface_get_stats(iface->id); | ||||||
| // Reset software stats for all interface types. | ||||||
| if (sw_stats != NULL) { | ||||||
| memset(sw_stats, 0, sizeof(*sw_stats)); | ||||||
| } | ||||||
|
|
||||||
| if (iface->type == GR_IFACE_TYPE_PORT) { | ||||||
| struct iface_info_port *port = (struct iface_info_port *)iface->info; | ||||||
| if ((ret = rte_eth_stats_reset(port->port_id)) < 0) | ||||||
| return api_out(-ret, 0); | ||||||
| if ((ret = rte_eth_xstats_reset(port->port_id)) < 0) | ||||||
| return api_out(-ret, 0); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return api_out(0, 0); | ||||||
| } | ||||||
|
|
||||||
| static struct api_out iface_stats_get(const void * /*request*/, void **response) { | ||||||
| struct gr_infra_iface_stats_get_resp *resp = NULL; | ||||||
| struct gr_iface_stats *stats_vec = NULL; | ||||||
| struct iface *iface = NULL; | ||||||
| int ret = 0; | ||||||
|
|
||||||
| while ((iface = iface_next(GR_IFACE_TYPE_UNDEF, iface)) != NULL) { | ||||||
| struct iface_stats *sw_stats = iface_get_stats(iface->id); | ||||||
| if (sw_stats == NULL) | ||||||
| continue; | ||||||
|
|
||||||
| // Create a single stats object per interface | ||||||
| struct gr_iface_stats s; | ||||||
| s.iface_id = iface->id; | ||||||
| s.rx_packets = 0; | ||||||
| s.rx_bytes = 0; | ||||||
| s.tx_packets = 0; | ||||||
| s.tx_bytes = 0; | ||||||
|
|
||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rjarry I'm already resetting the stats per iface_id before obtaining the stats. Isn't it enough?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean resetting the internal structure. grout/modules/infra/control/iface.c Line 175 in 9f716b3
So after this line: grout/modules/infra/control/iface.c Line 91 in 9f716b3
You need to add: memset(&stats[ifid], 0, sizeof(stats[ifid]));So that new interfaces are created with all their statistics reset to 0.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh thanks, added this part in latest change. |
||||||
| // Aggregate per-core stats | ||||||
| for (int i = 0; i < RTE_MAX_LCORE; i++) { | ||||||
| s.rx_packets += sw_stats->rx_packets[i]; | ||||||
| s.rx_bytes += sw_stats->rx_bytes[i]; | ||||||
| s.tx_packets += sw_stats->tx_packets[i]; | ||||||
| s.tx_bytes += sw_stats->tx_bytes[i]; | ||||||
| } | ||||||
| gr_vec_add(stats_vec, s); | ||||||
| } | ||||||
|
|
||||||
| size_t n_stats = gr_vec_len(stats_vec); | ||||||
| size_t len = sizeof(*resp) + n_stats * sizeof(struct gr_iface_stats); | ||||||
| if ((resp = calloc(1, len)) == NULL) { | ||||||
| ret = -ENOMEM; | ||||||
| goto err; | ||||||
| } | ||||||
|
|
||||||
| resp->n_stats = n_stats; | ||||||
| memcpy(resp->stats, stats_vec, n_stats * sizeof(struct gr_iface_stats)); | ||||||
|
|
||||||
| gr_vec_free(stats_vec); | ||||||
| *response = resp; | ||||||
| return api_out(0, len); | ||||||
| err: | ||||||
| gr_vec_free(stats_vec); | ||||||
| free(resp); | ||||||
| return api_out(-ret, 0); | ||||||
| } | ||||||
|
|
||||||
| static int | ||||||
| telemetry_sw_stats_get(const char * /*cmd*/, const char * /*params*/, struct rte_tel_data *d) { | ||||||
| struct stat *stats = NULL, *s; | ||||||
|
|
@@ -278,6 +335,131 @@ telemetry_sw_stats_get(const char * /*cmd*/, const char * /*params*/, struct rte | |||||
| return -1; | ||||||
| } | ||||||
|
|
||||||
| static int | ||||||
| telemetry_ifaces_info_get(const char * /*cmd*/, const char * /*params*/, struct rte_tel_data *d) { | ||||||
| struct iface *iface = NULL; | ||||||
|
|
||||||
| rte_tel_data_start_dict(d); | ||||||
|
|
||||||
| while ((iface = iface_next(GR_IFACE_TYPE_UNDEF, iface)) != NULL) { | ||||||
| if (iface->type != GR_IFACE_TYPE_LOOPBACK) { | ||||||
| struct rte_tel_data *iface_container = rte_tel_data_alloc(); | ||||||
| if (iface_container == NULL) { | ||||||
| goto err; | ||||||
| } | ||||||
| rte_tel_data_start_dict(iface_container); | ||||||
|
|
||||||
| rte_tel_data_add_dict_string(iface_container, "name", iface->name); | ||||||
| rte_tel_data_add_dict_uint(iface_container, "id", iface->id); | ||||||
| rte_tel_data_add_dict_string( | ||||||
| iface_container, "type", iface_type_to_str(iface->type) | ||||||
| ); | ||||||
| rte_tel_data_add_dict_uint(iface_container, "mtu", iface->mtu); | ||||||
|
|
||||||
| struct rte_tel_data *flags_array = rte_tel_data_alloc(); | ||||||
| if (flags_array == NULL) { | ||||||
| rte_tel_data_free(iface_container); | ||||||
| goto err; | ||||||
| } | ||||||
| rte_tel_data_start_array(flags_array, RTE_TEL_STRING_VAL); | ||||||
| if (iface->flags & GR_IFACE_F_UP) | ||||||
| rte_tel_data_add_array_string(flags_array, "up"); | ||||||
| if (iface->state & GR_IFACE_S_RUNNING) | ||||||
| rte_tel_data_add_array_string(flags_array, "running"); | ||||||
| rte_tel_data_add_dict_container(iface_container, "flags", flags_array, 0); | ||||||
|
|
||||||
| rte_tel_data_add_dict_string( | ||||||
| iface_container, "mode", iface_mode_to_str(iface->mode) | ||||||
| ); | ||||||
| rte_tel_data_add_dict_uint(iface_container, "vrf_id", iface->vrf_id); | ||||||
|
|
||||||
| struct rte_tel_data *stats_container = rte_tel_data_alloc(); | ||||||
| if (stats_container == NULL) { | ||||||
| rte_tel_data_free(iface_container); | ||||||
| goto err; | ||||||
| } | ||||||
| rte_tel_data_start_dict(stats_container); | ||||||
|
|
||||||
| // Software stats | ||||||
| struct iface_stats *sw_stats = iface_get_stats(iface->id); | ||||||
| if (sw_stats != NULL) { | ||||||
| uint64_t rx_pkts = 0, rx_bytes = 0, tx_pkts = 0, tx_bytes = 0; | ||||||
| for (int i = 0; i < RTE_MAX_LCORE; i++) { | ||||||
| rx_pkts += sw_stats->rx_packets[i]; | ||||||
| rx_bytes += sw_stats->rx_bytes[i]; | ||||||
| tx_pkts += sw_stats->tx_packets[i]; | ||||||
| tx_bytes += sw_stats->tx_bytes[i]; | ||||||
| } | ||||||
| rte_tel_data_add_dict_uint(stats_container, "rx_packets", rx_pkts); | ||||||
| rte_tel_data_add_dict_uint(stats_container, "rx_bytes", rx_bytes); | ||||||
| rte_tel_data_add_dict_uint(stats_container, "tx_packets", tx_pkts); | ||||||
| rte_tel_data_add_dict_uint(stats_container, "tx_bytes", tx_bytes); | ||||||
| } | ||||||
|
|
||||||
| // Get hardware stats for physical ports. | ||||||
| if (iface->type == GR_IFACE_TYPE_PORT) { | ||||||
| struct iface_info_port *port = (struct | ||||||
| iface_info_port *)iface->info; | ||||||
|
|
||||||
| struct rte_eth_stats eth_stats; | ||||||
| if (rte_eth_stats_get(port->port_id, ð_stats) == 0) { | ||||||
| rte_tel_data_add_dict_uint( | ||||||
| stats_container, "rx_missed", eth_stats.imissed | ||||||
| ); | ||||||
| rte_tel_data_add_dict_uint( | ||||||
| stats_container, "tx_errors", eth_stats.oerrors | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| int ret = rte_eth_xstats_get(port->port_id, NULL, 0); | ||||||
| if (ret > 0) { | ||||||
| unsigned num = ret; | ||||||
| struct rte_eth_xstat *xstats = calloc(num, sizeof(*xstats)); | ||||||
| struct rte_eth_xstat_name *names = calloc( | ||||||
| num, sizeof(*names) | ||||||
| ); | ||||||
| if (xstats != NULL && names != NULL | ||||||
| && rte_eth_xstats_get_names(port->port_id, names, num) | ||||||
| == (int)num | ||||||
| && rte_eth_xstats_get(port->port_id, xstats, num) | ||||||
| == (int)num) { | ||||||
| for (unsigned i = 0; i < num; i++) { | ||||||
| if (xstats[i].value > 0) { | ||||||
| rte_tel_data_add_dict_uint( | ||||||
| stats_container, | ||||||
| names[i].name, | ||||||
| xstats[i].value | ||||||
| ); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| free(xstats); | ||||||
| free(names); | ||||||
| } | ||||||
|
|
||||||
| if (rte_tel_data_add_dict_container( | ||||||
| iface_container, "statistics", stats_container, 0 | ||||||
| ) | ||||||
| != 0) { | ||||||
| rte_tel_data_free(stats_container); | ||||||
| rte_tel_data_free(iface_container); | ||||||
| goto err; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if (rte_tel_data_add_dict_container(d, iface->name, iface_container, 0) | ||||||
| != 0) { | ||||||
| rte_tel_data_free(iface_container); | ||||||
| goto err; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| return 0; | ||||||
|
|
||||||
| err: | ||||||
| return -1; | ||||||
| } | ||||||
|
|
||||||
| static struct gr_api_handler stats_get_handler = { | ||||||
| .name = "stats get", | ||||||
| .request_type = GR_INFRA_STATS_GET, | ||||||
|
|
@@ -290,12 +472,24 @@ static struct gr_api_handler stats_reset_handler = { | |||||
| .callback = stats_reset, | ||||||
| }; | ||||||
|
|
||||||
| static struct gr_api_handler iface_stats_get_handler = { | ||||||
| .name = "iface stats get", | ||||||
| .request_type = GR_INFRA_IFACE_STATS_GET, | ||||||
| .callback = iface_stats_get, | ||||||
| }; | ||||||
|
|
||||||
| RTE_INIT(infra_stats_init) { | ||||||
| gr_register_api_handler(&stats_get_handler); | ||||||
| gr_register_api_handler(&stats_reset_handler); | ||||||
| gr_register_api_handler(&iface_stats_get_handler); | ||||||
| rte_telemetry_register_cmd( | ||||||
| "/grout/stats/graph", | ||||||
| telemetry_sw_stats_get, | ||||||
| "Returns statistics of each graph node. No parameters" | ||||||
| ); | ||||||
| rte_telemetry_register_cmd( | ||||||
| "/grout/iface", | ||||||
| telemetry_ifaces_info_get, | ||||||
| "Returns information per interface. No parameters" | ||||||
| ); | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was squashed into the wrong commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic to clear the stats is part of "infra: add API and CLI command to obtain and clear iface stats" commit - d5f812f, I think we are good?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really. The statistics already exist in the first commit infra: implement per interface statistics even if they are not exposed in the API yet.
You should implement the reset of these statistics in this commit. Not the one that exposes the stats in the API.