diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/spdk/dpdk/lib/librte_telemetry | |
parent | Initial commit. (diff) | |
download | ceph-upstream.tar.xz ceph-upstream.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/dpdk/lib/librte_telemetry')
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/Makefile | 30 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/meson.build | 8 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/rte_telemetry.h | 265 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/rte_telemetry_legacy.h | 87 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/rte_telemetry_version.map | 18 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/telemetry.c | 412 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/telemetry_data.c | 130 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/telemetry_data.h | 46 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/telemetry_json.h | 158 | ||||
-rw-r--r-- | src/spdk/dpdk/lib/librte_telemetry/telemetry_legacy.c | 241 |
10 files changed, 1395 insertions, 0 deletions
diff --git a/src/spdk/dpdk/lib/librte_telemetry/Makefile b/src/spdk/dpdk/lib/librte_telemetry/Makefile new file mode 100644 index 000000000..c62cbd86d --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/Makefile @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_telemetry.a + +ARCH_DIR ?= $(RTE_ARCH) + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) +CFLAGS += -I$(RTE_SDK)/lib/librte_metrics/ +CFLAGS += -I$(RTE_SDK)/lib/librte_eal/include +CFLAGS += -I$(RTE_SDK)/lib/librte_eal/$(ARCH_DIR)/include +CFLAGS += -pthread + +LDLIBS += -lpthread + +EXPORT_MAP := rte_telemetry_version.map + +# library source files +SRCS-y += telemetry.c +SRCS-y += telemetry_data.c +SRCS-y += telemetry_legacy.c + +# export include files +SYMLINK-y-include := rte_telemetry.h + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/src/spdk/dpdk/lib/librte_telemetry/meson.build b/src/spdk/dpdk/lib/librte_telemetry/meson.build new file mode 100644 index 000000000..719973ff9 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +includes = [global_inc] + +sources = files('telemetry.c', 'telemetry_data.c', 'telemetry_legacy.c') +headers = files('rte_telemetry.h') +includes += include_directories('../librte_metrics') diff --git a/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry.h b/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry.h new file mode 100644 index 000000000..eb7f2c917 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry.h @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include <stdint.h> +#include <rte_compat.h> + +#ifndef _RTE_TELEMETRY_H_ +#define _RTE_TELEMETRY_H_ + +/** Maximum number of telemetry callbacks. */ +#define TELEMETRY_MAX_CALLBACKS 64 +/** Maximum length for string used in object. */ +#define RTE_TEL_MAX_STRING_LEN 64 +/** Maximum length of string. */ +#define RTE_TEL_MAX_SINGLE_STRING_LEN 8192 +/** Maximum number of dictionary entries. */ +#define RTE_TEL_MAX_DICT_ENTRIES 256 +/** Maximum number of array entries. */ +#define RTE_TEL_MAX_ARRAY_ENTRIES 512 + +/** + * @warning + * @b EXPERIMENTAL: all functions in this file may change without prior notice + * + * @file + * RTE Telemetry + * + * The telemetry library provides a method to retrieve statistics from + * DPDK by sending a request message over a socket. DPDK will send + * a JSON encoded response containing telemetry data. + ***/ + +/** opaque structure used internally for managing data from callbacks */ +struct rte_tel_data; + +/** + * The types of data that can be managed in arrays or dicts. + * For arrays, this must be specified at creation time, while for + * dicts this is specified implicitly each time an element is added + * via calling a type-specific function. + */ +enum rte_tel_value_type { + RTE_TEL_STRING_VAL, /** a string value */ + RTE_TEL_INT_VAL, /** a signed 32-bit int value */ + RTE_TEL_U64_VAL, /** an unsigned 64-bit int value */ +}; + +/** + * Start an array of the specified type for returning from a callback + * + * @param d + * The data structure passed to the callback + * @param type + * The type of the array of data + * @return + * 0 on success, negative errno on error + */ +__rte_experimental +int +rte_tel_data_start_array(struct rte_tel_data *d, enum rte_tel_value_type type); + +/** + * Start a dictionary of values for returning from a callback + * + * @param d + * The data structure passed to the callback + * @return + * 0 on success, negative errno on error + */ +__rte_experimental +int +rte_tel_data_start_dict(struct rte_tel_data *d); + +/** + * Set a string for returning from a callback + * + * @param d + * The data structure passed to the callback + * @param str + * The string to be returned in the data structure + * @return + * 0 on success, negative errno on error, E2BIG on string truncation + */ +__rte_experimental +int +rte_tel_data_string(struct rte_tel_data *d, const char *str); + +/** + * Add a string to an array. + * The array must have been started by rte_tel_data_start_array() with + * RTE_TEL_STRING_VAL as the type parameter. + * + * @param d + * The data structure passed to the callback + * @param str + * The string to be returned in the array + * @return + * 0 on success, negative errno on error, E2BIG on string truncation + */ +__rte_experimental +int +rte_tel_data_add_array_string(struct rte_tel_data *d, const char *str); + +/** + * Add an int to an array. + * The array must have been started by rte_tel_data_start_array() with + * RTE_TEL_INT_VAL as the type parameter. + * + * @param d + * The data structure passed to the callback + * @param x + * The number to be returned in the array + * @return + * 0 on success, negative errno on error + */ +__rte_experimental +int +rte_tel_data_add_array_int(struct rte_tel_data *d, int x); + +/** + * Add a uint64_t to an array. + * The array must have been started by rte_tel_data_start_array() with + * RTE_TEL_U64_VAL as the type parameter. + * + * @param d + * The data structure passed to the callback + * @param x + * The number to be returned in the array + * @return + * 0 on success, negative errno on error + */ +__rte_experimental +int +rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x); + +/** + * Add a string value to a dictionary. + * The dict must have been started by rte_tel_data_start_dict(). + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict + * @param val + * The string to be stored in the dict + * @return + * 0 on success, negative errno on error, E2BIG on string truncation of + * either name or value. + */ +__rte_experimental +int +rte_tel_data_add_dict_string(struct rte_tel_data *d, const char *name, + const char *val); + +/** + * Add an int value to a dictionary. + * The dict must have been started by rte_tel_data_start_dict(). + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict + * @param val + * The number to be stored in the dict + * @return + * 0 on success, negative errno on error, E2BIG on string truncation of name. + */ +__rte_experimental +int +rte_tel_data_add_dict_int(struct rte_tel_data *d, const char *name, int val); + +/** + * Add a uint64_t value to a dictionary. + * The dict must have been started by rte_tel_data_start_dict(). + * + * @param d + * The data structure passed to the callback + * @param name + * The name the value is to be stored under in the dict + * @param val + * The number to be stored in the dict + * @return + * 0 on success, negative errno on error, E2BIG on string truncation of name. + */ +__rte_experimental +int +rte_tel_data_add_dict_u64(struct rte_tel_data *d, + const char *name, uint64_t val); + +/** + * This telemetry callback is used when registering a telemetry command. + * It handles getting and formatting information to be returned to telemetry + * when requested. + * + * @param cmd + * The cmd that was requested by the client. + * @param params + * Contains data required by the callback function. + * @param info + * The information to be returned to the caller. + * + * @return + * Length of buffer used on success. + * @return + * Negative integer on error. + */ +typedef int (*telemetry_cb)(const char *cmd, const char *params, + struct rte_tel_data *info); + +/** + * Used for handling data received over a telemetry socket. + * + * @param sock_id + * ID for the socket to be used by the handler. + * + * @return + * Void. + */ +typedef void * (*handler)(void *sock_id); + +/** + * Used when registering a command and callback function with telemetry. + * + * @param cmd + * The command to register with telemetry. + * @param fn + * Callback function to be called when the command is requested. + * @param help + * Help text for the command. + * + * @return + * 0 on success. + * @return + * -EINVAL for invalid parameters failure. + * @return + * -ENOENT if max callbacks limit has been reached. + */ +__rte_experimental +int +rte_telemetry_register_cmd(const char *cmd, telemetry_cb fn, const char *help); + +/** + * @internal + * Initialize Telemetry. + * + * @param runtime_dir + * The runtime directory of DPDK. + * @param cpuset + * The CPU set to be used for setting the thread affinity. + * @param err_str + * This err_str pointer should point to NULL on entry. In the case of an error + * or warning, it will be non-NULL on exit. + * + * @return + * 0 on success. + * @return + * -1 on failure. + */ +__rte_experimental +int +rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, + const char **err_str); + +#endif diff --git a/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry_legacy.h b/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry_legacy.h new file mode 100644 index 000000000..c83f9a8d9 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry_legacy.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ + +#ifndef _RTE_TELEMETRY_LEGACY_H_ +#define _RTE_TELEMETRY_LEGACY_H_ + +#include <rte_compat.h> +#include "rte_telemetry.h" + +/** + * @internal + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + + * @file + * RTE Telemetry Legacy + * + ***/ + +/** + * @internal + * Value representing if data is required for the command + */ +enum rte_telemetry_legacy_data_req { + DATA_NOT_REQ = 0, + DATA_REQ +}; + +/** + * This telemetry callback is used when registering a legacy telemetry command. + * It handles getting and formatting stats to be returned to telemetry when + * requested. Stats up to buf_len in length are put in the buffer. + * + * @param cmd + * The cmd that was requested by the client. + * @param params + * Contains data required by the callback function. + * @param buffer + * A buffer to hold the formatted response. + * @param buf_len + * Length of the buffer. + * + * @return + * Length of buffer used on success. + * @return + * Negative integer on error. + */ +typedef int (*telemetry_legacy_cb)(const char *cmd, const char *params, + char *buffer, int buf_len); + +/** + * @internal + * Counter for the number of registered legacy callbacks + */ +extern int num_legacy_callbacks; + +/** + * @internal + * Used for handling data received over the legacy telemetry socket. + * + * @return + * Void. + */ +void * +legacy_client_handler(void *sock_id); + +/** + * @internal + * + * Used when registering a command and callback function with + * telemetry legacy support. + * + * @return + * 0 on success. + * @return + * -EINVAL for invalid parameters failure. + * @return + * -ENOENT if max callbacks limit has been reached. + */ +__rte_experimental +int +rte_telemetry_legacy_register(const char *cmd, + enum rte_telemetry_legacy_data_req data_req, + telemetry_legacy_cb fn); + +#endif diff --git a/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry_version.map b/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry_version.map new file mode 100644 index 000000000..86433c21d --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/rte_telemetry_version.map @@ -0,0 +1,18 @@ +EXPERIMENTAL { + global: + + rte_tel_data_add_array_int; + rte_tel_data_add_array_string; + rte_tel_data_add_array_u64; + rte_tel_data_add_dict_int; + rte_tel_data_add_dict_string; + rte_tel_data_add_dict_u64; + rte_tel_data_start_array; + rte_tel_data_start_dict; + rte_tel_data_string; + rte_telemetry_init; + rte_telemetry_legacy_register; + rte_telemetry_register_cmd; + + local: *; +}; diff --git a/src/spdk/dpdk/lib/librte_telemetry/telemetry.c b/src/spdk/dpdk/lib/librte_telemetry/telemetry.c new file mode 100644 index 000000000..e7e3d861d --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/telemetry.c @@ -0,0 +1,412 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ + +#include <unistd.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <dlfcn.h> + +/* we won't link against libbsd, so just always use DPDKs-specific strlcpy */ +#undef RTE_USE_LIBBSD +#include <rte_string_fns.h> +#include <rte_common.h> +#include <rte_spinlock.h> +#include <rte_version.h> + +#include "rte_telemetry.h" +#include "telemetry_json.h" +#include "telemetry_data.h" +#include "rte_telemetry_legacy.h" + +#define MAX_CMD_LEN 56 +#define MAX_HELP_LEN 64 +#define MAX_OUTPUT_LEN (1024 * 16) + +static void * +client_handler(void *socket); + +struct cmd_callback { + char cmd[MAX_CMD_LEN]; + telemetry_cb fn; + char help[MAX_HELP_LEN]; +}; + +struct socket { + int sock; + char path[sizeof(((struct sockaddr_un *)0)->sun_path)]; + handler fn; +}; +static struct socket v2_socket; /* socket for v2 telemetry */ +static struct socket v1_socket; /* socket for v1 telemetry */ +static char telemetry_log_error[1024]; /* Will contain error on init failure */ +/* list of command callbacks, with one command registered by default */ +static struct cmd_callback callbacks[TELEMETRY_MAX_CALLBACKS]; +static int num_callbacks; /* How many commands are registered */ +/* Used when accessing or modifying list of command callbacks */ +static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER; + +int +rte_telemetry_register_cmd(const char *cmd, telemetry_cb fn, const char *help) +{ + int i = 0; + + if (strlen(cmd) >= MAX_CMD_LEN || fn == NULL || cmd[0] != '/' + || strlen(help) >= MAX_HELP_LEN) + return -EINVAL; + if (num_callbacks >= TELEMETRY_MAX_CALLBACKS) + return -ENOENT; + + rte_spinlock_lock(&callback_sl); + while (i < num_callbacks && strcmp(cmd, callbacks[i].cmd) > 0) + i++; + if (i != num_callbacks) + /* Move elements to keep the list alphabetical */ + memmove(callbacks + i + 1, callbacks + i, + sizeof(struct cmd_callback) * (num_callbacks - i)); + + strlcpy(callbacks[i].cmd, cmd, MAX_CMD_LEN); + callbacks[i].fn = fn; + strlcpy(callbacks[i].help, help, MAX_HELP_LEN); + num_callbacks++; + rte_spinlock_unlock(&callback_sl); + + return 0; +} + +static int +list_commands(const char *cmd __rte_unused, const char *params __rte_unused, + struct rte_tel_data *d) +{ + int i; + + rte_tel_data_start_array(d, RTE_TEL_STRING_VAL); + for (i = 0; i < num_callbacks; i++) + rte_tel_data_add_array_string(d, callbacks[i].cmd); + return 0; +} + +static int +json_info(const char *cmd __rte_unused, const char *params __rte_unused, + struct rte_tel_data *d) +{ + rte_tel_data_start_dict(d); + rte_tel_data_add_dict_string(d, "version", rte_version()); + rte_tel_data_add_dict_int(d, "pid", getpid()); + rte_tel_data_add_dict_int(d, "max_output_len", MAX_OUTPUT_LEN); + return 0; +} + +static int +command_help(const char *cmd __rte_unused, const char *params, + struct rte_tel_data *d) +{ + int i; + + if (!params) + return -1; + rte_tel_data_start_dict(d); + rte_spinlock_lock(&callback_sl); + for (i = 0; i < num_callbacks; i++) + if (strcmp(params, callbacks[i].cmd) == 0) { + rte_tel_data_add_dict_string(d, params, + callbacks[i].help); + break; + } + rte_spinlock_unlock(&callback_sl); + if (i == num_callbacks) + return -1; + return 0; +} + +static void +output_json(const char *cmd, const struct rte_tel_data *d, int s) +{ + char out_buf[MAX_OUTPUT_LEN]; + + char *cb_data_buf; + size_t buf_len, prefix_used, used = 0; + unsigned int i; + + RTE_BUILD_BUG_ON(sizeof(out_buf) < MAX_CMD_LEN + + RTE_TEL_MAX_SINGLE_STRING_LEN + 10); + switch (d->type) { + case RTE_TEL_NULL: + used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":null}", + MAX_CMD_LEN, cmd ? cmd : "none"); + break; + case RTE_TEL_STRING: + used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":\"%.*s\"}", + MAX_CMD_LEN, cmd, + RTE_TEL_MAX_SINGLE_STRING_LEN, d->data.str); + break; + case RTE_TEL_DICT: + prefix_used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":", + MAX_CMD_LEN, cmd); + cb_data_buf = &out_buf[prefix_used]; + buf_len = sizeof(out_buf) - prefix_used - 1; /* space for '}' */ + + used = rte_tel_json_empty_obj(cb_data_buf, buf_len, 0); + for (i = 0; i < d->data_len; i++) { + const struct tel_dict_entry *v = &d->data.dict[i]; + switch (v->type) { + case RTE_TEL_STRING_VAL: + used = rte_tel_json_add_obj_str(cb_data_buf, + buf_len, used, + v->name, v->value.sval); + break; + case RTE_TEL_INT_VAL: + used = rte_tel_json_add_obj_int(cb_data_buf, + buf_len, used, + v->name, v->value.ival); + break; + case RTE_TEL_U64_VAL: + used = rte_tel_json_add_obj_u64(cb_data_buf, + buf_len, used, + v->name, v->value.u64val); + break; + } + } + used += prefix_used; + used += strlcat(out_buf + used, "}", sizeof(out_buf) - used); + break; + case RTE_TEL_ARRAY_STRING: + case RTE_TEL_ARRAY_INT: + case RTE_TEL_ARRAY_U64: + prefix_used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":", + MAX_CMD_LEN, cmd); + cb_data_buf = &out_buf[prefix_used]; + buf_len = sizeof(out_buf) - prefix_used - 1; /* space for '}' */ + + used = rte_tel_json_empty_array(cb_data_buf, buf_len, 0); + for (i = 0; i < d->data_len; i++) + if (d->type == RTE_TEL_ARRAY_STRING) + used = rte_tel_json_add_array_string( + cb_data_buf, + buf_len, used, + d->data.array[i].sval); + else if (d->type == RTE_TEL_ARRAY_INT) + used = rte_tel_json_add_array_int(cb_data_buf, + buf_len, used, + d->data.array[i].ival); + else if (d->type == RTE_TEL_ARRAY_U64) + used = rte_tel_json_add_array_u64(cb_data_buf, + buf_len, used, + d->data.array[i].u64val); + used += prefix_used; + used += strlcat(out_buf + used, "}", sizeof(out_buf) - used); + break; + } + if (write(s, out_buf, used) < 0) + perror("Error writing to socket"); +} + +static void +perform_command(telemetry_cb fn, const char *cmd, const char *param, int s) +{ + struct rte_tel_data data; + + int ret = fn(cmd, param, &data); + if (ret < 0) { + char out_buf[MAX_CMD_LEN + 10]; + int used = snprintf(out_buf, sizeof(out_buf), "{\"%.*s\":null}", + MAX_CMD_LEN, cmd ? cmd : "none"); + if (write(s, out_buf, used) < 0) + perror("Error writing to socket"); + return; + } + output_json(cmd, &data, s); +} + +static int +unknown_command(const char *cmd __rte_unused, const char *params __rte_unused, + struct rte_tel_data *d) +{ + return d->type = RTE_TEL_NULL; +} + +static void * +client_handler(void *sock_id) +{ + int s = (int)(uintptr_t)sock_id; + char buffer[1024]; + char info_str[1024]; + snprintf(info_str, sizeof(info_str), + "{\"version\":\"%s\",\"pid\":%d,\"max_output_len\":%d}", + rte_version(), getpid(), MAX_OUTPUT_LEN); + if (write(s, info_str, strlen(info_str)) < 0) { + close(s); + return NULL; + } + + /* receive data is not null terminated */ + int bytes = read(s, buffer, sizeof(buffer) - 1); + while (bytes > 0) { + buffer[bytes] = 0; + const char *cmd = strtok(buffer, ","); + const char *param = strtok(NULL, ","); + telemetry_cb fn = unknown_command; + int i; + + if (cmd && strlen(cmd) < MAX_CMD_LEN) { + rte_spinlock_lock(&callback_sl); + for (i = 0; i < num_callbacks; i++) + if (strcmp(cmd, callbacks[i].cmd) == 0) { + fn = callbacks[i].fn; + break; + } + rte_spinlock_unlock(&callback_sl); + } + perform_command(fn, cmd, param, s); + + bytes = read(s, buffer, sizeof(buffer) - 1); + } + close(s); + return NULL; +} + +static void * +socket_listener(void *socket) +{ + while (1) { + pthread_t th; + struct socket *s = (struct socket *)socket; + int s_accepted = accept(s->sock, NULL, NULL); + if (s_accepted < 0) { + snprintf(telemetry_log_error, + sizeof(telemetry_log_error), + "Error with accept, telemetry thread quitting"); + return NULL; + } + pthread_create(&th, NULL, s->fn, (void *)(uintptr_t)s_accepted); + pthread_detach(th); + } + return NULL; +} + +static inline char * +get_socket_path(const char *runtime_dir, const int version) +{ + static char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/dpdk_telemetry.v%d", + strlen(runtime_dir) ? runtime_dir : "/tmp", version); + return path; +} + +static void +unlink_sockets(void) +{ + if (v2_socket.path[0]) + unlink(v2_socket.path); + if (v1_socket.path[0]) + unlink(v1_socket.path); +} + +static int +create_socket(char *path) +{ + int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sock < 0) { + snprintf(telemetry_log_error, sizeof(telemetry_log_error), + "Error with socket creation, %s", + strerror(errno)); + return -1; + } + + struct sockaddr_un sun = {.sun_family = AF_UNIX}; + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + unlink(sun.sun_path); + if (bind(sock, (void *) &sun, sizeof(sun)) < 0) { + snprintf(telemetry_log_error, sizeof(telemetry_log_error), + "Error binding socket: %s", + strerror(errno)); + sun.sun_path[0] = 0; + goto error; + } + + if (listen(sock, 1) < 0) { + snprintf(telemetry_log_error, sizeof(telemetry_log_error), + "Error calling listen for socket: %s", + strerror(errno)); + goto error; + } + + return sock; + +error: + close(sock); + unlink_sockets(); + return -1; +} + +static int +telemetry_legacy_init(const char *runtime_dir, rte_cpuset_t *cpuset) +{ + pthread_t t_old; + + if (num_legacy_callbacks == 1) { + snprintf(telemetry_log_error, sizeof(telemetry_log_error), + "No legacy callbacks, legacy socket not created"); + return -1; + } + + v1_socket.fn = legacy_client_handler; + if ((size_t) snprintf(v1_socket.path, sizeof(v1_socket.path), + "%s/telemetry", runtime_dir) + >= sizeof(v1_socket.path)) { + snprintf(telemetry_log_error, sizeof(telemetry_log_error), + "Error with socket binding, path too long"); + return -1; + } + v1_socket.sock = create_socket(v1_socket.path); + if (v1_socket.sock < 0) + return -1; + pthread_create(&t_old, NULL, socket_listener, &v1_socket); + pthread_setaffinity_np(t_old, sizeof(*cpuset), cpuset); + + return 0; +} + +static int +telemetry_v2_init(const char *runtime_dir, rte_cpuset_t *cpuset) +{ + pthread_t t_new; + + rte_telemetry_register_cmd("/", list_commands, + "Returns list of available commands, Takes no parameters"); + rte_telemetry_register_cmd("/info", json_info, + "Returns DPDK Telemetry information. Takes no parameters"); + rte_telemetry_register_cmd("/help", command_help, + "Returns help text for a command. Parameters: string command"); + v2_socket.fn = client_handler; + if (strlcpy(v2_socket.path, get_socket_path(runtime_dir, 2), + sizeof(v2_socket.path)) >= sizeof(v2_socket.path)) { + snprintf(telemetry_log_error, sizeof(telemetry_log_error), + "Error with socket binding, path too long"); + return -1; + } + + v2_socket.sock = create_socket(v2_socket.path); + if (v2_socket.sock < 0) + return -1; + pthread_create(&t_new, NULL, socket_listener, &v2_socket); + pthread_setaffinity_np(t_new, sizeof(*cpuset), cpuset); + atexit(unlink_sockets); + + return 0; +} + +int32_t +rte_telemetry_init(const char *runtime_dir, rte_cpuset_t *cpuset, + const char **err_str) +{ + if (telemetry_v2_init(runtime_dir, cpuset) != 0) { + *err_str = telemetry_log_error; + return -1; + } + if (telemetry_legacy_init(runtime_dir, cpuset) != 0) { + *err_str = telemetry_log_error; + } + return 0; +} diff --git a/src/spdk/dpdk/lib/librte_telemetry/telemetry_data.c b/src/spdk/dpdk/lib/librte_telemetry/telemetry_data.c new file mode 100644 index 000000000..f424bbd48 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/telemetry_data.c @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ + +#undef RTE_USE_LIBBSD +#include <rte_string_fns.h> + +#include "telemetry_data.h" + +int +rte_tel_data_start_array(struct rte_tel_data *d, enum rte_tel_value_type type) +{ + enum tel_container_types array_types[] = { + RTE_TEL_ARRAY_STRING, /* RTE_TEL_STRING_VAL = 0 */ + RTE_TEL_ARRAY_INT, /* RTE_TEL_INT_VAL = 1 */ + RTE_TEL_ARRAY_U64, /* RTE_TEL_u64_VAL = 2 */ + }; + d->type = array_types[type]; + d->data_len = 0; + return 0; +} + +int +rte_tel_data_start_dict(struct rte_tel_data *d) +{ + d->type = RTE_TEL_DICT; + d->data_len = 0; + return 0; +} + +int +rte_tel_data_string(struct rte_tel_data *d, const char *str) +{ + d->type = RTE_TEL_STRING; + d->data_len = strlcpy(d->data.str, str, sizeof(d->data.str)); + if (d->data_len >= RTE_TEL_MAX_SINGLE_STRING_LEN) { + d->data_len = RTE_TEL_MAX_SINGLE_STRING_LEN - 1; + return E2BIG; /* not necessarily and error, just truncation */ + } + return 0; +} + +int +rte_tel_data_add_array_string(struct rte_tel_data *d, const char *str) +{ + if (d->type != RTE_TEL_ARRAY_STRING) + return -EINVAL; + if (d->data_len >= RTE_TEL_MAX_ARRAY_ENTRIES) + return -ENOSPC; + const size_t bytes = strlcpy(d->data.array[d->data_len++].sval, + str, RTE_TEL_MAX_STRING_LEN); + return bytes < RTE_TEL_MAX_STRING_LEN ? 0 : E2BIG; +} + +int +rte_tel_data_add_array_int(struct rte_tel_data *d, int x) +{ + if (d->type != RTE_TEL_ARRAY_INT) + return -EINVAL; + if (d->data_len >= RTE_TEL_MAX_ARRAY_ENTRIES) + return -ENOSPC; + d->data.array[d->data_len++].ival = x; + return 0; +} + +int +rte_tel_data_add_array_u64(struct rte_tel_data *d, uint64_t x) +{ + if (d->type != RTE_TEL_ARRAY_U64) + return -EINVAL; + if (d->data_len >= RTE_TEL_MAX_ARRAY_ENTRIES) + return -ENOSPC; + d->data.array[d->data_len++].u64val = x; + return 0; +} + +int +rte_tel_data_add_dict_string(struct rte_tel_data *d, const char *name, + const char *val) +{ + struct tel_dict_entry *e = &d->data.dict[d->data_len]; + size_t nbytes, vbytes; + + if (d->type != RTE_TEL_DICT) + return -EINVAL; + if (d->data_len >= RTE_TEL_MAX_DICT_ENTRIES) + return -ENOSPC; + + d->data_len++; + e->type = RTE_TEL_STRING_VAL; + vbytes = strlcpy(e->value.sval, val, RTE_TEL_MAX_STRING_LEN); + nbytes = strlcpy(e->name, name, RTE_TEL_MAX_STRING_LEN); + if (vbytes >= RTE_TEL_MAX_STRING_LEN || + nbytes >= RTE_TEL_MAX_STRING_LEN) + return E2BIG; + return 0; +} + +int +rte_tel_data_add_dict_int(struct rte_tel_data *d, const char *name, int val) +{ + struct tel_dict_entry *e = &d->data.dict[d->data_len]; + if (d->type != RTE_TEL_DICT) + return -EINVAL; + if (d->data_len >= RTE_TEL_MAX_DICT_ENTRIES) + return -ENOSPC; + + d->data_len++; + e->type = RTE_TEL_INT_VAL; + e->value.ival = val; + const size_t bytes = strlcpy(e->name, name, RTE_TEL_MAX_STRING_LEN); + return bytes < RTE_TEL_MAX_STRING_LEN ? 0 : E2BIG; +} + +int +rte_tel_data_add_dict_u64(struct rte_tel_data *d, + const char *name, uint64_t val) +{ + struct tel_dict_entry *e = &d->data.dict[d->data_len]; + if (d->type != RTE_TEL_DICT) + return -EINVAL; + if (d->data_len >= RTE_TEL_MAX_DICT_ENTRIES) + return -ENOSPC; + + d->data_len++; + e->type = RTE_TEL_U64_VAL; + e->value.u64val = val; + const size_t bytes = strlcpy(e->name, name, RTE_TEL_MAX_STRING_LEN); + return bytes < RTE_TEL_MAX_STRING_LEN ? 0 : E2BIG; +} diff --git a/src/spdk/dpdk/lib/librte_telemetry/telemetry_data.h b/src/spdk/dpdk/lib/librte_telemetry/telemetry_data.h new file mode 100644 index 000000000..ff3a371a3 --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/telemetry_data.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ + +#ifndef _TELEMETRY_DATA_H_ +#define _TELEMETRY_DATA_H_ + +#include <inttypes.h> +#include "rte_telemetry.h" + +enum tel_container_types { + RTE_TEL_NULL, /** null, used as error value */ + RTE_TEL_STRING, /** basic string type, no included data */ + RTE_TEL_DICT, /** name-value pairs, of individual value type */ + RTE_TEL_ARRAY_STRING, /** array of string values only */ + RTE_TEL_ARRAY_INT, /** array of signed, 32-bit int values */ + RTE_TEL_ARRAY_U64, /** array of unsigned 64-bit int values */ +}; + +/* each type here must have an equivalent enum in the value types enum in + * telemetry.h and an array type defined above, and have appropriate + * type assignment in the RTE_TEL_data_start_array() function + */ +union tel_value { + char sval[RTE_TEL_MAX_STRING_LEN]; + int ival; + uint64_t u64val; +}; + +struct tel_dict_entry { + char name[RTE_TEL_MAX_STRING_LEN]; + enum rte_tel_value_type type; + union tel_value value; +}; + +struct rte_tel_data { + enum tel_container_types type; + unsigned int data_len; /* for array or object, how many items */ + union { + char str[RTE_TEL_MAX_SINGLE_STRING_LEN]; + struct tel_dict_entry dict[RTE_TEL_MAX_DICT_ENTRIES]; + union tel_value array[RTE_TEL_MAX_ARRAY_ENTRIES]; + } data; /* data container */ +}; + +#endif diff --git a/src/spdk/dpdk/lib/librte_telemetry/telemetry_json.h b/src/spdk/dpdk/lib/librte_telemetry/telemetry_json.h new file mode 100644 index 000000000..a2ce4899e --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/telemetry_json.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ + +#ifndef _RTE_TELEMETRY_JSON_H_ +#define _RTE_TELEMETRY_JSON_H_ + +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> +#include <rte_common.h> + +/** + * @file + * Internal Telemetry Utility functions + * + * This file contains small inline functions to make it easier for applications + * to build up valid JSON responses to telemetry requests. + * + ***/ + +/** + * @internal + * Copies a value into a buffer if the buffer has enough available space. + * Nothing written to buffer if an overflow ocurs. + * This function is not for use for values larger than 1k. + */ +__rte_format_printf(3, 4) +static inline int +__json_snprintf(char *buf, const int len, const char *format, ...) +{ + char tmp[1024]; + va_list ap; + int ret; + + va_start(ap, format); + ret = vsnprintf(tmp, sizeof(tmp), format, ap); + va_end(ap); + if (ret > 0 && ret < (int)sizeof(tmp) && ret < len) { + strcpy(buf, tmp); + return ret; + } + return 0; /* nothing written or modified */ +} + +/* Copies an empty array into the provided buffer. */ +static inline int +rte_tel_json_empty_array(char *buf, const int len, const int used) +{ + return used + __json_snprintf(buf + used, len - used, "[]"); +} + +/* Copies an empty object into the provided buffer. */ +static inline int +rte_tel_json_empty_obj(char *buf, const int len, const int used) +{ + return used + __json_snprintf(buf + used, len - used, "{}"); +} + +/* Copies a string into the provided buffer, in JSON format. */ +static inline int +rte_tel_json_str(char *buf, const int len, const int used, const char *str) +{ + return used + __json_snprintf(buf + used, len - used, "\"%s\"", str); +} + +/* Appends a string into the JSON array in the provided buffer. */ +static inline int +rte_tel_json_add_array_string(char *buf, const int len, const int used, + const char *str) +{ + int ret, end = used - 1; /* strip off final delimiter */ + if (used <= 2) /* assume empty, since minimum is '[]' */ + return __json_snprintf(buf, len, "[\"%s\"]", str); + + ret = __json_snprintf(buf + end, len - end, ",\"%s\"]", str); + return ret == 0 ? used : end + ret; +} + +/* Appends an integer into the JSON array in the provided buffer. */ +static inline int +rte_tel_json_add_array_int(char *buf, const int len, const int used, int val) +{ + int ret, end = used - 1; /* strip off final delimiter */ + if (used <= 2) /* assume empty, since minimum is '[]' */ + return __json_snprintf(buf, len, "[%d]", val); + + ret = __json_snprintf(buf + end, len - end, ",%d]", val); + return ret == 0 ? used : end + ret; +} + +/* Appends a uint64_t into the JSON array in the provided buffer. */ +static inline int +rte_tel_json_add_array_u64(char *buf, const int len, const int used, + uint64_t val) +{ + int ret, end = used - 1; /* strip off final delimiter */ + if (used <= 2) /* assume empty, since minimum is '[]' */ + return __json_snprintf(buf, len, "[%"PRIu64"]", val); + + ret = __json_snprintf(buf + end, len - end, ",%"PRIu64"]", val); + return ret == 0 ? used : end + ret; +} + +/** + * Add a new element with uint64_t value to the JSON object stored in the + * provided buffer. + */ +static inline int +rte_tel_json_add_obj_u64(char *buf, const int len, const int used, + const char *name, uint64_t val) +{ + int ret, end = used - 1; + if (used <= 2) /* assume empty, since minimum is '{}' */ + return __json_snprintf(buf, len, "{\"%s\":%"PRIu64"}", name, + val); + + ret = __json_snprintf(buf + end, len - end, ",\"%s\":%"PRIu64"}", + name, val); + return ret == 0 ? used : end + ret; +} + +/** + * Add a new element with int value to the JSON object stored in the + * provided buffer. + */ +static inline int +rte_tel_json_add_obj_int(char *buf, const int len, const int used, + const char *name, int val) +{ + int ret, end = used - 1; + if (used <= 2) /* assume empty, since minimum is '{}' */ + return __json_snprintf(buf, len, "{\"%s\":%d}", name, + val); + + ret = __json_snprintf(buf + end, len - end, ",\"%s\":%d}", + name, val); + return ret == 0 ? used : end + ret; +} + +/** + * Add a new element with string value to the JSON object stored in the + * provided buffer. + */ +static inline int +rte_tel_json_add_obj_str(char *buf, const int len, const int used, + const char *name, const char *val) +{ + int ret, end = used - 1; + if (used <= 2) /* assume empty, since minimum is '{}' */ + return __json_snprintf(buf, len, "{\"%s\":\"%s\"}", name, val); + + ret = __json_snprintf(buf + end, len - end, ",\"%s\":\"%s\"}", + name, val); + return ret == 0 ? used : end + ret; +} + +#endif /*_RTE_TELEMETRY_JSON_H_*/ diff --git a/src/spdk/dpdk/lib/librte_telemetry/telemetry_legacy.c b/src/spdk/dpdk/lib/librte_telemetry/telemetry_legacy.c new file mode 100644 index 000000000..a341fe4eb --- /dev/null +++ b/src/spdk/dpdk/lib/librte_telemetry/telemetry_legacy.c @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2020 Intel Corporation + */ + +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <pthread.h> + +/* we won't link against libbsd, so just always use DPDKs-specific strlcpy */ +#undef RTE_USE_LIBBSD +#include <rte_string_fns.h> +#include <rte_common.h> +#include <rte_spinlock.h> + +#include "rte_telemetry_legacy.h" + +#define MAX_LEN 128 +#define BUF_SIZE 1024 +#define CLIENTS_UNREG_ACTION "\"action\":2" +#define CLIENTS_CMD "\"command\":\"clients\"" +#define CLIENTS_DATA "\"data\":{\"client_path\":\"" +#define STATS_ACTION "\"action\":0" +#define DATA_REQ_LABEL "\"data\":" +#define TELEMETRY_LEGACY_MAX_CALLBACKS 4 + + +static int +register_client(const char *cmd __rte_unused, + const char *params __rte_unused, + char *buffer, int buf_len); + +struct json_command { + char action[MAX_LEN]; + char cmd[MAX_LEN]; + char data[MAX_LEN]; + telemetry_legacy_cb fn; + +}; + +struct json_command callbacks[TELEMETRY_LEGACY_MAX_CALLBACKS] = { + { + .action = "\"action\":1", + .cmd = CLIENTS_CMD, + .data = CLIENTS_DATA, + .fn = register_client + } +}; +int num_legacy_callbacks = 1; +static rte_spinlock_t callback_sl = RTE_SPINLOCK_INITIALIZER; + +int +rte_telemetry_legacy_register(const char *cmd, + enum rte_telemetry_legacy_data_req data_req, + telemetry_legacy_cb fn) +{ + if (fn == NULL) + return -EINVAL; + if (num_legacy_callbacks >= (int) RTE_DIM(callbacks)) + return -ENOENT; + + rte_spinlock_lock(&callback_sl); + strlcpy(callbacks[num_legacy_callbacks].action, STATS_ACTION, MAX_LEN); + snprintf(callbacks[num_legacy_callbacks].cmd, MAX_LEN, + "\"command\":\"%s\"", cmd); + snprintf(callbacks[num_legacy_callbacks].data, MAX_LEN, + data_req ? "%s{\"" : "%snull", + DATA_REQ_LABEL); + callbacks[num_legacy_callbacks].fn = fn; + num_legacy_callbacks++; + rte_spinlock_unlock(&callback_sl); + + return 0; +} + +static int +register_client(const char *cmd __rte_unused, const char *params, + char *buffer __rte_unused, int buf_len __rte_unused) +{ + pthread_t th; + char data[BUF_SIZE]; + int fd; + struct sockaddr_un addrs; + + if (!strchr(params, ':')) { + fprintf(stderr, "Invalid data\n"); + return -1; + } + strlcpy(data, strchr(params, ':'), sizeof(data)); + memcpy(data, &data[strlen(":\"")], strlen(data)); + if (!strchr(data, '\"')) { + fprintf(stderr, "Invalid client data\n"); + return -1; + } + *strchr(data, '\"') = 0; + + fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (fd < 0) { + perror("Failed to open socket"); + return -1; + } + addrs.sun_family = AF_UNIX; + strlcpy(addrs.sun_path, data, sizeof(addrs.sun_path)); + + if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) { + perror("\nClient connection error\n"); + close(fd); + return -1; + } + pthread_create(&th, NULL, &legacy_client_handler, + (void *)(uintptr_t)fd); + return 0; +} + +static int +send_error_response(int s, int err) +{ + const char *desc; + char out_buf[100000]; + + switch (err) { + case -ENOMEM: + desc = "Memory Allocation Error"; + break; + case -EINVAL: + desc = "Invalid Argument 404"; + break; + case -EPERM: + desc = "Unknown"; + break; + default: + /* Default case keeps behaviour of Telemetry library */ + printf("\nInvalid error type: %d\n", err); + return -EINVAL; + } + int used = snprintf(out_buf, sizeof(out_buf), "{\"status_code\": " + "\"Status Error: %s\", \"data\": null}", desc); + if (write(s, out_buf, used) < 0) { + perror("Error writing to socket"); + return -1; + } + return 0; +} + +static void +perform_command(telemetry_legacy_cb fn, const char *param, int s) +{ + char out_buf[100000]; + int ret, used = 0; + + ret = fn("", param, out_buf, sizeof(out_buf)); + if (ret < 0) { + ret = send_error_response(s, ret); + if (ret < 0) + printf("\nCould not send error response\n"); + return; + } + used += ret; + if (write(s, out_buf, used) < 0) + perror("Error writing to socket"); +} + +static int +parse_client_request(char *buffer, int buf_len, int s) +{ + int i; + char *data = buffer + buf_len; + telemetry_legacy_cb fn = NULL; + const char *valid_sep = ",}"; + if (buffer[0] != '{' || buffer[buf_len - 1] != '}') + return -EPERM; + + if (strstr(buffer, CLIENTS_UNREG_ACTION) && strstr(buffer, CLIENTS_CMD) + && strstr(buffer, CLIENTS_DATA)) + return 0; + + for (i = 0; i < num_legacy_callbacks; i++) { + char *action_ptr = strstr(buffer, callbacks[i].action); + char *cmd_ptr = strstr(buffer, callbacks[i].cmd); + char *data_ptr = strstr(buffer, callbacks[i].data); + if (!action_ptr || !cmd_ptr || !data_ptr) + continue; + + char action_sep = action_ptr[strlen(callbacks[i].action)]; + char cmd_sep = cmd_ptr[strlen(callbacks[i].cmd)]; + if (!(strchr(valid_sep, action_sep) && strchr(valid_sep, + cmd_sep))) + return -EPERM; + char data_sep; + + if (!strchr(data_ptr, '{')) + data_sep = data_ptr[strlen(callbacks[i].data)]; + else { + if (!strchr(data_ptr, '}')) + return -EINVAL; + char *data_end = strchr(data_ptr, '}'); + data = data_ptr + strlen(DATA_REQ_LABEL); + data_sep = data_end[1]; + data_end[1] = 0; + } + if (!strchr(valid_sep, data_sep)) + return -EPERM; + fn = callbacks[i].fn; + break; + } + + if (!fn) + return -EINVAL; + perform_command(fn, data, s); + return 0; +} + +void * +legacy_client_handler(void *sock_id) +{ + int s = (int)(uintptr_t)sock_id; + int ret; + char buffer_recv[BUF_SIZE]; + /* receive data is not null terminated */ + int bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1); + + while (bytes > 0) { + buffer_recv[bytes] = 0; + int i, j; + char buffer[BUF_SIZE]; + for (i = 0, j = 0; buffer_recv[i] != '\0'; i++) { + buffer[j] = buffer_recv[i]; + j += !isspace(buffer_recv[i]); + } + buffer[j] = 0; + ret = parse_client_request(buffer, j, s); + if (ret < 0) { + ret = send_error_response(s, ret); + if (ret < 0) + printf("\nCould not send error response\n"); + } + bytes = read(s, buffer_recv, sizeof(buffer_recv) - 1); + } + close(s); + return NULL; +} |