numam-dpdk/lib/librte_metrics/rte_metrics_telemetry.c
Jie Zhou 88f4450ab2 metrics: export telemetry stubs if no libjansson
This patch allows the same set of rte_metrics_tel_* functions to be
exported no matter JANSSON is available or not, by doing following:
1.	Leverage dpdk_conf to set configuration flag RTE_HAS_JANSSON
when Jansson dependency is found.
2.	In rte_metrics_telemetry.c, leverage RTE_HAS_JANSSON to handle the
case when JANSSON is not available by adding stubs for all the instances.
3.	In meson.build, per dpdk/doc/guides/rel_notes/release_20_05.rst,
it is claimed that "Telemetry library is no longer dependent on the
external Jansson library, which allows Telemetry be enabled by default.",
thus make the deps and includes of Telemetry as not conditional anymore.

Signed-off-by: Jie Zhou <jizh@microsoft.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
2021-03-16 10:08:06 +01:00

603 lines
14 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2020 Intel Corporation
*/
#include <rte_ethdev.h>
#include <rte_string_fns.h>
#ifdef RTE_LIB_TELEMETRY
#include <rte_telemetry_legacy.h>
#endif
#include "rte_metrics.h"
#include "rte_metrics_telemetry.h"
#ifdef RTE_HAS_JANSSON
struct telemetry_metrics_data tel_met_data;
int metrics_log_level;
/* Logging Macros */
#define METRICS_LOG(level, fmt, args...) \
rte_log(RTE_LOG_ ##level, metrics_log_level, "%s(): "fmt "\n", \
__func__, ##args)
#define METRICS_LOG_ERR(fmt, args...) \
METRICS_LOG(ERR, fmt, ## args)
#define METRICS_LOG_WARN(fmt, args...) \
METRICS_LOG(WARNING, fmt, ## args)
static int32_t
rte_metrics_tel_reg_port_ethdev_to_metrics(uint16_t port_id)
{
int ret, num_xstats, i;
struct rte_eth_xstat_name *eth_xstats_names;
const char **xstats_names;
num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
if (num_xstats < 0) {
METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d",
port_id, num_xstats);
return -EPERM;
}
xstats_names = malloc(sizeof(*xstats_names) * num_xstats);
eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name)
* num_xstats);
if (eth_xstats_names == NULL || xstats_names == NULL) {
METRICS_LOG_ERR("Failed to malloc memory for xstats_names");
ret = -ENOMEM;
goto free_xstats;
}
if (rte_eth_xstats_get_names(port_id,
eth_xstats_names, num_xstats) != num_xstats) {
METRICS_LOG_ERR("rte_eth_xstats_get_names(%u) len %d failed",
port_id, num_xstats);
ret = -EPERM;
goto free_xstats;
}
for (i = 0; i < num_xstats; i++)
xstats_names[i] = eth_xstats_names[i].name;
ret = rte_metrics_reg_names(xstats_names, num_xstats);
if (ret < 0)
METRICS_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered");
free_xstats:
free(eth_xstats_names);
free(xstats_names);
return ret;
}
int32_t
rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, int *reg_index_list)
{
struct driver_index {
const void *dev_ops;
int reg_index;
} drv_idx[RTE_MAX_ETHPORTS] = { {0} };
int ret, nb_drv_idx = 0;
uint16_t d;
rte_metrics_init(rte_socket_id());
RTE_ETH_FOREACH_DEV(d) {
int i;
/* Different device types have different numbers of stats, so
* first check if the stats for this type of device have
* already been registered
*/
for (i = 0; i < nb_drv_idx; i++) {
if (rte_eth_devices[d].dev_ops == drv_idx[i].dev_ops) {
reg_index_list[d] = drv_idx[i].reg_index;
break;
}
}
if (i < nb_drv_idx)
continue; /* we found a match, go to next port */
/* No match, register a new set of xstats for this port */
ret = rte_metrics_tel_reg_port_ethdev_to_metrics(d);
if (ret < 0) {
METRICS_LOG_ERR("Failed to register ethdev to metrics");
return ret;
}
reg_index_list[d] = ret;
drv_idx[nb_drv_idx].dev_ops = rte_eth_devices[d].dev_ops;
drv_idx[nb_drv_idx].reg_index = ret;
nb_drv_idx++;
}
*metrics_register_done = 1;
return 0;
}
static int32_t
rte_metrics_tel_update_metrics_ethdev(uint16_t port_id, int reg_start_index)
{
int ret, num_xstats, i;
struct rte_eth_xstat *eth_xstats;
num_xstats = rte_eth_xstats_get(port_id, NULL, 0);
if (num_xstats < 0) {
METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id,
num_xstats);
return -EPERM;
}
eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats);
if (eth_xstats == NULL) {
METRICS_LOG_ERR("Failed to malloc memory for xstats");
return -ENOMEM;
}
ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats);
if (ret < 0 || ret > num_xstats) {
free(eth_xstats);
METRICS_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d",
port_id, num_xstats, ret);
return -EPERM;
}
uint64_t xstats_values[num_xstats];
for (i = 0; i < num_xstats; i++)
xstats_values[i] = eth_xstats[i].value;
if (rte_metrics_update_values(port_id, reg_start_index, xstats_values,
num_xstats) < 0) {
METRICS_LOG_ERR("Could not update metrics values");
free(eth_xstats);
return -EPERM;
}
free(eth_xstats);
return 0;
}
static int32_t
rte_metrics_tel_format_port(uint32_t pid, json_t *ports,
uint32_t *metric_ids, int num_metric_ids)
{
struct rte_metric_value *metrics = NULL;
struct rte_metric_name *names = NULL;
int num_metrics, i, ret = -EPERM; /* most error cases return EPERM */
json_t *port, *stats;
num_metrics = rte_metrics_get_names(NULL, 0);
if (num_metrics < 0) {
METRICS_LOG_ERR("Cannot get metrics count");
return -EINVAL;
} else if (num_metrics == 0) {
METRICS_LOG_ERR("No metrics to display (none have been registered)");
return -EPERM;
}
metrics = malloc(sizeof(struct rte_metric_value) * num_metrics);
names = malloc(sizeof(struct rte_metric_name) * num_metrics);
if (metrics == NULL || names == NULL) {
METRICS_LOG_ERR("Cannot allocate memory");
ret = -ENOMEM;
goto fail;
}
if (rte_metrics_get_names(names, num_metrics) != num_metrics ||
rte_metrics_get_values(pid, metrics, num_metrics)
!= num_metrics) {
METRICS_LOG_ERR("Error getting metrics");
goto fail;
}
stats = json_array();
if (stats == NULL) {
METRICS_LOG_ERR("Could not create stats JSON object");
goto fail;
}
for (i = 0; i < num_metrics; i++) {
int32_t j;
for (j = 0; j < num_metric_ids; j++)
if (metrics[i].key == metric_ids[j])
break;
if (num_metric_ids > 0 && j == num_metric_ids)
continue; /* can't find this id */
json_t *stat = json_pack("{s,s,s,I}",
"name", names[metrics[i].key].name,
"value", metrics[i].value);
if (stat == NULL || json_array_append_new(stats, stat) < 0) {
METRICS_LOG_ERR("Format stat with id: %u failed",
metrics[i].key);
goto fail;
}
}
port = json_pack("{s,i,s,o}", "port", pid, "stats",
json_array_size(stats) ? stats : json_null());
if (port == NULL || json_array_append_new(ports, port) < 0) {
METRICS_LOG_ERR("Error creating port and adding to ports");
goto fail;
}
free(metrics);
free(names);
return 0;
fail:
free(metrics);
free(names);
return ret;
}
int32_t
rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep,
char **json_buffer)
{
json_t *root, *ports;
int ret, i;
ports = json_array();
if (ports == NULL) {
METRICS_LOG_ERR("Could not create ports JSON array");
return -EPERM;
}
if (ep->type == PORT_STATS) {
if (ep->pp.num_port_ids <= 0) {
METRICS_LOG_ERR("Please provide port/metric ids");
return -EINVAL;
}
for (i = 0; i < ep->pp.num_port_ids; i++) {
ret = rte_metrics_tel_format_port(ep->pp.port_ids[i],
ports, &ep->pp.metric_ids[0],
ep->pp.num_metric_ids);
if (ret < 0) {
METRICS_LOG_ERR("Format port in JSON failed");
return ret;
}
}
} else if (ep->type == GLOBAL_STATS) {
/* Request Global Metrics */
ret = rte_metrics_tel_format_port(RTE_METRICS_GLOBAL,
ports, NULL, 0);
if (ret < 0) {
METRICS_LOG_ERR("Request Global Metrics Failed");
return ret;
}
} else {
METRICS_LOG_ERR("Invalid metrics type in encode params");
return -EINVAL;
}
root = json_pack("{s,s,s,o}", "status_code", "Status OK: 200",
"data", ports);
if (root == NULL) {
METRICS_LOG_ERR("Root, Status or data field cannot be set");
return -EPERM;
}
*json_buffer = json_dumps(root, JSON_INDENT(2));
json_decref(root);
return 0;
}
int32_t
rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep,
int *reg_index, char **json_buffer)
{
int ret, i;
uint32_t port_id;
for (i = 0; i < ep->pp.num_port_ids; i++) {
port_id = ep->pp.port_ids[i];
if (!rte_eth_dev_is_valid_port(port_id)) {
METRICS_LOG_ERR("Port: %d invalid", port_id);
return -EINVAL;
}
ret = rte_metrics_tel_update_metrics_ethdev(port_id,
reg_index[i]);
if (ret < 0) {
METRICS_LOG_ERR("Failed to update ethdev metrics");
return ret;
}
}
ret = rte_metrics_tel_encode_json_format(ep, json_buffer);
if (ret < 0) {
METRICS_LOG_ERR("JSON encode function failed");
return ret;
}
return 0;
}
int32_t
rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep)
{
int p, num_port_ids = 0;
RTE_ETH_FOREACH_DEV(p) {
ep->pp.port_ids[num_port_ids] = p;
num_port_ids++;
}
if (!num_port_ids) {
METRICS_LOG_ERR("No active ports");
return -EINVAL;
}
ep->pp.num_port_ids = num_port_ids;
ep->pp.num_metric_ids = 0;
ep->type = PORT_STATS;
return 0;
}
static int32_t
rte_metrics_tel_stat_names_to_ids(const char * const *stat_names,
uint32_t *stat_ids, int num_stat_names)
{
struct rte_metric_name *names;
int num_metrics;
int i, j, nb_stat_ids = 0;
num_metrics = rte_metrics_get_names(NULL, 0);
if (num_metrics <= 0) {
METRICS_LOG_ERR("Error getting metrics count - no metrics may be registered");
return -EPERM;
}
names = malloc(sizeof(struct rte_metric_name) * num_metrics);
if (names == NULL) {
METRICS_LOG_ERR("Cannot allocate memory for names");
return -ENOMEM;
}
if (rte_metrics_get_names(names, num_metrics) != num_metrics) {
METRICS_LOG_ERR("Cannot get metrics names");
free(names);
return -EPERM;
}
for (i = 0; i < num_stat_names; i++) {
for (j = 0; j < num_metrics; j++) {
if (strcmp(stat_names[i], names[j].name) == 0) {
stat_ids[nb_stat_ids++] = j;
break;
}
}
if (j == num_metrics) {
METRICS_LOG_WARN("Invalid stat name %s\n",
stat_names[i]);
free(names);
return -EINVAL;
}
}
free(names);
return 0;
}
int32_t
rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data)
{
int ret;
json_t *port_ids_json = json_object_get(data, "ports");
json_t *stat_names_json = json_object_get(data, "stats");
uint64_t num_stat_names = json_array_size(stat_names_json);
const char *stat_names[num_stat_names];
size_t index;
json_t *value;
memset(ep, 0, sizeof(*ep));
ep->pp.num_port_ids = json_array_size(port_ids_json);
ep->pp.num_metric_ids = num_stat_names;
if (!json_is_object(data) || !json_is_array(port_ids_json) ||
!json_is_array(stat_names_json)) {
METRICS_LOG_WARN("Invalid data provided for this command");
return -EINVAL;
}
json_array_foreach(port_ids_json, index, value) {
if (!json_is_integer(value)) {
METRICS_LOG_WARN("Port ID given is not valid");
return -EINVAL;
}
ep->pp.port_ids[index] = json_integer_value(value);
if (rte_eth_dev_is_valid_port(ep->pp.port_ids[index]) < 1)
return -EINVAL;
}
json_array_foreach(stat_names_json, index, value) {
if (!json_is_string(value)) {
METRICS_LOG_WARN("Stat Name given is not a string");
return -EINVAL;
}
stat_names[index] = json_string_value(value);
}
ret = rte_metrics_tel_stat_names_to_ids(stat_names, ep->pp.metric_ids,
num_stat_names);
if (ret < 0) {
METRICS_LOG_ERR("Could not convert stat names to IDs");
return ret;
}
ep->type = PORT_STATS;
return 0;
}
static int
rte_metrics_tel_initial_metrics_setup(void)
{
int ret;
rte_metrics_init(rte_socket_id());
if (!tel_met_data.metrics_register_done) {
ret = rte_metrics_tel_reg_all_ethdev(
&tel_met_data.metrics_register_done,
tel_met_data.reg_index);
if (ret < 0)
return ret;
}
return 0;
}
static int
handle_ports_all_stats_values(const char *cmd __rte_unused,
const char *params __rte_unused,
char *buffer, int buf_len)
{
struct telemetry_encode_param ep;
int ret, used = 0;
char *json_buffer = NULL;
ret = rte_metrics_tel_initial_metrics_setup();
if (ret < 0)
return ret;
memset(&ep, 0, sizeof(ep));
ret = rte_metrics_tel_get_port_stats_ids(&ep);
if (ret < 0)
return ret;
ret = rte_metrics_tel_get_ports_stats_json(&ep, tel_met_data.reg_index,
&json_buffer);
if (ret < 0)
return ret;
used += strlcpy(buffer, json_buffer, buf_len);
return used;
}
static int
handle_global_stats_values(const char *cmd __rte_unused,
const char *params __rte_unused,
char *buffer, int buf_len)
{
char *json_buffer = NULL;
struct telemetry_encode_param ep = { .type = GLOBAL_STATS };
int ret, used = 0;
ret = rte_metrics_tel_initial_metrics_setup();
if (ret < 0)
return ret;
ret = rte_metrics_tel_encode_json_format(&ep, &json_buffer);
if (ret < 0) {
METRICS_LOG_ERR("JSON encode function failed");
return ret;
}
used += strlcpy(buffer, json_buffer, buf_len);
return used;
}
static int
handle_ports_stats_values_by_name(const char *cmd __rte_unused,
const char *params,
char *buffer, int buf_len)
{
char *json_buffer = NULL;
struct telemetry_encode_param ep;
int ret, used = 0;
json_t *data;
json_error_t error;
ret = rte_metrics_tel_initial_metrics_setup();
if (ret < 0)
return ret;
data = json_loads(params, 0, &error);
if (!data) {
METRICS_LOG_WARN("Could not load JSON object from data passed in : %s",
error.text);
return -EPERM;
} else if (!json_is_object(data)) {
METRICS_LOG_WARN("JSON Request data is not a JSON object");
json_decref(data);
return -EINVAL;
}
ret = rte_metrics_tel_extract_data(&ep, data);
if (ret < 0) {
METRICS_LOG_ERR("Extract data function failed");
return ret;
}
ret = rte_metrics_tel_encode_json_format(&ep, &json_buffer);
if (ret < 0) {
METRICS_LOG_ERR("JSON encode function failed");
return ret;
}
used += strlcpy(buffer, json_buffer, buf_len);
return used;
}
RTE_LOG_REGISTER(metrics_log_level, lib.metrics, ERR);
RTE_INIT(metrics_ctor)
{
#ifdef RTE_LIB_TELEMETRY
rte_telemetry_legacy_register("ports_all_stat_values", DATA_NOT_REQ,
handle_ports_all_stats_values);
rte_telemetry_legacy_register("global_stat_values", DATA_NOT_REQ,
handle_global_stats_values);
rte_telemetry_legacy_register("ports_stats_values_by_name", DATA_REQ,
handle_ports_stats_values_by_name);
#endif
}
#else /* !RTE_HAS_JANSSON */
int32_t
rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, int *reg_index_list)
{
RTE_SET_USED(metrics_register_done);
RTE_SET_USED(reg_index_list);
return -ENOTSUP;
}
int32_t
rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep,
char **json_buffer)
{
RTE_SET_USED(ep);
RTE_SET_USED(json_buffer);
return -ENOTSUP;
}
int32_t
rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep,
int *reg_index, char **json_buffer)
{
RTE_SET_USED(ep);
RTE_SET_USED(reg_index);
RTE_SET_USED(json_buffer);
return -ENOTSUP;
}
int32_t
rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep)
{
RTE_SET_USED(ep);
return -ENOTSUP;
}
int32_t
rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data)
{
RTE_SET_USED(ep);
RTE_SET_USED(data);
return -ENOTSUP;
}
int32_t
rte_metrics_tel_get_global_stats(struct telemetry_encode_param *ep)
{
RTE_SET_USED(ep);
return -ENOTSUP;
}
#endif /* !RTE_HAS_JANSSON */