a5c79ad0f0
If an error occurred when allocating memory for metrics or names,
the function returned without freeing allocated memory. This is now
fixed to avoid the resource leak in the case that either metrics or
names had been successfully allocated memory.
Coverity issue: 362053
Fixes: c5b7197f66
("telemetry: move some functions to metrics library")
Cc: stable@dpdk.org
Reported-by: Gaurav Singh <gaurav1086@gmail.com>
Signed-off-by: Ciara Power <ciara.power@intel.com>
Acked-by: John McNamara <john.mcnamara@intel.com>
542 lines
13 KiB
C
542 lines
13 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2020 Intel Corporation
|
|
*/
|
|
|
|
#include <jansson.h>
|
|
|
|
#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"
|
|
|
|
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
|
|
}
|