diff options
Diffstat (limited to 'src/spdk/dpdk/lib/librte_metrics')
-rw-r--r-- | src/spdk/dpdk/lib/librte_metrics/Makefile | 30 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_metrics/meson.build | 16 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_metrics/rte_metrics.c | 307 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_metrics/rte_metrics.h | 241 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_metrics/rte_metrics_telemetry.c | 541 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_metrics/rte_metrics_telemetry.h | 65 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_metrics/rte_metrics_version.map | 26 |
7 files changed, 1226 insertions, 0 deletions
diff --git a/src/spdk/dpdk/lib/librte_metrics/Makefile b/src/spdk/dpdk/lib/librte_metrics/Makefile new file mode 100644 index 000000000..1264d3bbb --- /dev/null +++ b/src/spdk/dpdk/lib/librte_metrics/Makefile @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_metrics.a + +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3 +LDLIBS += -lrte_eal + +EXPORT_MAP := rte_metrics_version.map + +# all source are stored in SRCS-y +SRCS-$(CONFIG_RTE_LIBRTE_METRICS) := rte_metrics.c + +ifeq ($(CONFIG_RTE_LIBRTE_TELEMETRY),y) +SRCS-y += rte_metrics_telemetry.c +SYMLINK-$(CONFIG_RTE_LIBRTE_METRICS)-include += rte_metrics_telemetry.h + +LDLIBS += -lrte_ethdev -lrte_telemetry +LDLIBS += -ljansson + +CFLAGS += -I$(RTE_SDK)/lib/librte_telemetry/ +endif + +# Install header file +SYMLINK-$(CONFIG_RTE_LIBRTE_METRICS)-include += rte_metrics.h + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/src/spdk/dpdk/lib/librte_metrics/meson.build b/src/spdk/dpdk/lib/librte_metrics/meson.build new file mode 100644 index 000000000..6527a2464 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_metrics/meson.build @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2017 Intel Corporation + +sources = files('rte_metrics.c') +headers = files('rte_metrics.h') + +jansson = dependency('jansson', required: false) +if jansson.found() + ext_deps += jansson + sources += files('rte_metrics_telemetry.c') + headers = files('rte_metrics_telemetry.h') + deps += ['ethdev', 'telemetry'] + includes += include_directories('../librte_telemetry') +endif +build = false +reason = 'not needed by SPDK' diff --git a/src/spdk/dpdk/lib/librte_metrics/rte_metrics.c b/src/spdk/dpdk/lib/librte_metrics/rte_metrics.c new file mode 100644 index 000000000..e2a0fbeda --- /dev/null +++ b/src/spdk/dpdk/lib/librte_metrics/rte_metrics.c @@ -0,0 +1,307 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Intel Corporation + */ + +#include <string.h> +#include <sys/queue.h> + +#include <rte_common.h> +#include <rte_string_fns.h> +#include <rte_malloc.h> +#include <rte_metrics.h> +#include <rte_lcore.h> +#include <rte_memzone.h> +#include <rte_spinlock.h> + +int metrics_initialized; + +#define RTE_METRICS_MEMZONE_NAME "RTE_METRICS" + +/** + * Internal stats metadata and value entry. + * + * @internal + */ +struct rte_metrics_meta_s { + /** Name of metric */ + char name[RTE_METRICS_MAX_NAME_LEN]; + /** Current value for metric */ + uint64_t value[RTE_MAX_ETHPORTS]; + /** Used for global metrics */ + uint64_t global_value; + /** Index of next root element (zero for none) */ + uint16_t idx_next_set; + /** Index of next metric in set (zero for none) */ + uint16_t idx_next_stat; +}; + +/** + * Internal stats info structure. + * + * @internal + * Offsets into metadata are used instead of pointers because ASLR + * means that having the same physical addresses in different + * processes is not guaranteed. + */ +struct rte_metrics_data_s { + /** Index of last metadata entry with valid data. + * This value is not valid if cnt_stats is zero. + */ + uint16_t idx_last_set; + /** Number of metrics. */ + uint16_t cnt_stats; + /** Metric data memory block. */ + struct rte_metrics_meta_s metadata[RTE_METRICS_MAX_METRICS]; + /** Metric data access lock */ + rte_spinlock_t lock; +}; + +void +rte_metrics_init(int socket_id) +{ + struct rte_metrics_data_s *stats; + const struct rte_memzone *memzone; + + if (metrics_initialized) + return; + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return; + + memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME); + if (memzone != NULL) + return; + memzone = rte_memzone_reserve(RTE_METRICS_MEMZONE_NAME, + sizeof(struct rte_metrics_data_s), socket_id, 0); + if (memzone == NULL) + rte_exit(EXIT_FAILURE, "Unable to allocate stats memzone\n"); + stats = memzone->addr; + memset(stats, 0, sizeof(struct rte_metrics_data_s)); + rte_spinlock_init(&stats->lock); + metrics_initialized = 1; +} + +int +rte_metrics_deinit(void) +{ + struct rte_metrics_data_s *stats; + const struct rte_memzone *memzone; + int ret; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return -EINVAL; + + memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME); + if (memzone == NULL) + return -EIO; + + stats = memzone->addr; + memset(stats, 0, sizeof(struct rte_metrics_data_s)); + + ret = rte_memzone_free(memzone); + if (ret == 0) + metrics_initialized = 0; + return ret; +} + +int +rte_metrics_reg_name(const char *name) +{ + const char * const list_names[] = {name}; + + return rte_metrics_reg_names(list_names, 1); +} + +int +rte_metrics_reg_names(const char * const *names, uint16_t cnt_names) +{ + struct rte_metrics_meta_s *entry = NULL; + struct rte_metrics_data_s *stats; + const struct rte_memzone *memzone; + uint16_t idx_name; + uint16_t idx_base; + + /* Some sanity checks */ + if (cnt_names < 1 || names == NULL) + return -EINVAL; + for (idx_name = 0; idx_name < cnt_names; idx_name++) + if (names[idx_name] == NULL) + return -EINVAL; + + memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME); + if (memzone == NULL) + return -EIO; + stats = memzone->addr; + + if (stats->cnt_stats + cnt_names >= RTE_METRICS_MAX_METRICS) + return -ENOMEM; + + rte_spinlock_lock(&stats->lock); + + /* Overwritten later if this is actually first set.. */ + stats->metadata[stats->idx_last_set].idx_next_set = stats->cnt_stats; + + stats->idx_last_set = idx_base = stats->cnt_stats; + + for (idx_name = 0; idx_name < cnt_names; idx_name++) { + entry = &stats->metadata[idx_name + stats->cnt_stats]; + strlcpy(entry->name, names[idx_name], RTE_METRICS_MAX_NAME_LEN); + memset(entry->value, 0, sizeof(entry->value)); + entry->idx_next_stat = idx_name + stats->cnt_stats + 1; + } + entry->idx_next_stat = 0; + entry->idx_next_set = 0; + stats->cnt_stats += cnt_names; + + rte_spinlock_unlock(&stats->lock); + + return idx_base; +} + +int +rte_metrics_update_value(int port_id, uint16_t key, const uint64_t value) +{ + return rte_metrics_update_values(port_id, key, &value, 1); +} + +int +rte_metrics_update_values(int port_id, + uint16_t key, + const uint64_t *values, + uint32_t count) +{ + struct rte_metrics_meta_s *entry; + struct rte_metrics_data_s *stats; + const struct rte_memzone *memzone; + uint16_t idx_metric; + uint16_t idx_value; + uint16_t cnt_setsize; + + if (port_id != RTE_METRICS_GLOBAL && + (port_id < 0 || port_id >= RTE_MAX_ETHPORTS)) + return -EINVAL; + + if (values == NULL) + return -EINVAL; + + memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME); + if (memzone == NULL) + return -EIO; + stats = memzone->addr; + + rte_spinlock_lock(&stats->lock); + + if (key >= stats->cnt_stats) { + rte_spinlock_unlock(&stats->lock); + return -EINVAL; + } + idx_metric = key; + cnt_setsize = 1; + while (idx_metric < stats->cnt_stats) { + entry = &stats->metadata[idx_metric]; + if (entry->idx_next_stat == 0) + break; + cnt_setsize++; + idx_metric++; + } + /* Check update does not cross set border */ + if (count > cnt_setsize) { + rte_spinlock_unlock(&stats->lock); + return -ERANGE; + } + + if (port_id == RTE_METRICS_GLOBAL) + for (idx_value = 0; idx_value < count; idx_value++) { + idx_metric = key + idx_value; + stats->metadata[idx_metric].global_value = + values[idx_value]; + } + else + for (idx_value = 0; idx_value < count; idx_value++) { + idx_metric = key + idx_value; + stats->metadata[idx_metric].value[port_id] = + values[idx_value]; + } + rte_spinlock_unlock(&stats->lock); + return 0; +} + +int +rte_metrics_get_names(struct rte_metric_name *names, + uint16_t capacity) +{ + struct rte_metrics_data_s *stats; + const struct rte_memzone *memzone; + uint16_t idx_name; + int return_value; + + memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME); + if (memzone == NULL) + return -EIO; + + stats = memzone->addr; + rte_spinlock_lock(&stats->lock); + if (names != NULL) { + if (capacity < stats->cnt_stats) { + return_value = stats->cnt_stats; + rte_spinlock_unlock(&stats->lock); + return return_value; + } + for (idx_name = 0; idx_name < stats->cnt_stats; idx_name++) + strlcpy(names[idx_name].name, + stats->metadata[idx_name].name, + RTE_METRICS_MAX_NAME_LEN); + } + return_value = stats->cnt_stats; + rte_spinlock_unlock(&stats->lock); + return return_value; +} + +int +rte_metrics_get_values(int port_id, + struct rte_metric_value *values, + uint16_t capacity) +{ + struct rte_metrics_meta_s *entry; + struct rte_metrics_data_s *stats; + const struct rte_memzone *memzone; + uint16_t idx_name; + int return_value; + + if (port_id != RTE_METRICS_GLOBAL && + (port_id < 0 || port_id >= RTE_MAX_ETHPORTS)) + return -EINVAL; + + memzone = rte_memzone_lookup(RTE_METRICS_MEMZONE_NAME); + if (memzone == NULL) + return -EIO; + + stats = memzone->addr; + rte_spinlock_lock(&stats->lock); + + if (values != NULL) { + if (capacity < stats->cnt_stats) { + return_value = stats->cnt_stats; + rte_spinlock_unlock(&stats->lock); + return return_value; + } + if (port_id == RTE_METRICS_GLOBAL) + for (idx_name = 0; + idx_name < stats->cnt_stats; + idx_name++) { + entry = &stats->metadata[idx_name]; + values[idx_name].key = idx_name; + values[idx_name].value = entry->global_value; + } + else + for (idx_name = 0; + idx_name < stats->cnt_stats; + idx_name++) { + entry = &stats->metadata[idx_name]; + values[idx_name].key = idx_name; + values[idx_name].value = entry->value[port_id]; + } + } + return_value = stats->cnt_stats; + rte_spinlock_unlock(&stats->lock); + return return_value; +} diff --git a/src/spdk/dpdk/lib/librte_metrics/rte_metrics.h b/src/spdk/dpdk/lib/librte_metrics/rte_metrics.h new file mode 100644 index 000000000..fbe64ddf2 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_metrics/rte_metrics.h @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Intel Corporation + */ + +/** + * @file + * + * DPDK Metrics module + * + * Metrics are statistics that are not generated by PMDs, and hence + * are better reported through a mechanism that is independent from + * the ethdev-based extended statistics. Providers will typically + * be other libraries and consumers will typically be applications. + * + * Metric information is populated using a push model, where producers + * update the values contained within the metric library by calling + * an update function on the relevant metrics. Consumers receive + * metric information by querying the central metric data, which is + * held in shared memory. Currently only bulk querying of metrics + * by consumers is supported. + */ + +#ifndef _RTE_METRICS_H_ +#define _RTE_METRICS_H_ + +#include <stdint.h> +#include <rte_compat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern int metrics_initialized; + +/** Maximum length of metric name (including null-terminator) */ +#define RTE_METRICS_MAX_NAME_LEN 64 +#define RTE_METRICS_MAX_METRICS 256 + +/** + * Global metric special id. + * + * When used for the port_id parameter when calling + * rte_metrics_update_metric() or rte_metrics_update_metric(), + * the global metric, which are not associated with any specific + * port (i.e. device), are updated. + */ +#define RTE_METRICS_GLOBAL -1 + +/** + * A name-key lookup for metrics. + * + * An array of this structure is returned by rte_metrics_get_names(). + * The struct rte_metric_value references these names via their array index. + */ +struct rte_metric_name { + /** String describing metric */ + char name[RTE_METRICS_MAX_NAME_LEN]; +}; + + +/** + * Metric value structure. + * + * This structure is used by rte_metrics_get_values() to return metrics, + * which are statistics that are not generated by PMDs. It maps a name key, + * which corresponds to an index in the array returned by + * rte_metrics_get_names(). + */ +struct rte_metric_value { + /** Numeric identifier of metric. */ + uint16_t key; + /** Value for metric */ + uint64_t value; +}; + +/** + * Initializes metric module. This function must be called from + * a primary process before metrics are used. + * + * @param socket_id + * Socket to use for shared memory allocation. + */ +void rte_metrics_init(int socket_id); + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Deinitialize metric module. This function must be called from + * a primary process after all the metrics usage is over, to + * release the shared memory. + * + * @return + * -EINVAL - invalid parameter. + * -EIO: Error, unable to access metrics shared memory + * (rte_metrics_init() not called) + * 0 - success + */ +__rte_experimental +int rte_metrics_deinit(void); + +/** + * Register a metric, making it available as a reporting parameter. + * + * Registering a metric is the way producers declare a parameter + * that they wish to be reported. Once registered, the associated + * numeric key can be obtained via rte_metrics_get_names(), which + * is required for updating said metric's value. + * + * @param name + * Metric name. If this exceeds RTE_METRICS_MAX_NAME_LEN (including + * the NULL terminator), it is truncated. + * + * @return + * - Zero or positive: Success (index key of new metric) + * - -EIO: Error, unable to access metrics shared memory + * (rte_metrics_init() not called) + * - -EINVAL: Error, invalid parameters + * - -ENOMEM: Error, maximum metrics reached + */ +int rte_metrics_reg_name(const char *name); + +/** + * Register a set of metrics. + * + * This is a bulk version of rte_metrics_reg_names() and aside from + * handling multiple keys at once is functionally identical. + * + * @param names + * List of metric names + * + * @param cnt_names + * Number of metrics in set + * + * @return + * - Zero or positive: Success (index key of start of set) + * - -EIO: Error, unable to access metrics shared memory + * (rte_metrics_init() not called) + * - -EINVAL: Error, invalid parameters + * - -ENOMEM: Error, maximum metrics reached + */ +int rte_metrics_reg_names(const char * const *names, uint16_t cnt_names); + +/** + * Get metric name-key lookup table. + * + * @param names + * A struct rte_metric_name array of at least *capacity* in size to + * receive key names. If this is NULL, function returns the required + * number of elements for this array. + * + * @param capacity + * Size (number of elements) of struct rte_metric_name array. + * Disregarded if names is NULL. + * + * @return + * - Positive value above capacity: error, *names* is too small. + * Return value is required size. + * - Positive value equal or less than capacity: Success. Return + * value is number of elements filled in. + * - Negative value: error. + */ +int rte_metrics_get_names( + struct rte_metric_name *names, + uint16_t capacity); + +/** + * Get metric value table. + * + * @param port_id + * Port id to query + * + * @param values + * A struct rte_metric_value array of at least *capacity* in size to + * receive metric ids and values. If this is NULL, function returns + * the required number of elements for this array. + * + * @param capacity + * Size (number of elements) of struct rte_metric_value array. + * Disregarded if names is NULL. + * + * @return + * - Positive value above capacity: error, *values* is too small. + * Return value is required size. + * - Positive value equal or less than capacity: Success. Return + * value is number of elements filled in. + * - Negative value: error. + */ +int rte_metrics_get_values( + int port_id, + struct rte_metric_value *values, + uint16_t capacity); + +/** + * Updates a metric + * + * @param port_id + * Port to update metrics for + * @param key + * Id of metric to update + * @param value + * New value + * + * @return + * - -EIO if unable to access shared metrics memory + * - Zero on success + */ +int rte_metrics_update_value( + int port_id, + uint16_t key, + const uint64_t value); + +/** + * Updates a metric set. Note that it is an error to try to + * update across a set boundary. + * + * @param port_id + * Port to update metrics for + * @param key + * Base id of metrics set to update + * @param values + * Set of new values + * @param count + * Number of new values + * + * @return + * - -ERANGE if count exceeds metric set size + * - -EIO if unable to access shared metrics memory + * - Zero on success + */ +int rte_metrics_update_values( + int port_id, + uint16_t key, + const uint64_t *values, + uint32_t count); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/spdk/dpdk/lib/librte_metrics/rte_metrics_telemetry.c b/src/spdk/dpdk/lib/librte_metrics/rte_metrics_telemetry.c new file mode 100644 index 000000000..040e87d18 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_metrics/rte_metrics_telemetry.c @@ -0,0 +1,541 @@ +/* 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_LIBRTE_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"); + return -ENOMEM; + } + + 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_INIT(metrics_ctor) +{ +#ifdef RTE_LIBRTE_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 + metrics_log_level = rte_log_register("lib.metrics"); + if (metrics_log_level >= 0) + rte_log_set_level(metrics_log_level, RTE_LOG_ERR); +} diff --git a/src/spdk/dpdk/lib/librte_metrics/rte_metrics_telemetry.h b/src/spdk/dpdk/lib/librte_metrics/rte_metrics_telemetry.h new file mode 100644 index 000000000..6c2391c56 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_metrics/rte_metrics_telemetry.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ + +#ifdef RTE_LIBRTE_TELEMETRY +#include <jansson.h> +#else +#define json_t void * +#endif + +#include "rte_metrics.h" + +#ifndef _RTE_METRICS_TELEMETRY_H_ +#define _RTE_METRICS_TELEMETRY_H_ + + +enum rte_telemetry_stats_type { + PORT_STATS = 0, + GLOBAL_STATS = 1 +}; + +struct telemetry_encode_param { + enum rte_telemetry_stats_type type; + struct port_param { + int num_metric_ids; + uint32_t metric_ids[RTE_METRICS_MAX_METRICS]; + int num_port_ids; + uint32_t port_ids[RTE_MAX_ETHPORTS]; + } pp; +}; + +struct telemetry_metrics_data { + int reg_index[RTE_MAX_ETHPORTS]; + int metrics_register_done; +}; + +struct telemetry_metrics_data tel_met_data; + +__rte_experimental +int32_t rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, + int *reg_index_list); + +__rte_experimental +int32_t +rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep, + char **json_buffer); + +__rte_experimental +int32_t +rte_metrics_tel_get_global_stats(struct telemetry_encode_param *ep); + +__rte_experimental +int32_t +rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep); + +__rte_experimental +int32_t +rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep, + int *reg_index, char **json_buffer); + +__rte_experimental +int32_t +rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data); + +#endif diff --git a/src/spdk/dpdk/lib/librte_metrics/rte_metrics_version.map b/src/spdk/dpdk/lib/librte_metrics/rte_metrics_version.map new file mode 100644 index 000000000..c88939b11 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_metrics/rte_metrics_version.map @@ -0,0 +1,26 @@ +DPDK_20.0 { + global: + + rte_metrics_get_names; + rte_metrics_get_values; + rte_metrics_init; + rte_metrics_reg_name; + rte_metrics_reg_names; + rte_metrics_update_value; + rte_metrics_update_values; + + local: *; +}; + +EXPERIMENTAL { + global: + + rte_metrics_deinit; + rte_metrics_tel_encode_json_format; + rte_metrics_tel_reg_all_ethdev; + rte_metrics_tel_get_global_stats; + rte_metrics_tel_get_port_stats_ids; + rte_metrics_tel_get_ports_stats_json; + rte_metrics_tel_extract_data; + +}; |