From patchwork Sat Oct 27 09:17:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47510 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 513C24CA5; Sat, 27 Oct 2018 11:19:39 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 845FA293B for ; Sat, 27 Oct 2018 11:19:26 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:25 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903866" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:23 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Kevin Laatz Date: Sat, 27 Oct 2018 10:17:39 +0100 Message-Id: <20181027091750.17254-2-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 01/12] eal: add option register infrastructure X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Kevin Laatz This commit adds infrastructure to EAL that allows an application to register it's init function with EAL. This allows libraries to be initialized at the end of EAL init. This infrastructure allows libraries that depend on EAL to be initialized as part of EAL init, removing circular dependency issues. Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren Acked-by: Gaetan Rivet --- lib/librte_eal/bsdapp/eal/Makefile | 1 + lib/librte_eal/bsdapp/eal/eal.c | 14 ++++- lib/librte_eal/common/Makefile | 1 + lib/librte_eal/common/eal_private.h | 21 ++++++++ lib/librte_eal/common/include/rte_option.h | 63 ++++++++++++++++++++++ lib/librte_eal/common/meson.build | 2 + lib/librte_eal/common/rte_option.c | 54 +++++++++++++++++++ lib/librte_eal/linuxapp/eal/Makefile | 1 + lib/librte_eal/linuxapp/eal/eal.c | 14 ++++- lib/librte_eal/rte_eal_version.map | 1 + 10 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 lib/librte_eal/common/include/rte_option.h create mode 100644 lib/librte_eal/common/rte_option.c diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile index d19f53c1e..bfeddaadc 100644 --- a/lib/librte_eal/bsdapp/eal/Makefile +++ b/lib/librte_eal/bsdapp/eal/Makefile @@ -67,6 +67,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_elem.c SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_heap.c SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += malloc_mp.c SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_keepalive.c +SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_option.c SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_service.c SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += rte_reciprocal.c diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index f94c5c5f4..11cbda964 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -414,12 +415,20 @@ eal_parse_args(int argc, char **argv) argvopt = argv; optind = 1; optreset = 1; + opterr = 0; while ((opt = getopt_long(argc, argvopt, eal_short_options, eal_long_options, &option_index)) != EOF) { - /* getopt is not happy, stop right now */ + /* + * getopt didn't recognise the option, lets parse the + * registered options to see if the flag is valid + */ if (opt == '?') { + ret = rte_option_parse(argv[optind-1]); + if (ret == 0) + continue; + eal_usage(prgname); ret = -1; goto out; @@ -791,6 +800,9 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); + /* Call each registered callback, if enabled */ + rte_option_init(); + return fctret; } diff --git a/lib/librte_eal/common/Makefile b/lib/librte_eal/common/Makefile index cca68826f..87d8c455d 100644 --- a/lib/librte_eal/common/Makefile +++ b/lib/librte_eal/common/Makefile @@ -12,6 +12,7 @@ INC += rte_tailq.h rte_interrupts.h rte_alarm.h INC += rte_string_fns.h rte_version.h INC += rte_eal_memconfig.h rte_malloc_heap.h INC += rte_hexdump.h rte_devargs.h rte_bus.h rte_dev.h rte_class.h +INC += rte_option.h INC += rte_pci_dev_feature_defs.h rte_pci_dev_features.h INC += rte_malloc.h rte_keepalive.h rte_time.h INC += rte_service.h rte_service_component.h diff --git a/lib/librte_eal/common/eal_private.h b/lib/librte_eal/common/eal_private.h index b189d675d..442c6dc48 100644 --- a/lib/librte_eal/common/eal_private.h +++ b/lib/librte_eal/common/eal_private.h @@ -349,4 +349,25 @@ dev_sigbus_handler_register(void); int dev_sigbus_handler_unregister(void); +/** + * Check if the option is registered. + * + * @param option + * The option to be parsed. + * + * @return + * 0 on success + * @return + * -1 on fail + */ +int +rte_option_parse(const char *opt); + +/** + * Iterate through the registered options and execute the associated + * callback if enabled. + */ +void +rte_option_init(void); + #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/common/include/rte_option.h b/lib/librte_eal/common/include/rte_option.h new file mode 100644 index 000000000..8957b970c --- /dev/null +++ b/lib/librte_eal/common/include/rte_option.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#ifndef __INCLUDE_RTE_OPTION_H__ +#define __INCLUDE_RTE_OPTION_H__ + +/** + * @file + * + * This API offers the ability to register options to the EAL command line and + * map those options to functions that will be executed at the end of EAL + * initialization. These options will be available as part of the EAL command + * line of applications and are dynamically managed. + * + * This is used primarily by DPDK libraries offering command line options. + * Currently, this API is limited to registering options without argument. + * + * The register API can be used to resolve circular dependency issues + * between EAL and the library. The library uses EAL, but is also initialized + * by EAL. Hence, EAL depends on the init function of the library. The API + * introduced in rte_option allows us to register the library init with EAL + * (passing a function pointer) and avoid the circular dependency. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*rte_option_cb)(void); + +/* + * Structure describing the EAL command line option being registered. + */ +struct rte_option { + TAILQ_ENTRY(rte_option) next; /**< Next entry in the list. */ + char *opt_str; /**< The option name. */ + rte_option_cb cb; /**< Function called when option is used. */ + int enabled; /**< Set when the option is used. */ +}; + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Register an option to the EAL command line. + * When recognized, the associated function will be executed at the end of EAL + * initialization. + * + * The associated structure must be available the whole time this option is + * registered (i.e. not stack memory). + * + * @param opt + * Structure describing the option to parse. + */ +void __rte_experimental +rte_option_register(struct rte_option *opt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/librte_eal/common/meson.build b/lib/librte_eal/common/meson.build index 04c414356..2a10d57d8 100644 --- a/lib/librte_eal/common/meson.build +++ b/lib/librte_eal/common/meson.build @@ -34,6 +34,7 @@ common_sources = files( 'malloc_mp.c', 'rte_keepalive.c', 'rte_malloc.c', + 'rte_option.c', 'rte_reciprocal.c', 'rte_service.c' ) @@ -71,6 +72,7 @@ common_headers = files( 'include/rte_malloc_heap.h', 'include/rte_memory.h', 'include/rte_memzone.h', + 'include/rte_option.h', 'include/rte_pci_dev_feature_defs.h', 'include/rte_pci_dev_features.h', 'include/rte_per_lcore.h', diff --git a/lib/librte_eal/common/rte_option.c b/lib/librte_eal/common/rte_option.c new file mode 100644 index 000000000..02d59a869 --- /dev/null +++ b/lib/librte_eal/common/rte_option.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation. + */ + +#include +#include + +#include +#include + +#include "eal_private.h" + +TAILQ_HEAD(rte_option_list, rte_option); + +struct rte_option_list rte_option_list = + TAILQ_HEAD_INITIALIZER(rte_option_list); + +static struct rte_option *option; + +int +rte_option_parse(const char *opt) +{ + /* Check if the option is registered */ + TAILQ_FOREACH(option, &rte_option_list, next) { + if (strcmp(opt, option->opt_str) == 0) { + option->enabled = 1; + return 0; + } + } + + return -1; +} + +void __rte_experimental +rte_option_register(struct rte_option *opt) +{ + TAILQ_FOREACH(option, &rte_option_list, next) { + if (strcmp(opt->opt_str, option->opt_str) == 0) + RTE_LOG(INFO, EAL, "Option %s has already been registered.", + opt->opt_str); + return; + } + + TAILQ_INSERT_HEAD(&rte_option_list, opt, next); +} + +void +rte_option_init(void) +{ + TAILQ_FOREACH(option, &rte_option_list, next) { + if (option->enabled) + option->cb(); + } +} diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile index 728088594..51deb5797 100644 --- a/lib/librte_eal/linuxapp/eal/Makefile +++ b/lib/librte_eal/linuxapp/eal/Makefile @@ -75,6 +75,7 @@ SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_elem.c SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_heap.c SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += malloc_mp.c SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_keepalive.c +SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_option.c SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_service.c SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += rte_reciprocal.c diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index 76536bae2..930070e43 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "eal_private.h" #include "eal_thread.h" @@ -600,12 +601,20 @@ eal_parse_args(int argc, char **argv) argvopt = argv; optind = 1; + opterr = 0; while ((opt = getopt_long(argc, argvopt, eal_short_options, eal_long_options, &option_index)) != EOF) { - /* getopt is not happy, stop right now */ + /* + * getopt didn't recognise the option, lets parse the + * registered options to see if the flag is valid + */ if (opt == '?') { + ret = rte_option_parse(argv[optind-1]); + if (ret == 0) + continue; + eal_usage(prgname); ret = -1; goto out; @@ -1080,6 +1089,9 @@ rte_eal_init(int argc, char **argv) rte_eal_mcfg_complete(); + /* Call each registered callback, if enabled */ + rte_option_init(); + return fctret; } diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map index f4a2b38fd..5b6d91ffd 100644 --- a/lib/librte_eal/rte_eal_version.map +++ b/lib/librte_eal/rte_eal_version.map @@ -350,6 +350,7 @@ EXPERIMENTAL { rte_mp_request_sync; rte_mp_request_async; rte_mp_sendmsg; + rte_option_register; rte_service_lcore_attr_get; rte_service_lcore_attr_reset_all; rte_service_may_be_active; From patchwork Sat Oct 27 09:17:40 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47511 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id EA8EC4CE4; Sat, 27 Oct 2018 11:19:40 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id B69A44C8C for ; Sat, 27 Oct 2018 11:19:28 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:27 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903870" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:26 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Kevin Laatz Date: Sat, 27 Oct 2018 10:17:40 +0100 Message-Id: <20181027091750.17254-3-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 02/12] eal: make get runtime dir function public X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Kevin Laatz This patch makes the eal_get_runtime_dir() API public so it can be used from outside EAL. Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren --- lib/librte_eal/bsdapp/eal/eal.c | 2 +- lib/librte_eal/common/eal_filesystem.h | 15 ++++++++------- lib/librte_eal/common/include/rte_eal.h | 9 +++++++++ lib/librte_eal/linuxapp/eal/eal.c | 2 +- lib/librte_eal/rte_eal_version.map | 1 + 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c index 11cbda964..21997ced8 100644 --- a/lib/librte_eal/bsdapp/eal/eal.c +++ b/lib/librte_eal/bsdapp/eal/eal.c @@ -142,7 +142,7 @@ eal_create_runtime_dir(void) } const char * -eal_get_runtime_dir(void) +rte_eal_get_runtime_dir(void) { return runtime_dir; } diff --git a/lib/librte_eal/common/eal_filesystem.h b/lib/librte_eal/common/eal_filesystem.h index de05febf4..b3e8ae5ea 100644 --- a/lib/librte_eal/common/eal_filesystem.h +++ b/lib/librte_eal/common/eal_filesystem.h @@ -27,7 +27,7 @@ eal_create_runtime_dir(void); /* returns runtime dir */ const char * -eal_get_runtime_dir(void); +rte_eal_get_runtime_dir(void); #define RUNTIME_CONFIG_FNAME "config" static inline const char * @@ -35,7 +35,7 @@ eal_runtime_config_path(void) { static char buffer[PATH_MAX]; /* static so auto-zeroed */ - snprintf(buffer, sizeof(buffer) - 1, "%s/%s", eal_get_runtime_dir(), + snprintf(buffer, sizeof(buffer) - 1, "%s/%s", rte_eal_get_runtime_dir(), RUNTIME_CONFIG_FNAME); return buffer; } @@ -47,7 +47,7 @@ eal_mp_socket_path(void) { static char buffer[PATH_MAX]; /* static so auto-zeroed */ - snprintf(buffer, sizeof(buffer) - 1, "%s/%s", eal_get_runtime_dir(), + snprintf(buffer, sizeof(buffer) - 1, "%s/%s", rte_eal_get_runtime_dir(), MP_SOCKET_FNAME); return buffer; } @@ -55,7 +55,8 @@ eal_mp_socket_path(void) #define FBARRAY_NAME_FMT "%s/fbarray_%s" static inline const char * eal_get_fbarray_path(char *buffer, size_t buflen, const char *name) { - snprintf(buffer, buflen, FBARRAY_NAME_FMT, eal_get_runtime_dir(), name); + snprintf(buffer, buflen, FBARRAY_NAME_FMT, rte_eal_get_runtime_dir(), + name); return buffer; } @@ -66,7 +67,7 @@ eal_hugepage_info_path(void) { static char buffer[PATH_MAX]; /* static so auto-zeroed */ - snprintf(buffer, sizeof(buffer) - 1, "%s/%s", eal_get_runtime_dir(), + snprintf(buffer, sizeof(buffer) - 1, "%s/%s", rte_eal_get_runtime_dir(), HUGEPAGE_INFO_FNAME); return buffer; } @@ -78,7 +79,7 @@ eal_hugepage_data_path(void) { static char buffer[PATH_MAX]; /* static so auto-zeroed */ - snprintf(buffer, sizeof(buffer) - 1, "%s/%s", eal_get_runtime_dir(), + snprintf(buffer, sizeof(buffer) - 1, "%s/%s", rte_eal_get_runtime_dir(), HUGEPAGE_DATA_FNAME); return buffer; } @@ -99,7 +100,7 @@ eal_get_hugefile_path(char *buffer, size_t buflen, const char *hugedir, int f_id static inline const char * eal_get_hugefile_lock_path(char *buffer, size_t buflen, int f_id) { - snprintf(buffer, buflen, HUGEFILE_LOCK_FMT, eal_get_runtime_dir(), + snprintf(buffer, buflen, HUGEFILE_LOCK_FMT, rte_eal_get_runtime_dir(), f_id); buffer[buflen - 1] = '\0'; return buffer; diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h index 6514a9fe6..a0cedd573 100644 --- a/lib/librte_eal/common/include/rte_eal.h +++ b/lib/librte_eal/common/include/rte_eal.h @@ -507,6 +507,15 @@ enum rte_iova_mode rte_eal_iova_mode(void); const char * rte_eal_mbuf_user_pool_ops(void); +/** + * Get the runtime directory of DPDK + * + * @return + * The runtime directory path of DPDK + */ +const char * +rte_eal_get_runtime_dir(void); + #ifdef __cplusplus } #endif diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index 930070e43..7b11375f4 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -150,7 +150,7 @@ eal_create_runtime_dir(void) } const char * -eal_get_runtime_dir(void) +rte_eal_get_runtime_dir(void) { return runtime_dir; } diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map index 5b6d91ffd..28bfa4950 100644 --- a/lib/librte_eal/rte_eal_version.map +++ b/lib/librte_eal/rte_eal_version.map @@ -259,6 +259,7 @@ DPDK_18.08 { DPDK_18.11 { global: + rte_eal_get_runtime_dir; rte_eal_hotplug_add; rte_eal_hotplug_remove; rte_strscpy; From patchwork Sat Oct 27 09:17:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47512 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 09AEC532C; Sat, 27 Oct 2018 11:19:44 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 00BF34C8C for ; Sat, 27 Oct 2018 11:19:30 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:30 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903873" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:28 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz , Radu Nicolau Date: Sat, 27 Oct 2018 10:17:41 +0100 Message-Id: <20181027091750.17254-4-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 03/12] telemetry: initial telemetry infrastructure X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch adds the infrastructure and initial code for the telemetry library. The telemetry init is registered with eal_init(). We can then check to see if --telemetry was passed as an eal option. If --telemetry was parsed, then we call telemetry init at the end of eal init. Control threads are used to get CPU cycles for telemetry, which are configured in this patch also. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Signed-off-by: Radu Nicolau Acked-by: Harry van Haaren --- v10: - change position of Telemetry in index Doxygen index (Thomas) - Add MAINTAINER file entry for lib/telemetry files (Thomas) - Removed -ljansson from mk/rte.app.mk (Thomas) - Removed pkg-config jansson check in mk/rte.vars.mk (Thomas) - Set CONFIG_TELEMETRY=N by default (Thomas) --- MAINTAINERS | 3 + config/common_base | 5 + doc/api/doxy-api-index.md | 1 + doc/api/doxy-api.conf.in | 1 + lib/Makefile | 2 + lib/librte_telemetry/Makefile | 27 ++++ lib/librte_telemetry/meson.build | 7 + lib/librte_telemetry/rte_telemetry.c | 123 ++++++++++++++++++ lib/librte_telemetry/rte_telemetry.h | 51 ++++++++ lib/librte_telemetry/rte_telemetry_internal.h | 32 +++++ .../rte_telemetry_version.map | 8 ++ lib/meson.build | 2 +- mk/rte.app.mk | 3 +- 13 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 lib/librte_telemetry/Makefile create mode 100644 lib/librte_telemetry/meson.build create mode 100644 lib/librte_telemetry/rte_telemetry.c create mode 100644 lib/librte_telemetry/rte_telemetry.h create mode 100644 lib/librte_telemetry/rte_telemetry_internal.h create mode 100644 lib/librte_telemetry/rte_telemetry_version.map diff --git a/MAINTAINERS b/MAINTAINERS index 17ee5b9d3..a50214d7a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1208,6 +1208,9 @@ F: test/bpf/ F: test/test/test_bpf.c F: doc/guides/prog_guide/bpf_lib.rst +Telemetry - EXPERIMENTAL +M: Kevin Laatz +F: lib/librte_telemetry/ Test Applications ----------------- diff --git a/config/common_base b/config/common_base index 38beaabb3..4ba8f651d 100644 --- a/config/common_base +++ b/config/common_base @@ -756,6 +756,11 @@ CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n CONFIG_RTE_LIBRTE_HASH=y CONFIG_RTE_LIBRTE_HASH_DEBUG=n +# +# Compile librte_telemetry +# +CONFIG_RTE_LIBRTE_TELEMETRY=n + # # Compile librte_efd # diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md index a3039d168..cd2b7e413 100644 --- a/doc/api/doxy-api-index.md +++ b/doc/api/doxy-api-index.md @@ -29,6 +29,7 @@ The public API headers are grouped by topics: [metrics] (@ref rte_metrics.h), [bitrate] (@ref rte_bitrate.h), [latency] (@ref rte_latencystats.h), + [telemetry] (@ref rte_telemetry.h), [devargs] (@ref rte_devargs.h), [PCI] (@ref rte_pci.h), [vfio] (@ref rte_vfio.h) diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in index 3b652ac9c..77ba327a8 100644 --- a/doc/api/doxy-api.conf.in +++ b/doc/api/doxy-api.conf.in @@ -56,6 +56,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \ @TOPDIR@/lib/librte_sched \ @TOPDIR@/lib/librte_security \ @TOPDIR@/lib/librte_table \ + @TOPDIR@/lib/librte_telemetry \ @TOPDIR@/lib/librte_timer \ @TOPDIR@/lib/librte_vhost INPUT += @API_EXAMPLES@ diff --git a/lib/Makefile b/lib/Makefile index 062cbdfd7..b7370ef97 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -106,6 +106,8 @@ DEPDIRS-librte_gso := librte_eal librte_mbuf librte_ethdev librte_net DEPDIRS-librte_gso += librte_mempool DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev +DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry +DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile new file mode 100644 index 000000000..a2d4ff166 --- /dev/null +++ b/lib/librte_telemetry/Makefile @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +include $(RTE_SDK)/mk/rte.vars.mk + +# library name +LIB = librte_telemetry.a + +CFLAGS += -O3 +CFLAGS += -I$(SRCDIR) +CFLAGS += -DALLOW_EXPERIMENTAL_API + +LDLIBS += -lrte_eal -lrte_ethdev +LDLIBS += -lrte_metrics +LDLIBS += -lpthread + +EXPORT_MAP := rte_telemetry_version.map + +LIBABIVER := 1 + +# library source files +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c + +# export include files +SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build new file mode 100644 index 000000000..7716076a9 --- /dev/null +++ b/lib/librte_telemetry/meson.build @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +sources = files('rte_telemetry.c') +headers = files('rte_telemetry.h', 'rte_telemetry_internal.h') +deps += ['metrics', 'ethdev'] +cflags += '-DALLOW_EXPERIMENTAL_API' diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c new file mode 100644 index 000000000..7f4ad0342 --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry.c @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include +#include + +#include +#include +#include +#include + +#include "rte_telemetry.h" +#include "rte_telemetry_internal.h" + +#define SLEEP_TIME 10 + +static telemetry_impl *static_telemetry; + +static int32_t +rte_telemetry_run(void *userdata) +{ + struct telemetry_impl *telemetry = userdata; + + if (telemetry == NULL) { + TELEMETRY_LOG_WARN("TELEMETRY could not be initialised"); + return -1; + } + + return 0; +} + +static void +*rte_telemetry_run_thread_func(void *userdata) +{ + int ret; + struct telemetry_impl *telemetry = userdata; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("%s passed a NULL instance", __func__); + pthread_exit(0); + } + + while (telemetry->thread_status) { + rte_telemetry_run(telemetry); + ret = usleep(SLEEP_TIME); + if (ret < 0) + TELEMETRY_LOG_ERR("Calling thread could not be put to sleep"); + } + pthread_exit(0); +} + +int32_t __rte_experimental +rte_telemetry_init() +{ + int ret; + pthread_attr_t attr; + const char *telemetry_ctrl_thread = "telemetry"; + + if (static_telemetry) { + TELEMETRY_LOG_WARN("TELEMETRY structure already initialised"); + return -EALREADY; + } + + static_telemetry = calloc(1, sizeof(struct telemetry_impl)); + if (static_telemetry == NULL) { + TELEMETRY_LOG_ERR("Memory could not be allocated"); + return -ENOMEM; + } + + static_telemetry->socket_id = rte_socket_id(); + rte_metrics_init(static_telemetry->socket_id); + + ret = pthread_attr_init(&attr); + if (ret != 0) { + TELEMETRY_LOG_ERR("Pthread attribute init failed"); + return -EPERM; + } + + ret = rte_ctrl_thread_create(&static_telemetry->thread_id, + telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func, + (void *)static_telemetry); + static_telemetry->thread_status = 1; + + if (ret < 0) { + ret = rte_telemetry_cleanup(); + if (ret < 0) + TELEMETRY_LOG_ERR("TELEMETRY cleanup failed"); + return -EPERM; + } + + return 0; +} + +int32_t __rte_experimental +rte_telemetry_cleanup(void) +{ + struct telemetry_impl *telemetry = static_telemetry; + telemetry->thread_status = 0; + pthread_join(telemetry->thread_id, NULL); + free(telemetry); + static_telemetry = NULL; + return 0; +} + +int telemetry_log_level; +RTE_INIT(rte_telemetry_register); + +static struct rte_option option = { + .opt_str = "--telemetry", + .cb = &rte_telemetry_init, + .enabled = 0 +}; + +static void +rte_telemetry_register(void) +{ + telemetry_log_level = rte_log_register("lib.telemetry"); + if (telemetry_log_level >= 0) + rte_log_set_level(telemetry_log_level, RTE_LOG_ERR); + + rte_option_register(&option); +} diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h new file mode 100644 index 000000000..97674ae2d --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include + +#ifndef _RTE_TELEMETRY_H_ +#define _RTE_TELEMETRY_H_ + +/** + * @file + * RTE Telemetry + * + * The telemetry library provides a method to retrieve statistics from + * DPDK by sending a JSON encoded message over a socket. DPDK will send + * a JSON encoded response containing telemetry data. + ***/ + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Initialize Telemetry + * + * @return + * 0 on successful initialisation. + * @return + * -ENOMEM on memory allocation error + * @return + * -EPERM on unknown error failure + * @return + * -EALREADY if Telemetry is already initialised. + */ +int32_t __rte_experimental +rte_telemetry_init(void); + +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Clean up and free memory. + * + * @return + * 0 on success + * @return + * -EPERM on failure + */ +int32_t __rte_experimental +rte_telemetry_cleanup(void); + +#endif diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h new file mode 100644 index 000000000..4e810a84c --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry_internal.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include + +#ifndef _RTE_TELEMETRY_INTERNAL_H_ +#define _RTE_TELEMETRY_INTERNAL_H_ + +/* Logging Macros */ +extern int telemetry_log_level; + +#define TELEMETRY_LOG(level, fmt, args...) \ + rte_log(RTE_LOG_ ##level, telemetry_log_level, "%s(): "fmt "\n", \ + __func__, ##args) + +#define TELEMETRY_LOG_ERR(fmt, args...) \ + TELEMETRY_LOG(ERR, fmt, ## args) + +#define TELEMETRY_LOG_WARN(fmt, args...) \ + TELEMETRY_LOG(WARNING, fmt, ## args) + +#define TELEMETRY_LOG_INFO(fmt, args...) \ + TELEMETRY_LOG(INFO, fmt, ## args) + +typedef struct telemetry_impl { + pthread_t thread_id; + int thread_status; + uint32_t socket_id; +} telemetry_impl; + +#endif diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map new file mode 100644 index 000000000..bbcd9a796 --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry_version.map @@ -0,0 +1,8 @@ +EXPERIMENTAL { + global: + + rte_telemetry_cleanup; + rte_telemetry_init; + + local: *; +}; diff --git a/lib/meson.build b/lib/meson.build index 2b903fa37..9d1f353d2 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -25,7 +25,7 @@ libraries = [ 'compat', # just a header, used for versioning # add pkt framework libs which use other libs from above 'port', 'table', 'pipeline', # flow_classify lib depends on pkt framework table lib - 'flow_classify', 'bpf'] + 'flow_classify', 'bpf', 'telemetry'] default_cflags = machine_args if cc.has_argument('-Wno-format-truncation') diff --git a/mk/rte.app.mk b/mk/rte.app.mk index c0036daf8..a14e83c71 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -51,7 +51,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL) += --whole-archive _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL) += -lrte_acl _LDLIBS-$(CONFIG_RTE_LIBRTE_ACL) += --no-whole-archive _LDLIBS-$(CONFIG_RTE_LIBRTE_JOBSTATS) += -lrte_jobstats -_LDLIBS-$(CONFIG_RTE_LIBRTE_METRICS) += -lrte_metrics _LDLIBS-$(CONFIG_RTE_LIBRTE_BITRATE) += -lrte_bitratestats _LDLIBS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS) += -lrte_latencystats _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER) += -lrte_power @@ -80,6 +79,8 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_SECURITY) += -lrte_security _LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev _LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev _LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev +_LDLIBS-$(CONFIG_RTE_LIBRTE_METRICS) += -lrte_metrics +_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_telemetry _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool _LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring From patchwork Sat Oct 27 09:17:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47513 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id DA2525681; Sat, 27 Oct 2018 11:19:46 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 8320A4C8C for ; Sat, 27 Oct 2018 11:19:33 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:33 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903877" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:30 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz Date: Sat, 27 Oct 2018 10:17:42 +0100 Message-Id: <20181027091750.17254-5-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 04/12] telemetry: add initial connection socket X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch adds the telemetry UNIX socket. It is used to allow connections from external clients. On the initial connection from a client, ethdev stats are registered in the metrics library, to allow for their retrieval at a later stage. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren --- lib/librte_telemetry/rte_telemetry.c | 225 ++++++++++++++++++ lib/librte_telemetry/rte_telemetry_internal.h | 4 + 2 files changed, 229 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c index 7f4ad0342..e9b3330ca 100644 --- a/lib/librte_telemetry/rte_telemetry.c +++ b/lib/librte_telemetry/rte_telemetry.c @@ -3,23 +3,163 @@ */ #include +#include #include +#include +#include #include #include #include #include +#include #include "rte_telemetry.h" #include "rte_telemetry_internal.h" +#define BUF_SIZE 1024 #define SLEEP_TIME 10 static telemetry_impl *static_telemetry; +static void +rte_telemetry_get_runtime_dir(char *socket_path, size_t size) +{ + snprintf(socket_path, size, "%s/telemetry", rte_eal_get_runtime_dir()); +} + +int32_t +rte_telemetry_is_port_active(int port_id) +{ + int ret; + + ret = rte_eth_find_next(port_id); + if (ret == port_id) + return 1; + + TELEMETRY_LOG_ERR("port_id: %d is invalid, not active", + port_id); + return 0; +} + +static int32_t +rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id) +{ + int ret, num_xstats, ret_val, i; + struct rte_eth_xstat *eth_xstats = NULL; + struct rte_eth_xstat_name *eth_xstats_names = NULL; + + if (!rte_eth_dev_is_valid_port(port_id)) { + TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id); + return -EINVAL; + } + + num_xstats = rte_eth_xstats_get(port_id, NULL, 0); + if (num_xstats < 0) { + TELEMETRY_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) { + TELEMETRY_LOG_ERR("Failed to malloc memory for xstats"); + return -ENOMEM; + } + + ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats); + const char *xstats_names[num_xstats]; + eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) * num_xstats); + if (ret < 0 || ret > num_xstats) { + TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d", + port_id, num_xstats, ret); + ret_val = -EPERM; + goto free_xstats; + } + + if (eth_xstats_names == NULL) { + TELEMETRY_LOG_ERR("Failed to malloc memory for xstats_names"); + ret_val = -ENOMEM; + goto free_xstats; + } + + ret = rte_eth_xstats_get_names(port_id, eth_xstats_names, num_xstats); + if (ret < 0 || ret > num_xstats) { + TELEMETRY_LOG_ERR("rte_eth_xstats_get_names(%u) len%i failed: %d", + port_id, num_xstats, ret); + ret_val = -EPERM; + goto free_xstats; + } + + for (i = 0; i < num_xstats; i++) + xstats_names[i] = eth_xstats_names[eth_xstats[i].id].name; + + ret_val = rte_metrics_reg_names(xstats_names, num_xstats); + if (ret_val < 0) { + TELEMETRY_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered"); + ret_val = -1; + goto free_xstats; + } + + goto free_xstats; + +free_xstats: + free(eth_xstats); + free(eth_xstats_names); + return ret_val; +} + +static int32_t +rte_telemetry_initial_accept(struct telemetry_impl *telemetry) +{ + uint16_t pid; + + RTE_ETH_FOREACH_DEV(pid) { + telemetry->reg_index = + rte_telemetry_reg_ethdev_to_metrics(pid); + break; + } + + if (telemetry->reg_index < 0) { + TELEMETRY_LOG_ERR("Failed to register ethdev metrics"); + return -1; + } + + telemetry->metrics_register_done = 1; + + return 0; +} + +static int32_t +rte_telemetry_accept_new_client(struct telemetry_impl *telemetry) +{ + int ret; + + if (telemetry->accept_fd <= 0) { + ret = listen(telemetry->server_fd, 1); + if (ret < 0) { + TELEMETRY_LOG_ERR("Listening error with server fd"); + return -1; + } + telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL); + + if (telemetry->accept_fd >= 0 && + telemetry->metrics_register_done == 0) { + ret = rte_telemetry_initial_accept(telemetry); + if (ret < 0) { + TELEMETRY_LOG_ERR("Failed to run initial configurations/tests"); + return -1; + } + } + } + + return 0; +} + static int32_t rte_telemetry_run(void *userdata) { + int ret; struct telemetry_impl *telemetry = userdata; if (telemetry == NULL) { @@ -27,6 +167,12 @@ rte_telemetry_run(void *userdata) return -1; } + ret = rte_telemetry_accept_new_client(telemetry); + if (ret < 0) { + TELEMETRY_LOG_ERR("Accept and read new client failed"); + return -1; + } + return 0; } @@ -50,6 +196,67 @@ static void pthread_exit(0); } +static int32_t +rte_telemetry_set_socket_nonblock(int fd) +{ + int flags; + + if (fd < 0) { + TELEMETRY_LOG_ERR("Invalid fd provided"); + return -1; + } + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) + flags = 0; + + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +static int32_t +rte_telemetry_create_socket(struct telemetry_impl *telemetry) +{ + int ret; + struct sockaddr_un addr; + char socket_path[BUF_SIZE]; + + if (telemetry == NULL) + return -1; + + telemetry->server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (telemetry->server_fd == -1) { + TELEMETRY_LOG_ERR("Failed to open socket"); + return -1; + } + + ret = rte_telemetry_set_socket_nonblock(telemetry->server_fd); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK"); + goto close_socket; + } + + addr.sun_family = AF_UNIX; + rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path)); + strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path)); + unlink(socket_path); + + if (bind(telemetry->server_fd, (struct sockaddr *)&addr, + sizeof(addr)) < 0) { + TELEMETRY_LOG_ERR("Socket binding error"); + goto close_socket; + } + + return 0; + +close_socket: + if (close(telemetry->server_fd) < 0) { + TELEMETRY_LOG_ERR("Close TELEMETRY socket failed"); + return -EPERM; + } + + return -1; +} + int32_t __rte_experimental rte_telemetry_init() { @@ -77,6 +284,14 @@ rte_telemetry_init() return -EPERM; } + ret = rte_telemetry_create_socket(static_telemetry); + if (ret < 0) { + ret = rte_telemetry_cleanup(); + if (ret < 0) + TELEMETRY_LOG_ERR("TELEMETRY cleanup failed"); + return -EPERM; + } + ret = rte_ctrl_thread_create(&static_telemetry->thread_id, telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func, (void *)static_telemetry); @@ -95,11 +310,21 @@ rte_telemetry_init() int32_t __rte_experimental rte_telemetry_cleanup(void) { + int ret; struct telemetry_impl *telemetry = static_telemetry; + + ret = close(telemetry->server_fd); + if (ret < 0) { + TELEMETRY_LOG_ERR("Close TELEMETRY socket failed"); + free(telemetry); + return -EPERM; + } + telemetry->thread_status = 0; pthread_join(telemetry->thread_id, NULL); free(telemetry); static_telemetry = NULL; + return 0; } diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h index 4e810a84c..569d56ab8 100644 --- a/lib/librte_telemetry/rte_telemetry_internal.h +++ b/lib/librte_telemetry/rte_telemetry_internal.h @@ -24,9 +24,13 @@ extern int telemetry_log_level; TELEMETRY_LOG(INFO, fmt, ## args) typedef struct telemetry_impl { + int accept_fd; + int server_fd; pthread_t thread_id; int thread_status; uint32_t socket_id; + int reg_index; + int metrics_register_done; } telemetry_impl; #endif From patchwork Sat Oct 27 09:17:43 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47514 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id D5FFD56A3; Sat, 27 Oct 2018 11:19:50 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 1037A4C8C for ; Sat, 27 Oct 2018 11:19:35 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:35 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903885" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:33 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz Date: Sat, 27 Oct 2018 10:17:43 +0100 Message-Id: <20181027091750.17254-6-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 05/12] telemetry: add client feature and sockets X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch introduces clients to the telemetry API. When a client makes a connection through the initial telemetry socket, they can send a message through the socket to be parsed. Register messages are expected through this socket, to enable clients to register and have a client socket setup for future communications. A TAILQ is used to store all clients information. Using this, the client sockets are polled for messages, which will later be parsed and dealt with accordingly. Functionality that make use of the client sockets were introduced in this patch also, such as writing to client sockets, and sending error responses. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren --- v10: - remove error prone pkg-config detection (Thomas) - Re-add -ljansson to mk/rte.app.mk, was in patch 1 (Thomas) --- lib/librte_telemetry/Makefile | 1 + lib/librte_telemetry/meson.build | 7 + lib/librte_telemetry/rte_telemetry.c | 370 +++++++++++++++++- lib/librte_telemetry/rte_telemetry_internal.h | 25 ++ mk/rte.app.mk | 2 +- 5 files changed, 401 insertions(+), 4 deletions(-) diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile index a2d4ff166..0d61361f4 100644 --- a/lib/librte_telemetry/Makefile +++ b/lib/librte_telemetry/Makefile @@ -13,6 +13,7 @@ CFLAGS += -DALLOW_EXPERIMENTAL_API LDLIBS += -lrte_eal -lrte_ethdev LDLIBS += -lrte_metrics LDLIBS += -lpthread +LDLIBS += -ljansson EXPORT_MAP := rte_telemetry_version.map diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build index 7716076a9..7b939805e 100644 --- a/lib/librte_telemetry/meson.build +++ b/lib/librte_telemetry/meson.build @@ -5,3 +5,10 @@ sources = files('rte_telemetry.c') headers = files('rte_telemetry.h', 'rte_telemetry_internal.h') deps += ['metrics', 'ethdev'] cflags += '-DALLOW_EXPERIMENTAL_API' + +jansson = cc.find_library('jansson', required: false) +if jansson.found() + ext_deps += jansson +else + build = false +endif diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c index e9b3330ca..3c8b922f5 100644 --- a/lib/librte_telemetry/rte_telemetry.c +++ b/lib/librte_telemetry/rte_telemetry.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,7 @@ #include "rte_telemetry_internal.h" #define BUF_SIZE 1024 +#define ACTION_POST 1 #define SLEEP_TIME 10 static telemetry_impl *static_telemetry; @@ -39,6 +41,91 @@ rte_telemetry_is_port_active(int port_id) TELEMETRY_LOG_ERR("port_id: %d is invalid, not active", port_id); + + return 0; +} + +int32_t +rte_telemetry_write_to_socket(struct telemetry_impl *telemetry, + const char *json_string) +{ + int ret; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Could not initialise TELEMETRY_API"); + return -1; + } + + if (telemetry->request_client == NULL) { + TELEMETRY_LOG_ERR("No client has been chosen to write to"); + return -1; + } + + if (json_string == NULL) { + TELEMETRY_LOG_ERR("Invalid JSON string!"); + return -1; + } + + ret = send(telemetry->request_client->fd, + json_string, strlen(json_string), 0); + if (ret < 0) { + TELEMETRY_LOG_ERR("Failed to write to socket for client: %s", + telemetry->request_client->file_path); + return -1; + } + + return 0; +} + +int32_t +rte_telemetry_send_error_response(struct telemetry_impl *telemetry, + int error_type) +{ + int ret; + const char *status_code, *json_buffer; + json_t *root; + + if (error_type == -EPERM) + status_code = "Status Error: Unknown"; + else if (error_type == -EINVAL) + status_code = "Status Error: Invalid Argument 404"; + else if (error_type == -ENOMEM) + status_code = "Status Error: Memory Allocation Error"; + else { + TELEMETRY_LOG_ERR("Invalid error type"); + return -EINVAL; + } + + root = json_object(); + + if (root == NULL) { + TELEMETRY_LOG_ERR("Could not create root JSON object"); + return -EPERM; + } + + ret = json_object_set_new(root, "status_code", json_string(status_code)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Status code field cannot be set"); + json_decref(root); + return -EPERM; + } + + ret = json_object_set_new(root, "data", json_null()); + if (ret < 0) { + TELEMETRY_LOG_ERR("Data field cannot be set"); + json_decref(root); + return -EPERM; + } + + json_buffer = json_dumps(root, JSON_INDENT(2)); + json_decref(root); + + ret = rte_telemetry_write_to_socket(telemetry, json_buffer); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not write to socket"); + return -EPERM; + } + return 0; } @@ -115,8 +202,7 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry) uint16_t pid; RTE_ETH_FOREACH_DEV(pid) { - telemetry->reg_index = - rte_telemetry_reg_ethdev_to_metrics(pid); + telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid); break; } @@ -130,6 +216,38 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry) return 0; } +static int32_t +rte_telemetry_read_client(struct telemetry_impl *telemetry) +{ + char buf[BUF_SIZE]; + int ret, buffer_read; + + buffer_read = read(telemetry->accept_fd, buf, BUF_SIZE-1); + + if (buffer_read == -1) { + TELEMETRY_LOG_ERR("Read error"); + return -1; + } else if (buffer_read == 0) { + goto close_socket; + } else { + buf[buffer_read] = '\0'; + ret = rte_telemetry_parse_client_message(telemetry, buf); + if (ret < 0) + TELEMETRY_LOG_WARN("Parse message failed"); + goto close_socket; + } + +close_socket: + if (close(telemetry->accept_fd) < 0) { + TELEMETRY_LOG_ERR("Close TELEMETRY socket failed"); + free(telemetry); + return -EPERM; + } + telemetry->accept_fd = 0; + + return 0; +} + static int32_t rte_telemetry_accept_new_client(struct telemetry_impl *telemetry) { @@ -141,8 +259,8 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry) TELEMETRY_LOG_ERR("Listening error with server fd"); return -1; } - telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL); + telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL); if (telemetry->accept_fd >= 0 && telemetry->metrics_register_done == 0) { ret = rte_telemetry_initial_accept(telemetry); @@ -151,6 +269,31 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry) return -1; } } + } else { + ret = rte_telemetry_read_client(telemetry); + if (ret < 0) { + TELEMETRY_LOG_ERR("Failed to read socket buffer"); + return -1; + } + } + + return 0; +} + +static int32_t +rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry) +{ + telemetry_client *client; + char client_buf[BUF_SIZE]; + int bytes; + + TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) { + bytes = read(client->fd, client_buf, BUF_SIZE-1); + + if (bytes > 0) { + client_buf[bytes] = '\0'; + telemetry->request_client = client; + } } return 0; @@ -173,6 +316,12 @@ rte_telemetry_run(void *userdata) return -1; } + ret = rte_telemetry_read_client_sockets(telemetry); + if (ret < 0) { + TELEMETRY_LOG_ERR("Client socket read failed"); + return -1; + } + return 0; } @@ -291,6 +440,7 @@ rte_telemetry_init() TELEMETRY_LOG_ERR("TELEMETRY cleanup failed"); return -EPERM; } + TAILQ_INIT(&static_telemetry->client_list_head); ret = rte_ctrl_thread_create(&static_telemetry->thread_id, telemetry_ctrl_thread, &attr, rte_telemetry_run_thread_func, @@ -307,11 +457,39 @@ rte_telemetry_init() return 0; } +static int32_t +rte_telemetry_client_cleanup(struct telemetry_client *client) +{ + int ret; + + ret = close(client->fd); + free(client->file_path); + free(client); + + if (ret < 0) { + TELEMETRY_LOG_ERR("Close client socket failed"); + return -EPERM; + } + + return 0; +} + int32_t __rte_experimental rte_telemetry_cleanup(void) { int ret; struct telemetry_impl *telemetry = static_telemetry; + telemetry_client *client, *temp_client; + + TAILQ_FOREACH_SAFE(client, &telemetry->client_list_head, client_list, + temp_client) { + TAILQ_REMOVE(&telemetry->client_list_head, client, client_list); + ret = rte_telemetry_client_cleanup(client); + if (ret < 0) { + TELEMETRY_LOG_ERR("Client cleanup failed"); + return -EPERM; + } + } ret = close(telemetry->server_fd); if (ret < 0) { @@ -328,6 +506,192 @@ rte_telemetry_cleanup(void) return 0; } +int32_t +rte_telemetry_unregister_client(struct telemetry_impl *telemetry, + const char *client_path) +{ + int ret; + telemetry_client *client, *temp_client; + + if (telemetry == NULL) { + TELEMETRY_LOG_WARN("TELEMETRY is not initialised"); + return -ENODEV; + } + + if (client_path == NULL) { + TELEMETRY_LOG_ERR("Invalid client path"); + goto einval_fail; + } + + if (TAILQ_EMPTY(&telemetry->client_list_head)) { + TELEMETRY_LOG_ERR("There are no clients currently registered"); + return -EPERM; + } + + TAILQ_FOREACH_SAFE(client, &telemetry->client_list_head, client_list, + temp_client) { + if (strcmp(client_path, client->file_path) == 0) { + TAILQ_REMOVE(&telemetry->client_list_head, client, + client_list); + ret = rte_telemetry_client_cleanup(client); + + if (ret < 0) { + TELEMETRY_LOG_ERR("Client cleanup failed"); + return -EPERM; + } + + return 0; + } + } + + TELEMETRY_LOG_WARN("Couldn't find client, possibly not registered yet."); + return -1; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -EINVAL; +} + +int32_t +rte_telemetry_register_client(struct telemetry_impl *telemetry, + const char *client_path) +{ + int ret, fd; + struct sockaddr_un addrs; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Could not initialize TELEMETRY API"); + return -ENODEV; + } + + if (client_path == NULL) { + TELEMETRY_LOG_ERR("Invalid client path"); + return -EINVAL; + } + + telemetry_client *client; + TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) { + if (strcmp(client_path, client->file_path) == 0) { + TELEMETRY_LOG_WARN("'%s' already registered", + client_path); + return -EINVAL; + } + } + + fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (fd == -1) { + TELEMETRY_LOG_ERR("Client socket error"); + return -EACCES; + } + + ret = rte_telemetry_set_socket_nonblock(fd); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not set socket to NONBLOCK"); + return -EPERM; + } + + addrs.sun_family = AF_UNIX; + strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path)); + telemetry_client *new_client = malloc(sizeof(telemetry_client)); + new_client->file_path = strdup(client_path); + new_client->fd = fd; + + if (connect(fd, (struct sockaddr *)&addrs, sizeof(addrs)) == -1) { + TELEMETRY_LOG_ERR("TELEMETRY client connect to %s didn't work", + client_path); + ret = rte_telemetry_client_cleanup(new_client); + if (ret < 0) { + TELEMETRY_LOG_ERR("Client cleanup failed"); + return -EPERM; + } + return -EINVAL; + } + + TAILQ_INSERT_HEAD(&telemetry->client_list_head, new_client, client_list); + + return 0; +} + +int32_t +rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf) +{ + int ret, action_int; + json_error_t error; + json_t *root = json_loads(buf, 0, &error); + + if (root == NULL) { + TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s", + error.text); + goto fail; + } else if (!json_is_object(root)) { + TELEMETRY_LOG_WARN("JSON Request is not a JSON object"); + goto fail; + } + + json_t *action = json_object_get(root, "action"); + if (action == NULL) { + TELEMETRY_LOG_WARN("Request does not have action field"); + goto fail; + } else if (!json_is_integer(action)) { + TELEMETRY_LOG_WARN("Action value is not an integer"); + goto fail; + } + + json_t *command = json_object_get(root, "command"); + if (command == NULL) { + TELEMETRY_LOG_WARN("Request does not have command field"); + goto fail; + } else if (!json_is_string(command)) { + TELEMETRY_LOG_WARN("Command value is not a string"); + goto fail; + } + + action_int = json_integer_value(action); + if (action_int != ACTION_POST) { + TELEMETRY_LOG_WARN("Invalid action code"); + goto fail; + } + + if (strcmp(json_string_value(command), "clients") != 0) { + TELEMETRY_LOG_WARN("Invalid command"); + goto fail; + } + + json_t *data = json_object_get(root, "data"); + if (data == NULL) { + TELEMETRY_LOG_WARN("Request does not have data field"); + goto fail; + } + + json_t *client_path = json_object_get(data, "client_path"); + if (client_path == NULL) { + TELEMETRY_LOG_WARN("Request does not have client_path field"); + goto fail; + } + + if (!json_is_string(client_path)) { + TELEMETRY_LOG_WARN("Client_path value is not a string"); + goto fail; + } + + ret = rte_telemetry_register_client(telemetry, + json_string_value(client_path)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not register client"); + telemetry->register_fail_count++; + goto fail; + } + + return 0; + +fail: + TELEMETRY_LOG_WARN("Client attempted to register with invalid message"); + json_decref(root); + return -1; +} + int telemetry_log_level; RTE_INIT(rte_telemetry_register); diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h index 569d56ab8..e3292cf40 100644 --- a/lib/librte_telemetry/rte_telemetry_internal.h +++ b/lib/librte_telemetry/rte_telemetry_internal.h @@ -3,6 +3,7 @@ */ #include +#include #ifndef _RTE_TELEMETRY_INTERNAL_H_ #define _RTE_TELEMETRY_INTERNAL_H_ @@ -23,6 +24,12 @@ extern int telemetry_log_level; #define TELEMETRY_LOG_INFO(fmt, args...) \ TELEMETRY_LOG(INFO, fmt, ## args) +typedef struct telemetry_client { + char *file_path; + int fd; + TAILQ_ENTRY(telemetry_client) client_list; +} telemetry_client; + typedef struct telemetry_impl { int accept_fd; int server_fd; @@ -31,6 +38,24 @@ typedef struct telemetry_impl { uint32_t socket_id; int reg_index; int metrics_register_done; + TAILQ_HEAD(, telemetry_client) client_list_head; + struct telemetry_client *request_client; + int register_fail_count; } telemetry_impl; +int32_t +rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf); + +int32_t +rte_telemetry_send_error_response(struct telemetry_impl *telemetry, + int error_type); + +int32_t +rte_telemetry_register_client(struct telemetry_impl *telemetry, + const char *client_path); + +int32_t +rte_telemetry_unregister_client(struct telemetry_impl *telemetry, + const char *client_path); + #endif diff --git a/mk/rte.app.mk b/mk/rte.app.mk index a14e83c71..c5aaa9da5 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -80,7 +80,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_COMPRESSDEV) += -lrte_compressdev _LDLIBS-$(CONFIG_RTE_LIBRTE_EVENTDEV) += -lrte_eventdev _LDLIBS-$(CONFIG_RTE_LIBRTE_RAWDEV) += -lrte_rawdev _LDLIBS-$(CONFIG_RTE_LIBRTE_METRICS) += -lrte_metrics -_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_telemetry +_LDLIBS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += -lrte_telemetry -ljansson _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER) += -lrte_timer _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += -lrte_mempool _LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING) += -lrte_mempool_ring From patchwork Sat Oct 27 09:17:44 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47515 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id CD42A58FE; Sat, 27 Oct 2018 11:19:55 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id C03B24C8C for ; Sat, 27 Oct 2018 11:19:38 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:38 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903890" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:35 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz Date: Sat, 27 Oct 2018 10:17:44 +0100 Message-Id: <20181027091750.17254-7-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 06/12] telemetry: add parser for client socket messages X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch adds the parser file. This is used to parse any messages that are received on any of the client sockets. Currently, the unregister functionality works using the parser. Functionality relating to getting statistic values for certain ports will be added in a subsequent patch, however the parsing involved for that command is added in this patch. Some of the parser code included is in preparation for future functionality, that is not implemented yet in this patchset. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren --- lib/librte_telemetry/Makefile | 1 + lib/librte_telemetry/meson.build | 4 +- lib/librte_telemetry/rte_telemetry.c | 8 + lib/librte_telemetry/rte_telemetry_internal.h | 13 + lib/librte_telemetry/rte_telemetry_parser.c | 569 ++++++++++++++++++ lib/librte_telemetry/rte_telemetry_parser.h | 14 + .../rte_telemetry_version.map | 1 + 7 files changed, 608 insertions(+), 2 deletions(-) create mode 100644 lib/librte_telemetry/rte_telemetry_parser.c create mode 100644 lib/librte_telemetry/rte_telemetry_parser.h diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile index 0d61361f4..95c72963a 100644 --- a/lib/librte_telemetry/Makefile +++ b/lib/librte_telemetry/Makefile @@ -21,6 +21,7 @@ LIBABIVER := 1 # library source files SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c # export include files SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build index 7b939805e..e459d0b80 100644 --- a/lib/librte_telemetry/meson.build +++ b/lib/librte_telemetry/meson.build @@ -1,8 +1,8 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright(c) 2018 Intel Corporation -sources = files('rte_telemetry.c') -headers = files('rte_telemetry.h', 'rte_telemetry_internal.h') +sources = files('rte_telemetry.c', 'rte_telemetry_parser.c') +headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h') deps += ['metrics', 'ethdev'] cflags += '-DALLOW_EXPERIMENTAL_API' diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c index 3c8b922f5..cac788438 100644 --- a/lib/librte_telemetry/rte_telemetry.c +++ b/lib/librte_telemetry/rte_telemetry.c @@ -17,6 +17,7 @@ #include "rte_telemetry.h" #include "rte_telemetry_internal.h" +#include "rte_telemetry_parser.h" #define BUF_SIZE 1024 #define ACTION_POST 1 @@ -283,6 +284,7 @@ rte_telemetry_accept_new_client(struct telemetry_impl *telemetry) static int32_t rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry) { + int ret; telemetry_client *client; char client_buf[BUF_SIZE]; int bytes; @@ -293,6 +295,12 @@ rte_telemetry_read_client_sockets(struct telemetry_impl *telemetry) if (bytes > 0) { client_buf[bytes] = '\0'; telemetry->request_client = client; + ret = rte_telemetry_parse(telemetry, client_buf); + if (ret < 0) { + TELEMETRY_LOG_WARN("Parse socket input failed: %i", + ret); + return -1; + } } } diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h index e3292cf40..86a5ba15e 100644 --- a/lib/librte_telemetry/rte_telemetry_internal.h +++ b/lib/librte_telemetry/rte_telemetry_internal.h @@ -43,6 +43,11 @@ typedef struct telemetry_impl { int register_fail_count; } telemetry_impl; +enum rte_telemetry_parser_actions { + ACTION_GET = 0, + ACTION_DELETE = 2 +}; + int32_t rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf); @@ -58,4 +63,12 @@ int32_t rte_telemetry_unregister_client(struct telemetry_impl *telemetry, const char *client_path); +/** + * This is a wrapper for the ethdev api rte_eth_find_next(). + * If rte_eth_find_next() returns the same port id that we passed it, + * then we know that that port is active. + */ +int32_t +rte_telemetry_is_port_active(int port_id); + #endif diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c new file mode 100644 index 000000000..556abbe4a --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry_parser.c @@ -0,0 +1,569 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rte_telemetry_internal.h" + +typedef int (*command_func)(struct telemetry_impl *, int, json_t *); + +struct rte_telemetry_command { + char *text; + command_func fn; +} command; + +static int32_t +rte_telemetry_command_clients(struct telemetry_impl *telemetry, int action, + json_t *data) +{ + int ret; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + if (action != ACTION_DELETE) { + TELEMETRY_LOG_WARN("Invalid action for this command"); + goto einval_fail; + } + + if (!json_is_object(data)) { + TELEMETRY_LOG_WARN("Invalid data provided for this command"); + goto einval_fail; + } + + json_t *client_path = json_object_get(data, "client_path"); + if (!json_is_string(client_path)) { + TELEMETRY_LOG_WARN("Command value is not a string"); + goto einval_fail; + } + + ret = rte_telemetry_unregister_client(telemetry, + json_string_value(client_path)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not unregister client"); + goto einval_fail; + } + + return 0; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; +} + +static int32_t +rte_telemetry_command_ports(struct telemetry_impl *telemetry, int action, + json_t *data) +{ + int ret; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + if (!json_is_null(data)) { + TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'ports' command"); + goto einval_fail; + } + + if (action != ACTION_GET) { + TELEMETRY_LOG_WARN("Invalid action for this command"); + goto einval_fail; + } + + return 0; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; +} + +static int32_t +rte_telemetry_command_ports_details(struct telemetry_impl *telemetry, + int action, json_t *data) +{ + json_t *value, *port_ids_json = json_object_get(data, "ports"); + uint64_t num_port_ids = json_array_size(port_ids_json); + int ret, port_ids[num_port_ids]; + RTE_SET_USED(port_ids); + size_t index; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + if (action != ACTION_GET) { + TELEMETRY_LOG_WARN("Invalid action for this command"); + goto einval_fail; + } + + if (!json_is_object(data)) { + TELEMETRY_LOG_WARN("Invalid data provided for this command"); + goto einval_fail; + } + + if (!json_is_array(port_ids_json)) { + TELEMETRY_LOG_WARN("Invalid Port ID array"); + goto einval_fail; + } + + json_array_foreach(port_ids_json, index, value) { + if (!json_is_integer(value)) { + TELEMETRY_LOG_WARN("Port ID given is invalid"); + goto einval_fail; + } + port_ids[index] = json_integer_value(value); + } + + return 0; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; +} + +static int32_t +rte_telemetry_command_port_stats(struct telemetry_impl *telemetry, int action, + json_t *data) +{ + int ret; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + if (!json_is_null(data)) { + TELEMETRY_LOG_WARN("Data should be NULL JSON object for 'port_stats' command"); + goto einval_fail; + } + + if (action != ACTION_GET) { + TELEMETRY_LOG_WARN("Invalid action for this command"); + goto einval_fail; + } + + return 0; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; +} + +static int32_t +rte_telemetry_stat_names_to_ids(struct telemetry_impl *telemetry, + const char * const *stat_names, uint32_t *stat_ids, + uint64_t num_stat_names) +{ + struct rte_metric_name *names; + int ret, num_metrics; + uint32_t i, k; + + if (stat_names == NULL) { + TELEMETRY_LOG_WARN("Invalid stat_names argument"); + goto einval_fail; + } + + if (num_stat_names <= 0) { + TELEMETRY_LOG_WARN("Invalid num_stat_names argument"); + goto einval_fail; + } + + num_metrics = rte_metrics_get_names(NULL, 0); + if (num_metrics < 0) { + TELEMETRY_LOG_ERR("Cannot get metrics count"); + goto eperm_fail; + } else if (num_metrics == 0) { + TELEMETRY_LOG_WARN("No metrics have been registered"); + goto eperm_fail; + } + + names = malloc(sizeof(struct rte_metric_name) * num_metrics); + if (names == NULL) { + TELEMETRY_LOG_ERR("Cannot allocate memory for names"); + + ret = rte_telemetry_send_error_response(telemetry, -ENOMEM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + + return -1; + } + + ret = rte_metrics_get_names(names, num_metrics); + if (ret < 0 || ret > num_metrics) { + TELEMETRY_LOG_ERR("Cannot get metrics names"); + free(names); + goto eperm_fail; + } + + k = 0; + for (i = 0; i < (uint32_t)num_stat_names; i++) { + uint32_t j; + for (j = 0; j < (uint32_t)num_metrics; j++) { + if (strcmp(stat_names[i], names[j].name) == 0) { + stat_ids[k] = j; + k++; + break; + } + } + } + + if (k != num_stat_names) { + TELEMETRY_LOG_WARN("Invalid stat names provided"); + free(names); + goto einval_fail; + } + + free(names); + return 0; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + +eperm_fail: + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; +} + +int32_t +rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry, + int action, json_t *data) +{ + int ret, num_metrics, i, p; + struct rte_metric_name *names; + uint64_t num_port_ids = 0; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + if (action != ACTION_GET) { + TELEMETRY_LOG_WARN("Invalid action for this command"); + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + if (json_is_object(data)) { + TELEMETRY_LOG_WARN("Invalid data provided for this command"); + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + num_metrics = rte_metrics_get_names(NULL, 0); + if (num_metrics < 0) { + TELEMETRY_LOG_ERR("Cannot get metrics count"); + + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + + return -1; + } else if (num_metrics == 0) { + TELEMETRY_LOG_ERR("No metrics to display (none have been registered)"); + + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + + return -1; + } + + names = malloc(sizeof(struct rte_metric_name) * num_metrics); + if (names == NULL) { + TELEMETRY_LOG_ERR("Cannot allocate memory"); + ret = rte_telemetry_send_error_response(telemetry, + -ENOMEM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + const char *stat_names[num_metrics]; + uint32_t stat_ids[num_metrics]; + + RTE_ETH_FOREACH_DEV(p) { + num_port_ids++; + } + + if (!num_port_ids) { + TELEMETRY_LOG_WARN("No active ports"); + + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + + goto fail; + } + + ret = rte_metrics_get_names(names, num_metrics); + for (i = 0; i < num_metrics; i++) + stat_names[i] = names[i].name; + + ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids, + num_metrics); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not convert stat names to IDs"); + goto fail; + } + + return 0; + +fail: + free(names); + return -1; +} + +int32_t +rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl + *telemetry, int action, 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_port_ids = json_array_size(port_ids_json); + uint64_t num_stat_names = json_array_size(stat_names_json); + const char *stat_names[num_stat_names]; + uint32_t port_ids[num_port_ids], stat_ids[num_stat_names]; + size_t index; + json_t *value; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + if (action != ACTION_GET) { + TELEMETRY_LOG_WARN("Invalid action for this command"); + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + if (!json_is_object(data)) { + TELEMETRY_LOG_WARN("Invalid data provided for this command"); + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + if (!json_is_array(port_ids_json) || + !json_is_array(stat_names_json)) { + TELEMETRY_LOG_WARN("Invalid input data array(s)"); + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + json_array_foreach(port_ids_json, index, value) { + if (!json_is_integer(value)) { + TELEMETRY_LOG_WARN("Port ID given is not valid"); + ret = rte_telemetry_send_error_response(telemetry, + -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + port_ids[index] = json_integer_value(value); + ret = rte_telemetry_is_port_active(port_ids[index]); + if (ret < 1) { + ret = rte_telemetry_send_error_response(telemetry, + -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + } + + json_array_foreach(stat_names_json, index, value) { + if (!json_is_string(value)) { + TELEMETRY_LOG_WARN("Stat Name given is not a string"); + + ret = rte_telemetry_send_error_response(telemetry, + -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + + return -1; + } + stat_names[index] = json_string_value(value); + } + + ret = rte_telemetry_stat_names_to_ids(telemetry, stat_names, stat_ids, + num_stat_names); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not convert stat names to IDs"); + return -1; + } + return 0; +} + +static int32_t +rte_telemetry_parse_command(struct telemetry_impl *telemetry, int action, + const char *command, json_t *data) +{ + int ret; + uint32_t i; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + struct rte_telemetry_command commands[] = { + { + .text = "clients", + .fn = &rte_telemetry_command_clients + }, + { + .text = "ports", + .fn = &rte_telemetry_command_ports + }, + { + .text = "ports_details", + .fn = &rte_telemetry_command_ports_details + }, + { + .text = "port_stats", + .fn = &rte_telemetry_command_port_stats + }, + { + .text = "ports_stats_values_by_name", + .fn = &rte_telemetry_command_ports_stats_values_by_name + }, + { + .text = "ports_all_stat_values", + .fn = &rte_telemetry_command_ports_all_stat_values + } + }; + + const uint32_t num_commands = RTE_DIM(commands); + + for (i = 0; i < num_commands; i++) { + if (strcmp(command, commands[i].text) == 0) { + ret = commands[i].fn(telemetry, action, data); + if (ret < 0) { + TELEMETRY_LOG_ERR("Command Function for %s failed", + commands[i].text); + return -1; + } + return 0; + } + } + + TELEMETRY_LOG_WARN("\"%s\" command not found", command); + + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + + return -1; +} + +int32_t __rte_experimental +rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data) +{ + int ret, action_int; + json_error_t error; + json_t *root, *action, *command, *data; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + root = json_loads(socket_rx_data, 0, &error); + if (root == NULL) { + TELEMETRY_LOG_WARN("Could not load JSON object from data passed in : %s", + error.text); + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -EPERM; + } else if (!json_is_object(root)) { + TELEMETRY_LOG_WARN("JSON Request is not a JSON object"); + json_decref(root); + goto einval_fail; + } + + action = json_object_get(root, "action"); + if (action == NULL) { + TELEMETRY_LOG_WARN("Request does not have action field"); + goto einval_fail; + } else if (!json_is_integer(action)) { + TELEMETRY_LOG_WARN("Action value is not an integer"); + goto einval_fail; + } + + command = json_object_get(root, "command"); + if (command == NULL) { + TELEMETRY_LOG_WARN("Request does not have command field"); + goto einval_fail; + } else if (!json_is_string(command)) { + TELEMETRY_LOG_WARN("Command value is not a string"); + goto einval_fail; + } + + action_int = json_integer_value(action); + if (action_int != ACTION_GET && action_int != ACTION_DELETE) { + TELEMETRY_LOG_WARN("Invalid action code"); + goto einval_fail; + } + + const char *command_string = json_string_value(command); + data = json_object_get(root, "data"); + if (data == NULL) { + TELEMETRY_LOG_WARN("Request does not have data field"); + goto einval_fail; + } + + ret = rte_telemetry_parse_command(telemetry, action_int, command_string, + data); + if (ret < 0) { + TELEMETRY_LOG_WARN("Could not parse command"); + return -EINVAL; + } + + return 0; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not send error"); + return -EPERM; + } + return -EINVAL; +} diff --git a/lib/librte_telemetry/rte_telemetry_parser.h b/lib/librte_telemetry/rte_telemetry_parser.h new file mode 100644 index 000000000..b7051945b --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry_parser.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include "rte_telemetry_internal.h" +#include "rte_compat.h" + +#ifndef _RTE_TELEMETRY_PARSER_H_ +#define _RTE_TELEMETRY_PARSER_H_ + +int32_t __rte_experimental +rte_telemetry_parse(struct telemetry_impl *telemetry, char *socket_rx_data); + +#endif diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map index bbcd9a796..fb0b5be62 100644 --- a/lib/librte_telemetry/rte_telemetry_version.map +++ b/lib/librte_telemetry/rte_telemetry_version.map @@ -3,6 +3,7 @@ EXPERIMENTAL { rte_telemetry_cleanup; rte_telemetry_init; + rte_telemetry_parse; local: *; }; From patchwork Sat Oct 27 09:17:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47516 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id C03325323; Sat, 27 Oct 2018 11:20:23 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 48C524F94 for ; Sat, 27 Oct 2018 11:19:41 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:40 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903894" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:38 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz Date: Sat, 27 Oct 2018 10:17:45 +0100 Message-Id: <20181027091750.17254-8-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 07/12] telemetry: update metrics before sending stats X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch adds functionality to update the statistics in the metrics library with values from the ethdev stats. Values need to be updated before they are encoded into a JSON message and sent to the client that requested them. The JSON encoding will be added in a subsequent patch. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren --- lib/librte_telemetry/rte_telemetry.c | 134 ++++++++++++++++++ lib/librte_telemetry/rte_telemetry_internal.h | 4 + lib/librte_telemetry/rte_telemetry_parser.c | 17 +++ 3 files changed, 155 insertions(+) diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c index cac788438..7616e7c91 100644 --- a/lib/librte_telemetry/rte_telemetry.c +++ b/lib/librte_telemetry/rte_telemetry.c @@ -46,6 +46,78 @@ rte_telemetry_is_port_active(int port_id) return 0; } +static int32_t +rte_telemetry_update_metrics_ethdev(struct telemetry_impl *telemetry, + uint16_t port_id, int reg_start_index) +{ + int ret, num_xstats, i; + struct rte_eth_xstat *eth_xstats; + + if (!rte_eth_dev_is_valid_port(port_id)) { + TELEMETRY_LOG_ERR("port_id: %d is invalid", port_id); + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + ret = rte_telemetry_is_port_active(port_id); + if (ret < 1) { + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + num_xstats = rte_eth_xstats_get(port_id, NULL, 0); + if (num_xstats < 0) { + TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id, + num_xstats); + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats); + if (eth_xstats == NULL) { + TELEMETRY_LOG_ERR("Failed to malloc memory for xstats"); + ret = rte_telemetry_send_error_response(telemetry, -ENOMEM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats); + if (ret < 0 || ret > num_xstats) { + free(eth_xstats); + TELEMETRY_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d", + port_id, num_xstats, ret); + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + uint64_t xstats_values[num_xstats]; + for (i = 0; i < num_xstats; i++) + xstats_values[i] = eth_xstats[i].value; + + ret = rte_metrics_update_values(port_id, reg_start_index, xstats_values, + num_xstats); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not update metrics values"); + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + free(eth_xstats); + return -1; + } + + free(eth_xstats); + return 0; +} + int32_t rte_telemetry_write_to_socket(struct telemetry_impl *telemetry, const char *json_string) @@ -130,6 +202,68 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry, return 0; } +int32_t +rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids, + uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry) +{ + int ret, i; + char *json_buffer = NULL; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Invalid telemetry argument"); + return -1; + } + + if (metric_ids == NULL) { + TELEMETRY_LOG_ERR("Invalid metric_ids array"); + goto einval_fail; + } + + if (num_metric_ids < 0) { + TELEMETRY_LOG_ERR("Invalid num_metric_ids, must be positive"); + goto einval_fail; + } + + if (port_ids == NULL) { + TELEMETRY_LOG_ERR("Invalid port_ids array"); + goto einval_fail; + } + + if (num_port_ids < 0) { + TELEMETRY_LOG_ERR("Invalid num_port_ids, must be positive"); + goto einval_fail; + } + + for (i = 0; i < num_port_ids; i++) { + if (!rte_eth_dev_is_valid_port(port_ids[i])) { + TELEMETRY_LOG_ERR("Port: %d invalid", port_ids[i]); + goto einval_fail; + } + + ret = rte_telemetry_update_metrics_ethdev(telemetry, + port_ids[i], telemetry->reg_index); + if (ret < 0) { + TELEMETRY_LOG_ERR("Failed to update ethdev metrics"); + return -1; + } + } + + ret = rte_telemetry_write_to_socket(telemetry, json_buffer); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not write to socket"); + return -1; + } + + return 0; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; +} + + static int32_t rte_telemetry_reg_ethdev_to_metrics(uint16_t port_id) { diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h index 86a5ba15e..0082cb2ca 100644 --- a/lib/librte_telemetry/rte_telemetry_internal.h +++ b/lib/librte_telemetry/rte_telemetry_internal.h @@ -71,4 +71,8 @@ rte_telemetry_unregister_client(struct telemetry_impl *telemetry, int32_t rte_telemetry_is_port_active(int port_id); +int32_t +rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids, + uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry); + #endif diff --git a/lib/librte_telemetry/rte_telemetry_parser.c b/lib/librte_telemetry/rte_telemetry_parser.c index 556abbe4a..03a58a2fd 100644 --- a/lib/librte_telemetry/rte_telemetry_parser.c +++ b/lib/librte_telemetry/rte_telemetry_parser.c @@ -258,6 +258,7 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry, int ret, num_metrics, i, p; struct rte_metric_name *names; uint64_t num_port_ids = 0; + uint32_t port_ids[RTE_MAX_ETHPORTS]; if (telemetry == NULL) { TELEMETRY_LOG_ERR("Invalid telemetry argument"); @@ -313,6 +314,7 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry, uint32_t stat_ids[num_metrics]; RTE_ETH_FOREACH_DEV(p) { + port_ids[num_port_ids] = p; num_port_ids++; } @@ -337,6 +339,13 @@ rte_telemetry_command_ports_all_stat_values(struct telemetry_impl *telemetry, goto fail; } + ret = rte_telemetry_send_ports_stats_values(stat_ids, num_metrics, + port_ids, num_port_ids, telemetry); + if (ret < 0) { + TELEMETRY_LOG_ERR("Sending ports stats values failed"); + goto fail; + } + return 0; fail: @@ -428,6 +437,14 @@ rte_telemetry_command_ports_stats_values_by_name(struct telemetry_impl TELEMETRY_LOG_ERR("Could not convert stat names to IDs"); return -1; } + + ret = rte_telemetry_send_ports_stats_values(stat_ids, num_stat_names, + port_ids, num_port_ids, telemetry); + if (ret < 0) { + TELEMETRY_LOG_ERR("Sending ports stats values failed"); + return -1; + } + return 0; } From patchwork Sat Oct 27 09:17:46 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47517 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 0C7795B12; Sat, 27 Oct 2018 11:20:26 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 4253B548B for ; Sat, 27 Oct 2018 11:19:44 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:43 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903902" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:41 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz Date: Sat, 27 Oct 2018 10:17:46 +0100 Message-Id: <20181027091750.17254-9-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 08/12] telemetry: format json response when sending stats X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch adds functionality to create a JSON message in order to send it to a client socket. When stats are requested by a client, they are retrieved from the metrics library and encoded in JSON format. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren --- lib/librte_telemetry/Makefile | 1 + lib/librte_telemetry/meson.build | 4 +- lib/librte_telemetry/rte_telemetry.c | 962 +++++++++++++++++- lib/librte_telemetry/rte_telemetry.h | 15 + lib/librte_telemetry/rte_telemetry_internal.h | 3 + .../rte_telemetry_parser_test.c | 534 ++++++++++ .../rte_telemetry_parser_test.h | 39 + .../rte_telemetry_socket_tests.h | 36 + .../rte_telemetry_version.map | 1 + 9 files changed, 1591 insertions(+), 4 deletions(-) create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.c create mode 100644 lib/librte_telemetry/rte_telemetry_parser_test.h create mode 100644 lib/librte_telemetry/rte_telemetry_socket_tests.h diff --git a/lib/librte_telemetry/Makefile b/lib/librte_telemetry/Makefile index 95c72963a..1a0506913 100644 --- a/lib/librte_telemetry/Makefile +++ b/lib/librte_telemetry/Makefile @@ -22,6 +22,7 @@ LIBABIVER := 1 # library source files SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) := rte_telemetry.c SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser.c +SRCS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += rte_telemetry_parser_test.c # export include files SYMLINK-$(CONFIG_RTE_LIBRTE_TELEMETRY)-include := rte_telemetry.h diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build index e459d0b80..b3bbf5bd0 100644 --- a/lib/librte_telemetry/meson.build +++ b/lib/librte_telemetry/meson.build @@ -1,8 +1,8 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright(c) 2018 Intel Corporation -sources = files('rte_telemetry.c', 'rte_telemetry_parser.c') -headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h') +sources = files('rte_telemetry.c', 'rte_telemetry_parser.c', 'rte_telemetry_parser_test.c') +headers = files('rte_telemetry.h', 'rte_telemetry_internal.h', 'rte_telemetry_parser.h', 'rte_telemetry_parser_test.h') deps += ['metrics', 'ethdev'] cflags += '-DALLOW_EXPERIMENTAL_API' diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c index 7616e7c91..5b66b71c4 100644 --- a/lib/librte_telemetry/rte_telemetry.c +++ b/lib/librte_telemetry/rte_telemetry.c @@ -18,13 +18,32 @@ #include "rte_telemetry.h" #include "rte_telemetry_internal.h" #include "rte_telemetry_parser.h" +#include "rte_telemetry_parser_test.h" +#include "rte_telemetry_socket_tests.h" #define BUF_SIZE 1024 #define ACTION_POST 1 #define SLEEP_TIME 10 +#define SELFTEST_VALID_CLIENT "/var/run/dpdk/valid_client" +#define SELFTEST_INVALID_CLIENT "/var/run/dpdk/invalid_client" +#define SOCKET_TEST_CLIENT_PATH "/var/run/dpdk/client" + static telemetry_impl *static_telemetry; +struct telemetry_message_test { + char *test_name; + int (*test_func_ptr)(struct telemetry_impl *telemetry, int fd); +}; + +struct json_data { + char *status_code; + char *data; + int port; + char *stat_name; + int stat_value; +}; + static void rte_telemetry_get_runtime_dir(char *socket_path, size_t size) { @@ -190,7 +209,7 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry, return -EPERM; } - json_buffer = json_dumps(root, JSON_INDENT(2)); + json_buffer = json_dumps(root, 0); json_decref(root); ret = rte_telemetry_write_to_socket(telemetry, json_buffer); @@ -202,6 +221,304 @@ rte_telemetry_send_error_response(struct telemetry_impl *telemetry, return 0; } +static int +rte_telemetry_get_metrics(struct telemetry_impl *telemetry, uint32_t port_id, + struct rte_metric_value *metrics, struct rte_metric_name *names, + int num_metrics) +{ + int ret, num_values; + + if (num_metrics < 0) { + TELEMETRY_LOG_ERR("Invalid metrics count"); + goto einval_fail; + } else if (num_metrics == 0) { + TELEMETRY_LOG_ERR("No metrics to display (none have been registered)"); + goto eperm_fail; + } + + if (metrics == NULL) { + TELEMETRY_LOG_ERR("Metrics must be initialised."); + goto einval_fail; + } + + if (names == NULL) { + TELEMETRY_LOG_ERR("Names must be initialised."); + goto einval_fail; + } + + ret = rte_metrics_get_names(names, num_metrics); + if (ret < 0 || ret > num_metrics) { + TELEMETRY_LOG_ERR("Cannot get metrics names"); + goto eperm_fail; + } + + num_values = rte_metrics_get_values(port_id, NULL, 0); + ret = rte_metrics_get_values(port_id, metrics, num_values); + if (ret < 0 || ret > num_values) { + TELEMETRY_LOG_ERR("Cannot get metrics values"); + goto eperm_fail; + } + + return 0; + +eperm_fail: + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + +} + +static int32_t +rte_telemetry_json_format_stat(struct telemetry_impl *telemetry, json_t *stats, + const char *metric_name, uint64_t metric_value) +{ + int ret; + json_t *stat = json_object(); + + if (stat == NULL) { + TELEMETRY_LOG_ERR("Could not create stat JSON object"); + goto eperm_fail; + } + + ret = json_object_set_new(stat, "name", json_string(metric_name)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Stat Name field cannot be set"); + goto eperm_fail; + } + + ret = json_object_set_new(stat, "value", json_integer(metric_value)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Stat Value field cannot be set"); + goto eperm_fail; + } + + ret = json_array_append_new(stats, stat); + if (ret < 0) { + TELEMETRY_LOG_ERR("Stat cannot be added to stats json array"); + goto eperm_fail; + } + + return 0; + +eperm_fail: + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + +} + +static int32_t +rte_telemetry_json_format_port(struct telemetry_impl *telemetry, + uint32_t port_id, json_t *ports, uint32_t *metric_ids, + uint32_t num_metric_ids) +{ + struct rte_metric_value *metrics = 0; + struct rte_metric_name *names = 0; + int num_metrics, ret, err_ret; + json_t *port, *stats; + uint32_t i; + + num_metrics = rte_metrics_get_names(NULL, 0); + if (num_metrics < 0) { + TELEMETRY_LOG_ERR("Cannot get metrics count"); + goto einval_fail; + } else if (num_metrics == 0) { + TELEMETRY_LOG_ERR("No metrics to display (none have been registered)"); + goto eperm_fail; + } + + metrics = malloc(sizeof(struct rte_metric_value) * num_metrics); + names = malloc(sizeof(struct rte_metric_name) * num_metrics); + if (metrics == NULL || names == NULL) { + TELEMETRY_LOG_ERR("Cannot allocate memory"); + free(metrics); + free(names); + + err_ret = rte_telemetry_send_error_response(telemetry, -ENOMEM); + if (err_ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + } + + ret = rte_telemetry_get_metrics(telemetry, port_id, metrics, names, + num_metrics); + if (ret < 0) { + free(metrics); + free(names); + TELEMETRY_LOG_ERR("rte_telemetry_get_metrics failed"); + return -1; + } + + port = json_object(); + stats = json_array(); + if (port == NULL || stats == NULL) { + TELEMETRY_LOG_ERR("Could not create port/stats JSON objects"); + goto eperm_fail; + } + + ret = json_object_set_new(port, "port", json_integer(port_id)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Port field cannot be set"); + goto eperm_fail; + } + + for (i = 0; i < num_metric_ids; i++) { + int metric_id = metric_ids[i]; + int metric_index = -1; + int metric_name_key = -1; + int32_t j; + uint64_t metric_value; + + if (metric_id >= num_metrics) { + TELEMETRY_LOG_ERR("Metric_id: %d is not valid", + metric_id); + goto einval_fail; + } + + for (j = 0; j < num_metrics; j++) { + if (metrics[j].key == metric_id) { + metric_name_key = metrics[j].key; + metric_index = j; + break; + } + } + + const char *metric_name = names[metric_name_key].name; + metric_value = metrics[metric_index].value; + + if (metric_name_key < 0 || metric_index < 0) { + TELEMETRY_LOG_ERR("Could not get metric name/index"); + goto eperm_fail; + } + + ret = rte_telemetry_json_format_stat(telemetry, stats, + metric_name, metric_value); + if (ret < 0) { + TELEMETRY_LOG_ERR("Format stat with id: %u failed", + metric_id); + free(metrics); + free(names); + return -1; + } + } + + if (json_array_size(stats) == 0) + ret = json_object_set_new(port, "stats", json_null()); + else + ret = json_object_set_new(port, "stats", stats); + + if (ret < 0) { + TELEMETRY_LOG_ERR("Stats object cannot be set"); + goto eperm_fail; + } + + ret = json_array_append_new(ports, port); + if (ret < 0) { + TELEMETRY_LOG_ERR("Port object cannot be added to ports array"); + goto eperm_fail; + } + + free(metrics); + free(names); + return 0; + +eperm_fail: + free(metrics); + free(names); + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + +einval_fail: + free(metrics); + free(names); + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; +} + +static int32_t +rte_telemetry_encode_json_format(struct telemetry_impl *telemetry, + uint32_t *port_ids, uint32_t num_port_ids, uint32_t *metric_ids, + uint32_t num_metric_ids, char **json_buffer) +{ + int ret; + json_t *root, *ports; + uint32_t i; + + if (num_port_ids <= 0 || num_metric_ids <= 0) { + TELEMETRY_LOG_ERR("Please provide port and metric ids to query"); + goto einval_fail; + } + + ports = json_array(); + if (ports == NULL) { + TELEMETRY_LOG_ERR("Could not create ports JSON array"); + goto eperm_fail; + } + + for (i = 0; i < num_port_ids; i++) { + if (!rte_eth_dev_is_valid_port(port_ids[i])) { + TELEMETRY_LOG_ERR("Port: %d invalid", port_ids[i]); + goto einval_fail; + } + } + + for (i = 0; i < num_port_ids; i++) { + ret = rte_telemetry_json_format_port(telemetry, port_ids[i], + ports, metric_ids, num_metric_ids); + if (ret < 0) { + TELEMETRY_LOG_ERR("Format port in JSON failed"); + return -1; + } + } + + root = json_object(); + if (root == NULL) { + TELEMETRY_LOG_ERR("Could not create root JSON object"); + goto eperm_fail; + } + + ret = json_object_set_new(root, "status_code", + json_string("Status OK: 200")); + if (ret < 0) { + TELEMETRY_LOG_ERR("Status code field cannot be set"); + goto eperm_fail; + } + + ret = json_object_set_new(root, "data", ports); + if (ret < 0) { + TELEMETRY_LOG_ERR("Data field cannot be set"); + goto eperm_fail; + } + + *json_buffer = json_dumps(root, JSON_INDENT(2)); + json_decref(root); + return 0; + +eperm_fail: + ret = rte_telemetry_send_error_response(telemetry, -EPERM); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; + +einval_fail: + ret = rte_telemetry_send_error_response(telemetry, -EINVAL); + if (ret < 0) + TELEMETRY_LOG_ERR("Could not send error"); + return -1; +} + int32_t rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids, uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry) @@ -241,13 +558,20 @@ rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids, } ret = rte_telemetry_update_metrics_ethdev(telemetry, - port_ids[i], telemetry->reg_index); + port_ids[i], telemetry->reg_index); if (ret < 0) { TELEMETRY_LOG_ERR("Failed to update ethdev metrics"); return -1; } } + ret = rte_telemetry_encode_json_format(telemetry, port_ids, + num_port_ids, metric_ids, num_metric_ids, &json_buffer); + if (ret < 0) { + TELEMETRY_LOG_ERR("JSON encode function failed"); + return -1; + } + ret = rte_telemetry_write_to_socket(telemetry, json_buffer); if (ret < 0) { TELEMETRY_LOG_ERR("Could not write to socket"); @@ -335,6 +659,7 @@ static int32_t rte_telemetry_initial_accept(struct telemetry_impl *telemetry) { uint16_t pid; + int ret; RTE_ETH_FOREACH_DEV(pid) { telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid); @@ -347,6 +672,18 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry) } telemetry->metrics_register_done = 1; + ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index, + telemetry->server_fd); + if (ret < 0) + return -1; + + ret = rte_telemetry_parser_test(telemetry); + if (ret < 0) { + TELEMETRY_LOG_ERR("Parser Tests Failed"); + return -1; + } + + TELEMETRY_LOG_INFO("Success - All Parser Tests Passed"); return 0; } @@ -834,6 +1171,627 @@ rte_telemetry_parse_client_message(struct telemetry_impl *telemetry, char *buf) return -1; } +int32_t +rte_telemetry_dummy_client_socket(const char *valid_client_path) +{ + int sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + struct sockaddr_un addr = {0}; + + if (sockfd < 0) { + TELEMETRY_LOG_ERR("Test socket creation failure"); + return -1; + } + + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, valid_client_path, sizeof(addr.sun_path)); + unlink(valid_client_path); + + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + TELEMETRY_LOG_ERR("Test socket binding failure"); + return -1; + } + + if (listen(sockfd, 1) < 0) { + TELEMETRY_LOG_ERR("Listen failure"); + return -1; + } + + return sockfd; +} + +int32_t __rte_experimental +rte_telemetry_selftest(void) +{ + const char *invalid_client_path = SELFTEST_INVALID_CLIENT; + const char *valid_client_path = SELFTEST_VALID_CLIENT; + int ret, sockfd; + + TELEMETRY_LOG_INFO("Selftest"); + + ret = rte_telemetry_init(); + if (ret < 0) { + TELEMETRY_LOG_ERR("Valid initialisation test failed"); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Valid initialisation test passed"); + + ret = rte_telemetry_init(); + if (ret != -EALREADY) { + TELEMETRY_LOG_ERR("Invalid initialisation test failed"); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Invalid initialisation test passed"); + + ret = rte_telemetry_unregister_client(static_telemetry, + invalid_client_path); + if (ret != -EPERM) { + TELEMETRY_LOG_ERR("Invalid unregister test failed"); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Invalid unregister test passed"); + + sockfd = rte_telemetry_dummy_client_socket(valid_client_path); + if (sockfd < 0) { + TELEMETRY_LOG_ERR("Test socket creation failed"); + return -1; + } + + ret = rte_telemetry_register_client(static_telemetry, valid_client_path); + if (ret != 0) { + TELEMETRY_LOG_ERR("Valid register test failed: %i", ret); + return -1; + } + + accept(sockfd, NULL, NULL); + TELEMETRY_LOG_INFO("Success - Valid register test passed"); + + ret = rte_telemetry_register_client(static_telemetry, valid_client_path); + if (ret != -EINVAL) { + TELEMETRY_LOG_ERR("Invalid register test failed: %i", ret); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Invalid register test passed"); + + ret = rte_telemetry_unregister_client(static_telemetry, + invalid_client_path); + if (ret != -1) { + TELEMETRY_LOG_ERR("Invalid unregister test failed: %i", ret); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Invalid unregister test passed"); + + ret = rte_telemetry_unregister_client(static_telemetry, valid_client_path); + if (ret != 0) { + TELEMETRY_LOG_ERR("Valid unregister test failed: %i", ret); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Valid unregister test passed"); + + ret = rte_telemetry_cleanup(); + if (ret < 0) { + TELEMETRY_LOG_ERR("Cleanup test failed"); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Valid cleanup test passed"); + + return 0; +} + +int32_t +rte_telemetry_socket_messaging_testing(int index, int socket) +{ + struct telemetry_impl *telemetry = calloc(1, sizeof(telemetry_impl)); + int fd, bad_send_fd, send_fd, bad_fd, bad_recv_fd, recv_fd, ret; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Could not initialize Telemetry API"); + return -1; + } + + telemetry->server_fd = socket; + telemetry->reg_index = index; + TELEMETRY_LOG_INFO("Beginning Telemetry socket message Selftest"); + rte_telemetry_socket_test_setup(telemetry, &send_fd, &recv_fd); + TELEMETRY_LOG_INFO("Register valid client test"); + + ret = rte_telemetry_socket_register_test(telemetry, &fd, send_fd, + recv_fd); + if (ret < 0) { + TELEMETRY_LOG_ERR("Register valid client test failed!"); + free(telemetry); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Register valid client test passed!"); + + TELEMETRY_LOG_INFO("Register invalid/same client test"); + ret = rte_telemetry_socket_test_setup(telemetry, &bad_send_fd, + &bad_recv_fd); + ret = rte_telemetry_socket_register_test(telemetry, &bad_fd, + bad_send_fd, bad_recv_fd); + if (!ret) { + TELEMETRY_LOG_ERR("Register invalid/same client test failed!"); + free(telemetry); + return -1; + } + + TELEMETRY_LOG_INFO("Success - Register invalid/same client test passed!"); + + ret = rte_telemetry_json_socket_message_test(telemetry, fd); + if (ret < 0) { + free(telemetry); + return -1; + } + + free(telemetry); + return 0; +} + +int32_t +rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd, + int send_fd, int recv_fd) +{ + int ret; + char good_req_string[BUF_SIZE]; + + snprintf(good_req_string, sizeof(good_req_string), + "{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\"" + ":\"%s\"}}", SOCKET_TEST_CLIENT_PATH); + + listen(recv_fd, 1); + + ret = send(send_fd, good_req_string, strlen(good_req_string), 0); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not send message over socket"); + return -1; + } + + rte_telemetry_run(telemetry); + + if (telemetry->register_fail_count != 0) + return -1; + + *fd = accept(recv_fd, NULL, NULL); + + return 0; +} + +int32_t +rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd, + int *recv_fd) +{ + int ret; + const char *client_path = SOCKET_TEST_CLIENT_PATH; + char socket_path[BUF_SIZE]; + struct sockaddr_un addr = {0}; + struct sockaddr_un addrs = {0}; + *send_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + *recv_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + + listen(telemetry->server_fd, 5); + addr.sun_family = AF_UNIX; + rte_telemetry_get_runtime_dir(socket_path, sizeof(socket_path)); + strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path)); + + ret = connect(*send_fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not connect socket"); + return -1; + } + + telemetry->accept_fd = accept(telemetry->server_fd, NULL, NULL); + + addrs.sun_family = AF_UNIX; + strlcpy(addrs.sun_path, client_path, sizeof(addrs.sun_path)); + unlink(client_path); + + ret = bind(*recv_fd, (struct sockaddr *)&addrs, sizeof(addrs)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not bind socket"); + return -1; + } + + return 0; +} + +static int32_t +rte_telemetry_stat_parse(char *buf, struct json_data *json_data_struct) +{ + json_error_t error; + json_t *root = json_loads(buf, 0, &error); + int arraylen, i; + json_t *status, *dataArray, *port, *stats, *name, *value, *dataArrayObj, + *statsArrayObj; + + stats = NULL; + port = NULL; + name = NULL; + + if (buf == NULL) { + TELEMETRY_LOG_ERR("JSON message is NULL"); + return -EINVAL; + } + + if (root == NULL) { + TELEMETRY_LOG_ERR("Could not load JSON object from data passed in : %s", + error.text); + return -EPERM; + } else if (!json_is_object(root)) { + TELEMETRY_LOG_ERR("JSON Request is not a JSON object"); + json_decref(root); + return -EINVAL; + } + + status = json_object_get(root, "status_code"); + if (!status) { + TELEMETRY_LOG_ERR("Request does not have status field"); + return -EINVAL; + } else if (!json_is_string(status)) { + TELEMETRY_LOG_ERR("Status value is not a string"); + return -EINVAL; + } + + json_data_struct->status_code = strdup(json_string_value(status)); + + dataArray = json_object_get(root, "data"); + if (dataArray == NULL) { + TELEMETRY_LOG_ERR("Request does not have data field"); + return -EINVAL; + } + + arraylen = json_array_size(dataArray); + if (arraylen == 0) { + json_data_struct->data = "null"; + return -EINVAL; + } + + for (i = 0; i < arraylen; i++) { + dataArrayObj = json_array_get(dataArray, i); + port = json_object_get(dataArrayObj, "port"); + stats = json_object_get(dataArrayObj, "stats"); + } + + if (port == NULL) { + TELEMETRY_LOG_ERR("Request does not have port field"); + return -EINVAL; + } + + if (!json_is_integer(port)) { + TELEMETRY_LOG_ERR("Port value is not an integer"); + return -EINVAL; + } + + json_data_struct->port = json_integer_value(port); + + if (stats == NULL) { + TELEMETRY_LOG_ERR("Request does not have stats field"); + return -EINVAL; + } + + arraylen = json_array_size(stats); + for (i = 0; i < arraylen; i++) { + statsArrayObj = json_array_get(stats, i); + name = json_object_get(statsArrayObj, "name"); + value = json_object_get(statsArrayObj, "value"); + } + + if (name == NULL) { + TELEMETRY_LOG_ERR("Request does not have name field"); + return -EINVAL; + } + + if (!json_is_string(name)) { + TELEMETRY_LOG_ERR("Stat name value is not a string"); + return -EINVAL; + } + + json_data_struct->stat_name = strdup(json_string_value(name)); + + if (value == NULL) { + TELEMETRY_LOG_ERR("Request does not have value field"); + return -EINVAL; + } + + if (!json_is_integer(value)) { + TELEMETRY_LOG_ERR("Stat value is not an integer"); + return -EINVAL; + } + + json_data_struct->stat_value = json_integer_value(value); + + return 0; +} + +static void +rte_telemetry_free_test_data(struct json_data *data) +{ + free(data->status_code); + free(data->stat_name); + free(data); +} + +int32_t +rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd) +{ + int ret; + int port = 0; + int value = 0; + int fail_count = 0; + int buffer_read = 0; + char buf[BUF_SIZE]; + struct json_data *data_struct; + errno = 0; + const char *status = "Status OK: 200"; + const char *name = "rx_good_packets"; + const char *valid_json_message = "{\"action\":0,\"command\":" + "\"ports_stats_values_by_name\",\"data\":{\"ports\"" + ":[0],\"stats\":[\"rx_good_packets\"]}}"; + + ret = send(fd, valid_json_message, strlen(valid_json_message), 0); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not send message over socket"); + return -1; + } + + rte_telemetry_run(telemetry); + buffer_read = recv(fd, buf, BUF_SIZE-1, 0); + + if (buffer_read == -1) { + TELEMETRY_LOG_ERR("Read error"); + return -1; + } + + buf[buffer_read] = '\0'; + data_struct = calloc(1, sizeof(struct json_data)); + ret = rte_telemetry_stat_parse(buf, data_struct); + + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not parse stats"); + fail_count++; + } + + if (strcmp(data_struct->status_code, status) != 0) { + TELEMETRY_LOG_ERR("Status code is invalid"); + fail_count++; + } + + if (data_struct->port != port) { + TELEMETRY_LOG_ERR("Port is invalid"); + fail_count++; + } + + if (strcmp(data_struct->stat_name, name) != 0) { + TELEMETRY_LOG_ERR("Stat name is invalid"); + fail_count++; + } + + if (data_struct->stat_value != value) { + TELEMETRY_LOG_ERR("Stat value is invalid"); + fail_count++; + } + + rte_telemetry_free_test_data(data_struct); + if (fail_count > 0) + return -1; + + TELEMETRY_LOG_INFO("Success - Passed valid JSON message test passed"); + + return 0; +} + +int32_t +rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd) +{ + int ret; + char buf[BUF_SIZE]; + int fail_count = 0; + const char *invalid_json = "{]"; + const char *status = "Status Error: Unknown"; + const char *data = "null"; + struct json_data *data_struct; + int buffer_read = 0; + errno = 0; + + ret = send(fd, invalid_json, strlen(invalid_json), 0); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not send message over socket"); + return -1; + } + + rte_telemetry_run(telemetry); + buffer_read = recv(fd, buf, BUF_SIZE-1, 0); + + if (buffer_read == -1) { + TELEMETRY_LOG_ERR("Read error"); + return -1; + } + + buf[buffer_read] = '\0'; + + data_struct = calloc(1, sizeof(struct json_data)); + ret = rte_telemetry_stat_parse(buf, data_struct); + + if (ret < 0) + TELEMETRY_LOG_ERR("Could not parse stats"); + + if (strcmp(data_struct->status_code, status) != 0) { + TELEMETRY_LOG_ERR("Status code is invalid"); + fail_count++; + } + + if (strcmp(data_struct->data, data) != 0) { + TELEMETRY_LOG_ERR("Data status is invalid"); + fail_count++; + } + + rte_telemetry_free_test_data(data_struct); + if (fail_count > 0) + return -1; + + TELEMETRY_LOG_INFO("Success - Passed invalid JSON message test"); + + return 0; +} + +int32_t +rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd) +{ + int ret; + char buf[BUF_SIZE]; + int fail_count = 0; + char *status = "Status Error: Invalid Argument 404"; + char *data = "null"; + struct json_data *data_struct; + const char *invalid_contents = "{\"action\":0,\"command\":" + "\"ports_stats_values_by_name\",\"data\":{\"ports\"" + ":[0],\"stats\":[\"some_invalid_param\"," + "\"another_invalid_param\"]}}"; + int buffer_read = 0; + errno = 0; + + ret = send(fd, invalid_contents, strlen(invalid_contents), 0); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not send message over socket"); + return -1; + } + + rte_telemetry_run(telemetry); + buffer_read = recv(fd, buf, BUF_SIZE-1, 0); + + if (buffer_read == -1) { + TELEMETRY_LOG_ERR("Read error"); + return -1; + } + + buf[buffer_read] = '\0'; + data_struct = calloc(1, sizeof(struct json_data)); + ret = rte_telemetry_stat_parse(buf, data_struct); + + if (ret < 0) + TELEMETRY_LOG_ERR("Could not parse stats"); + + if (strcmp(data_struct->status_code, status) != 0) { + TELEMETRY_LOG_ERR("Status code is invalid"); + fail_count++; + } + + if (strcmp(data_struct->data, data) != 0) { + TELEMETRY_LOG_ERR("Data status is invalid"); + fail_count++; + } + + rte_telemetry_free_test_data(data_struct); + if (fail_count > 0) + return -1; + + TELEMETRY_LOG_INFO("Success - Passed invalid JSON content test"); + + return 0; +} + +int32_t +rte_telemetry_json_empty_test(struct telemetry_impl *telemetry, int fd) +{ + int ret; + char buf[BUF_SIZE]; + int fail_count = 0; + const char *status = "Status Error: Invalid Argument 404"; + char *data = "null"; + struct json_data *data_struct; + const char *empty_json = "{}"; + int buffer_read = 0; + errno = 0; + + ret = (send(fd, empty_json, strlen(empty_json), 0)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not send message over socket"); + return -1; + } + + rte_telemetry_run(telemetry); + buffer_read = recv(fd, buf, BUF_SIZE-1, 0); + + if (buffer_read == -1) { + TELEMETRY_LOG_ERR("Read error"); + return -1; + } + + buf[buffer_read] = '\0'; + data_struct = calloc(1, sizeof(struct json_data)); + ret = rte_telemetry_stat_parse(buf, data_struct); + + if (ret < 0) + TELEMETRY_LOG_ERR("Could not parse stats"); + + if (strcmp(data_struct->status_code, status) != 0) { + TELEMETRY_LOG_ERR("Status code is invalid"); + fail_count++; + } + + if (strcmp(data_struct->data, data) != 0) { + TELEMETRY_LOG_ERR("Data status is invalid"); + fail_count++; + } + + rte_telemetry_free_test_data(data_struct); + + if (fail_count > 0) + return -1; + + TELEMETRY_LOG_INFO("Success - Passed JSON empty message test"); + + return 0; +} + +int32_t +rte_telemetry_json_socket_message_test(struct telemetry_impl *telemetry, int fd) +{ + uint16_t i; + int ret, fail_count; + + fail_count = 0; + struct telemetry_message_test socket_json_tests[] = { + {.test_name = "Invalid JSON test", + .test_func_ptr = rte_telemetry_invalid_json_test}, + {.test_name = "Valid JSON test", + .test_func_ptr = rte_telemetry_valid_json_test}, + {.test_name = "JSON contents test", + .test_func_ptr = rte_telemetry_json_contents_test}, + {.test_name = "JSON empty tests", + .test_func_ptr = rte_telemetry_json_empty_test} + }; + +#define NUM_TESTS RTE_DIM(socket_json_tests) + + for (i = 0; i < NUM_TESTS; i++) { + TELEMETRY_LOG_INFO("%s", socket_json_tests[i].test_name); + ret = (socket_json_tests[i].test_func_ptr) + (telemetry, fd); + if (ret < 0) { + TELEMETRY_LOG_ERR("%s failed", + socket_json_tests[i].test_name); + fail_count++; + } + } + + if (fail_count > 0) { + TELEMETRY_LOG_ERR("Failed %i JSON socket message test(s)", + fail_count); + return -1; + } + + TELEMETRY_LOG_INFO("Success - All JSON tests passed"); + + return 0; +} + int telemetry_log_level; RTE_INIT(rte_telemetry_register); diff --git a/lib/librte_telemetry/rte_telemetry.h b/lib/librte_telemetry/rte_telemetry.h index 97674ae2d..119db16fe 100644 --- a/lib/librte_telemetry/rte_telemetry.h +++ b/lib/librte_telemetry/rte_telemetry.h @@ -48,4 +48,19 @@ rte_telemetry_init(void); int32_t __rte_experimental rte_telemetry_cleanup(void); +/** + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * Runs various tests to ensure telemetry initialisation and register/unregister + * functions are working correctly. + * + * @return + * 0 on success when all tests have passed + * @return + * -1 on failure when the test has failed + */ +int32_t __rte_experimental +rte_telemetry_selftest(void); + #endif diff --git a/lib/librte_telemetry/rte_telemetry_internal.h b/lib/librte_telemetry/rte_telemetry_internal.h index 0082cb2ca..de7afda30 100644 --- a/lib/librte_telemetry/rte_telemetry_internal.h +++ b/lib/librte_telemetry/rte_telemetry_internal.h @@ -75,4 +75,7 @@ int32_t rte_telemetry_send_ports_stats_values(uint32_t *metric_ids, int num_metric_ids, uint32_t *port_ids, int num_port_ids, struct telemetry_impl *telemetry); +int32_t +rte_telemetry_socket_messaging_testing(int index, int socket); + #endif diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.c b/lib/librte_telemetry/rte_telemetry_parser_test.c new file mode 100644 index 000000000..5fe93fa6e --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry_parser_test.c @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rte_telemetry_parser.h" + +enum choices { + INV_ACTION_VAL, + INV_COMMAND_VAL, + INV_DATA_VAL, + INV_ACTION_FIELD, + INV_COMMAND_FIELD, + INV_DATA_FIELD, + INV_JSON_FORMAT, + VALID_REQ +}; + + +#define TEST_CLIENT "/var/run/dpdk/test_client" + +int32_t +rte_telemetry_create_test_socket(struct telemetry_impl *telemetry, + const char *test_client_path) +{ + int ret, sockfd; + struct sockaddr_un addr = {0}; + struct telemetry_client *client; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Telemetry argument has not been initialised"); + return -EINVAL; + } + + sockfd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (sockfd < 0) { + TELEMETRY_LOG_ERR("Test socket creation failure"); + return -1; + } + + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, test_client_path, sizeof(addr.sun_path)); + unlink(test_client_path); + + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + TELEMETRY_LOG_ERR("Test socket binding failure"); + return -1; + } + + if (listen(sockfd, 1) < 0) { + TELEMETRY_LOG_ERR("Listen failure"); + return -1; + } + + ret = rte_telemetry_register_client(telemetry, test_client_path); + if (ret < 0) { + TELEMETRY_LOG_ERR("Register dummy client failed: %i", ret); + return -1; + } + + ret = accept(sockfd, NULL, NULL); + if (ret < 0) { + TELEMETRY_LOG_ERR("Socket accept failed"); + return -1; + } + + TAILQ_FOREACH(client, &telemetry->client_list_head, client_list) + telemetry->request_client = client; + + return 0; +} + +int32_t +rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids, + const char * const *stat_names, int num_stat_names, json_t **data) +{ + + int ret; + json_t *stat_names_json_array = NULL; + json_t *port_ids_json_array = NULL; + uint32_t i; + + if (num_port_ids < 0) { + TELEMETRY_LOG_ERR("Port Ids Count invalid"); + goto fail; + } + + *data = json_object(); + if (*data == NULL) { + TELEMETRY_LOG_ERR("Data json object creation failed"); + goto fail; + } + + port_ids_json_array = json_array(); + if (port_ids_json_array == NULL) { + TELEMETRY_LOG_ERR("port_ids_json_array creation failed"); + goto fail; + } + + for (i = 0; i < (uint32_t)num_port_ids; i++) { + ret = json_array_append(port_ids_json_array, + json_integer(port_ids[i])); + if (ret < 0) { + TELEMETRY_LOG_ERR("JSON array creation failed"); + goto fail; + } + } + + ret = json_object_set_new(*data, "ports", port_ids_json_array); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting 'ports' value in data object failed"); + goto fail; + } + + if (stat_names) { + if (num_stat_names < 0) { + TELEMETRY_LOG_ERR("Stat Names Count invalid"); + goto fail; + } + + stat_names_json_array = json_array(); + if (stat_names_json_array == NULL) { + TELEMETRY_LOG_ERR("stat_names_json_array creation failed"); + goto fail; + } + + uint32_t i; + for (i = 0; i < (uint32_t)num_stat_names; i++) { + ret = json_array_append(stat_names_json_array, + json_string(stat_names[i])); + if (ret < 0) { + TELEMETRY_LOG_ERR("JSON array creation failed"); + goto fail; + } + } + + ret = json_object_set_new(*data, "stats", stat_names_json_array); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting 'stats' value in data object failed"); + goto fail; + } + } + + return 0; + +fail: + if (*data) + json_decref(*data); + if (stat_names_json_array) + json_decref(stat_names_json_array); + if (port_ids_json_array) + json_decref(port_ids_json_array); + return -1; +} + +int32_t +rte_telemetry_create_json_request(int action, char *command, + const char *client_path, int *port_ids, int num_port_ids, + const char * const *stat_names, int num_stat_names, char **request, + int inv_choice) +{ + int ret; + json_t *root = json_object(); + json_t *data; + + if (root == NULL) { + TELEMETRY_LOG_ERR("Could not create root json object"); + goto fail; + } + + if (inv_choice == INV_ACTION_FIELD) { + ret = json_object_set_new(root, "ac--on", json_integer(action)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting invalid action field in root object failed"); + goto fail; + } + } else { + ret = json_object_set_new(root, "action", json_integer(action)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting valid action field in root object failed"); + goto fail; + } + } + + if (inv_choice == INV_COMMAND_FIELD) { + ret = json_object_set_new(root, "co---nd", json_string(command)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting invalid command field in root object failed"); + goto fail; + } + } else { + ret = json_object_set_new(root, "command", json_string(command)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting valid command field in root object failed"); + goto fail; + } + } + + data = json_null(); + if (client_path) { + data = json_object(); + if (data == NULL) { + TELEMETRY_LOG_ERR("Data json object creation failed"); + goto fail; + } + + ret = json_object_set_new(data, "client_path", + json_string(client_path)); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting valid client_path field in data object failed"); + goto fail; + } + + } else if (port_ids) { + ret = rte_telemetry_format_port_stat_ids(port_ids, num_port_ids, + stat_names, num_stat_names, &data); + if (ret < 0) { + TELEMETRY_LOG_ERR("Formatting Port/Stat arrays failed"); + goto fail; + } + + } + + if (inv_choice == INV_DATA_FIELD) { + ret = json_object_set_new(root, "d--a", data); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting invalid data field in data object failed"); + goto fail; + } + } else { + ret = json_object_set_new(root, "data", data); + if (ret < 0) { + TELEMETRY_LOG_ERR("Setting valid data field in data object failed"); + goto fail; + } + } + + *request = json_dumps(root, 0); + if (*request == NULL) { + TELEMETRY_LOG_ERR("Converting JSON root object to char* failed"); + goto fail; + } + + json_decref(root); + return 0; + +fail: + if (root) + json_decref(root); + return -1; +} + +int32_t +rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry, + int action_choice, char *command_choice, int inv_choice) +{ + int ret; + char *request; + char *client_path_data = NULL; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Telemetry argument has not been initialised"); + return -EINVAL; + } + + + if (inv_choice == INV_ACTION_VAL) + action_choice = -1; + else if (inv_choice == INV_COMMAND_VAL) + command_choice = "INVALID_COMMAND"; + else if (inv_choice == INV_DATA_VAL) + client_path_data = "INVALID_DATA"; + + ret = rte_telemetry_create_json_request(action_choice, command_choice, + client_path_data, NULL, -1, NULL, -1, &request, inv_choice); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not create JSON Request"); + return -1; + } + + if (inv_choice == INV_JSON_FORMAT) + request++; + + ret = rte_telemetry_parse(telemetry, request); + if (ret < 0) { + TELEMETRY_LOG_WARN("Could not parse JSON Request"); + return -1; + } + + return 0; +} + +int32_t +rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry, + int action_choice, int *port_ids, int num_port_ids, int inv_choice) +{ + int ret; + char *request; + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Telemetry argument has not been initialised"); + return -EINVAL; + } + + char *command = "ports_details"; + + if (inv_choice == INV_ACTION_VAL) + action_choice = -1; + else if (inv_choice == INV_COMMAND_VAL) + command = "INVALID_COMMAND"; + else if (inv_choice == INV_DATA_VAL) + port_ids = NULL; + + + ret = rte_telemetry_create_json_request(action_choice, command, NULL, + port_ids, num_port_ids, NULL, -1, &request, inv_choice); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not create JSON Request"); + return -1; + } + + if (inv_choice == INV_JSON_FORMAT) + request++; + + ret = rte_telemetry_parse(telemetry, request); + if (ret < 0) { + TELEMETRY_LOG_WARN("Could not parse JSON Request"); + return -1; + } + + return 0; +} + +int32_t +rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl + *telemetry, int action_choice, int *port_ids, int num_port_ids, + const char * const *stat_names, int num_stat_names, + int inv_choice) +{ + int ret; + char *request; + char *command = "ports_stats_values_by_name"; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Telemetry argument has not been initialised"); + return -EINVAL; + } + + if (inv_choice == INV_ACTION_VAL) + action_choice = -1; + else if (inv_choice == INV_COMMAND_VAL) + command = "INVALID_COMMAND"; + else if (inv_choice == INV_DATA_VAL) { + port_ids = NULL; + stat_names = NULL; + } + + ret = rte_telemetry_create_json_request(action_choice, command, NULL, + port_ids, num_port_ids, stat_names, num_stat_names, &request, + inv_choice); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not create JSON Request"); + return -1; + } + + if (inv_choice == INV_JSON_FORMAT) + request++; + + ret = rte_telemetry_parse(telemetry, request); + if (ret < 0) { + TELEMETRY_LOG_WARN("Could not parse JSON Request"); + return -1; + } + + return 0; +} + +int32_t +rte_telemetry_send_unreg_request(struct telemetry_impl *telemetry, + int action_choice, const char *client_path, int inv_choice) +{ + int ret; + char *request; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Telemetry argument has not been initialised"); + return -EINVAL; + } + + char *command = "clients"; + + if (inv_choice == INV_ACTION_VAL) + action_choice = -1; + else if (inv_choice == INV_COMMAND_VAL) + command = "INVALID_COMMAND"; + else if (inv_choice == INV_DATA_VAL) + client_path = NULL; + + ret = rte_telemetry_create_json_request(action_choice, command, + client_path, NULL, -1, NULL, -1, &request, inv_choice); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not create JSON Request"); + return -1; + } + + if (inv_choice == INV_JSON_FORMAT) + request++; + + ret = rte_telemetry_parse(telemetry, request); + if (ret < 0) { + TELEMETRY_LOG_WARN("Could not parse JSON Request"); + return -1; + } + + return 0; +} + +int32_t +rte_telemetry_parser_test(struct telemetry_impl *telemetry) +{ + int ret; + const char *client_path = TEST_CLIENT; + + if (telemetry == NULL) { + TELEMETRY_LOG_ERR("Telemetry argument has not been initialised"); + return -EINVAL; + } + + ret = rte_telemetry_create_test_socket(telemetry, client_path); + if (ret < 0) { + TELEMETRY_LOG_ERR("Could not create test request client socket"); + return -1; + } + + int port_ids[] = {0, 1}; + int num_port_ids = RTE_DIM(port_ids); + + static const char * const stat_names[] = {"tx_good_packets", + "rx_good_packets"}; + int num_stat_names = RTE_DIM(stat_names); + + static const char * const test_types[] = { + "INVALID ACTION VALUE TESTS", + "INVALID COMMAND VALUE TESTS", + "INVALID DATA VALUE TESTS", + "INVALID ACTION FIELD TESTS", + "INVALID COMMAND FIELD TESTS", + "INVALID DATA FIELD TESTS", + "INVALID JSON FORMAT TESTS", + "VALID TESTS" + }; + + +#define NUM_TEST_TYPES (sizeof(test_types)/sizeof(const char * const)) + + uint32_t i; + for (i = 0; i < NUM_TEST_TYPES; i++) { + TELEMETRY_LOG_INFO("%s", test_types[i]); + + ret = rte_telemetry_send_get_ports_and_stats_request(telemetry, + ACTION_GET, "ports", i); + if (ret != 0 && i == VALID_REQ) { + TELEMETRY_LOG_ERR("Get ports valid test failed"); + return -EPERM; + } else if (ret != -1 && i != VALID_REQ) { + TELEMETRY_LOG_ERR("Get ports invalid test failed"); + return -EPERM; + } + + TELEMETRY_LOG_INFO("Success - Get ports test passed"); + + ret = rte_telemetry_send_get_ports_details_request(telemetry, + ACTION_GET, port_ids, num_port_ids, i); + if (ret != 0 && i == VALID_REQ) { + TELEMETRY_LOG_ERR("Get ports details valid"); + return -EPERM; + } else if (ret != -1 && i != VALID_REQ) { + TELEMETRY_LOG_ERR("Get ports details invalid"); + return -EPERM; + } + + TELEMETRY_LOG_INFO("Success - Get ports details test passed"); + + ret = rte_telemetry_send_get_ports_and_stats_request(telemetry, + ACTION_GET, "port_stats", i); + if (ret != 0 && i == VALID_REQ) { + TELEMETRY_LOG_ERR("Get port stats valid test"); + return -EPERM; + } else if (ret != -1 && i != VALID_REQ) { + TELEMETRY_LOG_ERR("Get ports stats invalid test failed"); + return -EPERM; + } + + TELEMETRY_LOG_INFO("Success - Get ports stats test passed"); + + ret = rte_telemetry_send_stats_values_by_name_request(telemetry, + ACTION_GET, port_ids, num_port_ids, stat_names, + num_stat_names, i); + if (ret != 0 && i == VALID_REQ) { + TELEMETRY_LOG_ERR("Get ports stats values by name valid test failed"); + return -EPERM; + } else if (ret != -1 && i != VALID_REQ) { + TELEMETRY_LOG_ERR("Get ports stats values by name invalid test failed"); + return -EPERM; + } + + TELEMETRY_LOG_INFO("Success - Get ports stats values by name test passed"); + + ret = rte_telemetry_send_unreg_request(telemetry, ACTION_DELETE, + client_path, i); + if (ret != 0 && i == VALID_REQ) { + TELEMETRY_LOG_ERR("Deregister valid test failed"); + return -EPERM; + } else if (ret != -1 && i != VALID_REQ) { + TELEMETRY_LOG_ERR("Deregister invalid test failed"); + return -EPERM; + } + + TELEMETRY_LOG_INFO("Success - Deregister test passed"); + } + + return 0; +} diff --git a/lib/librte_telemetry/rte_telemetry_parser_test.h b/lib/librte_telemetry/rte_telemetry_parser_test.h new file mode 100644 index 000000000..6ada85276 --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry_parser_test.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#ifndef _RTE_TELEMETRY_PARSER_TEST_H_ +#define _RTE_TELEMETRY_PARSER_TEST_H_ + +int32_t +rte_telemetry_parser_test(struct telemetry_impl *telemetry); + +int32_t +rte_telemetry_format_port_stat_ids(int *port_ids, int num_port_ids, + const char * const stat_names, int num_stat_names, json_t **data); + +int32_t +rte_telemetry_create_json_request(int action, char *command, + const char *client_path, int *port_ids, int num_port_ids, + const char * const stat_names, int num_stat_names, char **request, + int inv_choice); + +int32_t +rte_telemetry_send_get_ports_and_stats_request(struct telemetry_impl *telemetry, + int action_choice, char *command_choice, int inv_choice); + +int32_t +rte_telemetry_send_get_ports_details_request(struct telemetry_impl *telemetry, + int action_choice, int *port_ids, int num_port_ids, int inv_choice); + +int32_t +rte_telemetry_send_stats_values_by_name_request(struct telemetry_impl + *telemetry, int action_choice, int *port_ids, int num_port_ids, + const char * const stat_names, int num_stat_names, + int inv_choice); + +int32_t +rte_telemetry_send_unreg_request(int action_choice, const char *client_path, + int inv_choice); + +#endif diff --git a/lib/librte_telemetry/rte_telemetry_socket_tests.h b/lib/librte_telemetry/rte_telemetry_socket_tests.h new file mode 100644 index 000000000..db9167c5d --- /dev/null +++ b/lib/librte_telemetry/rte_telemetry_socket_tests.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018 Intel Corporation + */ + +#include + +#include "rte_telemetry_internal.h" + +#ifndef _RTE_TELEMETRY_SOCKET_TESTING_H_ +#define _RTE_TELEMETRY_SOCKET_TESTING_H_ + +int32_t +rte_telemetry_json_socket_message_test(struct telemetry_impl *telemetry, + int fd); + +int32_t +rte_telemetry_invalid_json_test(struct telemetry_impl *telemetry, int fd); + +int32_t +rte_telemetry_valid_json_test(struct telemetry_impl *telemetry, int fd); + +int32_t +rte_telemetry_json_contents_test(struct telemetry_impl *telemetry, int fd); + +int32_t +rte_telemetry_json_empty_test(struct telemetry_impl *telemetry, int fd); + +int32_t +rte_telemetry_socket_register_test(struct telemetry_impl *telemetry, int *fd, + int send_fd, int recv_fd); + +int32_t +rte_telemetry_socket_test_setup(struct telemetry_impl *telemetry, int *send_fd, + int *recv_fd); + +#endif diff --git a/lib/librte_telemetry/rte_telemetry_version.map b/lib/librte_telemetry/rte_telemetry_version.map index fb0b5be62..fa62d7718 100644 --- a/lib/librte_telemetry/rte_telemetry_version.map +++ b/lib/librte_telemetry/rte_telemetry_version.map @@ -4,6 +4,7 @@ EXPERIMENTAL { rte_telemetry_cleanup; rte_telemetry_init; rte_telemetry_parse; + rte_telemetry_selftest; local: *; }; From patchwork Sat Oct 27 09:17:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47518 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 5FB345B2C; Sat, 27 Oct 2018 11:20:28 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id B21C75592 for ; Sat, 27 Oct 2018 11:19:46 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:46 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903911" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:44 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz Date: Sat, 27 Oct 2018 10:17:47 +0100 Message-Id: <20181027091750.17254-10-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 09/12] telemetry: add ability to disable selftest X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch adds functionality to enable/disable the selftest. This functionality will be extended in future to make the enabling/disabling more dynamic and remove this 'hardcoded' approach. We are temporarily using this approach due to the design changes (vdev vs eal) made to the library. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren --- lib/librte_telemetry/rte_telemetry.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/librte_telemetry/rte_telemetry.c b/lib/librte_telemetry/rte_telemetry.c index 5b66b71c4..2e27491b5 100644 --- a/lib/librte_telemetry/rte_telemetry.c +++ b/lib/librte_telemetry/rte_telemetry.c @@ -660,6 +660,7 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry) { uint16_t pid; int ret; + int selftest = 0; RTE_ETH_FOREACH_DEV(pid) { telemetry->reg_index = rte_telemetry_reg_ethdev_to_metrics(pid); @@ -672,18 +673,20 @@ rte_telemetry_initial_accept(struct telemetry_impl *telemetry) } telemetry->metrics_register_done = 1; - ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index, - telemetry->server_fd); - if (ret < 0) - return -1; + if (selftest) { + ret = rte_telemetry_socket_messaging_testing(telemetry->reg_index, + telemetry->server_fd); + if (ret < 0) + return -1; - ret = rte_telemetry_parser_test(telemetry); - if (ret < 0) { - TELEMETRY_LOG_ERR("Parser Tests Failed"); - return -1; - } + ret = rte_telemetry_parser_test(telemetry); + if (ret < 0) { + TELEMETRY_LOG_ERR("Parser Tests Failed"); + return -1; + } - TELEMETRY_LOG_INFO("Success - All Parser Tests Passed"); + TELEMETRY_LOG_INFO("Success - All Parser Tests Passed"); + } return 0; } From patchwork Sat Oct 27 09:17:48 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47519 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id AADD65F13; Sat, 27 Oct 2018 11:20:30 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 3E62B5592 for ; Sat, 27 Oct 2018 11:19:49 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:48 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903916" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:46 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz Date: Sat, 27 Oct 2018 10:17:48 +0100 Message-Id: <20181027091750.17254-11-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH v10 10/12] doc: add telemetry documentation X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch adds all documentation for telemetry. A description on how to use the Telemetry API with a DPDK application is given in this document. It also adds the MAINTAINERS file entry and a release notes update for telemetry. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren Acked-by: Marko Kovacevic --- v10: - Add MAINTAINERS entry for documentation (Thomas) --- MAINTAINERS | 1 + doc/guides/howto/index.rst | 1 + doc/guides/howto/telemetry.rst | 85 ++++++++++++++++++++++++++ doc/guides/rel_notes/release_18_11.rst | 6 ++ 4 files changed, 93 insertions(+) create mode 100644 doc/guides/howto/telemetry.rst diff --git a/MAINTAINERS b/MAINTAINERS index a50214d7a..86712b139 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1211,6 +1211,7 @@ F: doc/guides/prog_guide/bpf_lib.rst Telemetry - EXPERIMENTAL M: Kevin Laatz F: lib/librte_telemetry/ +F: doc/guides/howto/telemetry.rst Test Applications ----------------- diff --git a/doc/guides/howto/index.rst b/doc/guides/howto/index.rst index e13a090c3..a642a2be1 100644 --- a/doc/guides/howto/index.rst +++ b/doc/guides/howto/index.rst @@ -17,3 +17,4 @@ HowTo Guides virtio_user_for_container_networking virtio_user_as_exceptional_path packet_capture_framework + telemetry diff --git a/doc/guides/howto/telemetry.rst b/doc/guides/howto/telemetry.rst new file mode 100644 index 000000000..3fcb0619e --- /dev/null +++ b/doc/guides/howto/telemetry.rst @@ -0,0 +1,85 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2018 Intel Corporation. + +DPDK Telemetry API User Guide +============================== + +This document describes how the Data Plane Development Kit(DPDK) Telemetry API +is used for querying port statistics from incoming traffic. + +Introduction +------------ + +The ``librte_telemetry`` provides the functionality so that users may query +metrics from incoming port traffic. The application which initializes packet +forwarding will act as the server, sending metrics to the requesting application +which acts as the client. + +In DPDK, applications are used to initialize the ``telemetry``. To view incoming +traffic on featured ports, the application should be run first (ie. after ports +are configured). Once the application is running, the service assurance agent +(for example the collectd plugin) should be run to begin querying the API. + +A client connects their Service Assurance application to the DPDK application +via a UNIX socket. Once a connection is established, a client can send JSON +messages to the DPDK application requesting metrics via another UNIX client. +This request is then handled and parsed if valid. The response is then +formatted in JSON and sent back to the requesting client. + +Pre-requisites +~~~~~~~~~~~~~~ + +* Python ≥ 2.5 + +* Jansson library for JSON serialization + +Test Environment +---------------- + +``telemetry`` offers a range of selftests that a client can run within +the DPDK application. + +Selftests are disabled by default. They can be enabled by setting the 'selftest' +variable to 1 in rte_telemetry_initial_accept(). + +Note: this 'hardcoded' value is temporary. + +Configuration +------------- + +Enable the telemetry API by modifying the following config option before +building DPDK:: + + CONFIG_RTE_LIBRTE_TELEMETRY=y + +Note: Meson will pick this up automatically if ``libjansson`` is available. + +Running the Application +----------------------- + +The following steps demonstrate how to run the ``telemetry`` API to query all +statistics on all active ports, using the ``telemetry_client`` python script +to query. +Note: This guide assumes packet generation is applicable and the user is +testing with testpmd as a DPDK primary application to forward packets, although +any DPDK application is applicable. + +#. Launch testpmd as the primary application with ``telemetry``.:: + + ./app/testpmd --telemetry + +#. Launch the ``telemetry`` python script with a client filepath.:: + + python usertools/telemetry_client.py /var/run/some_client + + The client filepath is going to be used to setup our UNIX connection with the + DPDK primary application, in this case ``testpmd`` + This will initialize a menu where a client can proceed to recursively query + statistics, request statistics once or unregister the file_path, thus exiting + the menu. + +#. Send traffic to any or all available ports from a traffic generator. + Select a query option(recursive or singular polling). + The metrics will then be displayed on the client terminal in JSON format. + +#. Once finished, unregister the client using the menu command. diff --git a/doc/guides/rel_notes/release_18_11.rst b/doc/guides/rel_notes/release_18_11.rst index c6256939b..ed7994241 100644 --- a/doc/guides/rel_notes/release_18_11.rst +++ b/doc/guides/rel_notes/release_18_11.rst @@ -256,6 +256,12 @@ New Features the specified port. The port must be stopped before the command call in order to reconfigure queues. +* **Added Telemetry API.** + + Added the telemetry API which allows applications to transparently expose + their telemetry via a UNIX socket in JSON. The JSON can be consumed by any + Service Assurance agent, such as CollectD. + * **Add a new sample for vDPA** The vdpa sample application creates vhost-user sockets by using the From patchwork Sat Oct 27 09:17:49 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47520 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id E58A95F1C; Sat, 27 Oct 2018 11:20:33 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id BCCBB58CB for ; Sat, 27 Oct 2018 11:19:51 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:51 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903919" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:49 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Ciara Power , Brian Archbold , Kevin Laatz Date: Sat, 27 Oct 2018 10:17:49 +0100 Message-Id: <20181027091750.17254-12-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 11/12] usertools: add client python script for telemetry X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Ciara Power This patch adds a python script which can be used as a demo client. The script is interactive and will allow the user to register, request statistics, and unregister. To run the script, an argument for the client file path must be passed in: "python telemetry_client.py ". This script is useful to see how the Telemetry API for DPDK is used, and how to make the initial connection. Signed-off-by: Ciara Power Signed-off-by: Brian Archbold Signed-off-by: Kevin Laatz Acked-by: Harry van Haaren --- v10: - Add MAINTAINER entry for client script (Thomas) --- MAINTAINERS | 1 + usertools/dpdk-telemetry-client.py | 116 +++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 usertools/dpdk-telemetry-client.py diff --git a/MAINTAINERS b/MAINTAINERS index 86712b139..a919ffd45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1212,6 +1212,7 @@ Telemetry - EXPERIMENTAL M: Kevin Laatz F: lib/librte_telemetry/ F: doc/guides/howto/telemetry.rst +F: usertools/dpdk-telemetry-client.py Test Applications ----------------- diff --git a/usertools/dpdk-telemetry-client.py b/usertools/dpdk-telemetry-client.py new file mode 100644 index 000000000..6dcf62bac --- /dev/null +++ b/usertools/dpdk-telemetry-client.py @@ -0,0 +1,116 @@ +# SPDK-License-Identifier: BSD-3-Clause +# Copyright(c) 2018 Intel Corporation + +import socket +import os +import sys +import time + +BUFFER_SIZE = 200000 + +METRICS_REQ = "{\"action\":0,\"command\":\"ports_all_stat_values\",\"data\":null}" +API_REG = "{\"action\":1,\"command\":\"clients\",\"data\":{\"client_path\":\"" +API_UNREG = "{\"action\":2,\"command\":\"clients\",\"data\":{\"client_path\":\"" +DEFAULT_FP = "/var/run/dpdk/default_client" + +class Socket: + + def __init__(self): + self.send_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.recv_fd = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + self.client_fd = None + + def __del__(self): + try: + self.send_fd.close() + self.recv_fd.close() + self.client_fd.close() + except: + print("Error - Sockets could not be closed") + +class Client: + + def __init__(self): # Creates a client instance + self.socket = Socket() + self.file_path = None + self.choice = None + self.unregistered = 0 + + def __del__(self): + try: + if self.unregistered == 0: + self.unregister(); + except: + print("Error - Client could not be destroyed") + + def getFilepath(self, file_path): # Gets arguments from Command-Line and assigns to instance of client + self.file_path = file_path + + def register(self): # Connects a client to DPDK-instance + if os.path.exists(self.file_path): + os.unlink(self.file_path) + try: + self.socket.recv_fd.bind(self.file_path) + except socket.error as msg: + print ("Error - Socket binding error: " + str(msg) + "\n") + self.socket.recv_fd.settimeout(2) + self.socket.send_fd.connect("/var/run/dpdk/rte/telemetry") + JSON = (API_REG + self.file_path + "\"}}") + self.socket.send_fd.sendall(JSON) + self.socket.recv_fd.listen(1) + self.socket.client_fd = self.socket.recv_fd.accept()[0] + + def unregister(self): # Unregister a given client + self.socket.client_fd.send(API_UNREG + self.file_path + "\"}}") + self.socket.client_fd.close() + + def requestMetrics(self): # Requests metrics for given client + self.socket.client_fd.send(METRICS_REQ) + data = self.socket.client_fd.recv(BUFFER_SIZE) + print "\nResponse: \n", str(data) + + def repeatedlyRequestMetrics(self, sleep_time): # Recursively requests metrics for given client + print("\nPlease enter the number of times you'd like to continuously request Metrics:") + n_requests = int(input("\n:")) + print("\033[F") #Removes the user input from screen, cleans it up + print("\033[K") + for i in range(n_requests): + self.requestMetrics() + time.sleep(sleep_time) + + def interactiveMenu(self, sleep_time): # Creates Interactive menu within the script + while self.choice != 3: + print("\nOptions Menu") + print("[1] Send for Metrics for all ports") + print("[2] Send for Metrics for all ports recursively") + print("[3] Unregister client") + + try: + self.choice = int(input("\n:")) + print("\033[F") #Removes the user input for screen, cleans it up + print("\033[K") + if self.choice == 1: + self.requestMetrics() + elif self.choice == 2: + self.repeatedlyRequestMetrics(sleep_time) + elif self.choice == 3: + self.unregister() + self.unregistered = 1 + else: + print("Error - Invalid request choice") + except: + pass + +if __name__ == "__main__": + + sleep_time = 1 + file_path = "" + if (len(sys.argv) == 2): + file_path = sys.argv[1] + else: + print("Warning - No filepath passed, using default (" + DEFAULT_FP + ").") + file_path = DEFAULT_FP + client = Client() + client.getFilepath(file_path) + client.register() + client.interactiveMenu(sleep_time) From patchwork Sat Oct 27 09:17:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Van Haaren, Harry" X-Patchwork-Id: 47521 Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 44AC35F24; Sat, 27 Oct 2018 11:20:36 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by dpdk.org (Postfix) with ESMTP id 31D0358FE for ; Sat, 27 Oct 2018 11:19:54 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2018 02:19:53 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,431,1534834800"; d="scan'208";a="84903924" Received: from silpixa00399779.ir.intel.com (HELO silpixa00399779.ger.corp.intel.com) ([10.237.223.188]) by orsmga008.jf.intel.com with ESMTP; 27 Oct 2018 02:19:51 -0700 From: Harry van Haaren To: dev@dpdk.org Cc: thomas@monjalon.net, bruce.richardson@intel.com, stephen@networkplumber.org, gaetan.rivet@6wind.com, shreyansh.jain@nxp.com, mattias.ronnblom@ericsson.com, Kevin Laatz , Radu Nicolau Date: Sat, 27 Oct 2018 10:17:50 +0100 Message-Id: <20181027091750.17254-13-harry.van.haaren@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181027091750.17254-1-harry.van.haaren@intel.com> References: <20181026235933.79779-1-harry.van.haaren@intel.com> <20181027091750.17254-1-harry.van.haaren@intel.com> Subject: [dpdk-dev] [PATCH v10 12/12] build: add dependency on telemetry to apps in meson X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Kevin Laatz This patch adds telemetry as a dependecy to all applications. Without these changes, the --telemetry flag will not be recognised and applications will fail to run if they want to enable telemetry. Signed-off-by: Bruce Richardson Signed-off-by: Kevin Laatz Signed-off-by: Radu Nicolau Acked-by: Harry van Haaren --- app/meson.build | 4 ++-- app/pdump/meson.build | 2 +- app/proc-info/meson.build | 2 +- app/test-bbdev/meson.build | 2 +- app/test-crypto-perf/meson.build | 2 +- app/test-pmd/meson.build | 2 +- config/meson.build | 3 +++ lib/librte_telemetry/meson.build | 1 + lib/meson.build | 1 + meson.build | 2 ++ 10 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/meson.build b/app/meson.build index e68d949e9..a9a026bbf 100644 --- a/app/meson.build +++ b/app/meson.build @@ -29,7 +29,7 @@ foreach app:apps # use "deps" for internal DPDK dependencies, and "ext_deps" for # external package/library requirements ext_deps = [] - deps = [] + deps = dpdk_app_link_libraries subdir(name) @@ -43,7 +43,7 @@ foreach app:apps link_libs = [] if get_option('default_library') == 'static' - link_libs = dpdk_drivers + link_libs = dpdk_static_libraries + dpdk_drivers endif if allow_experimental_apis diff --git a/app/pdump/meson.build b/app/pdump/meson.build index 988cb4eb2..116c27f02 100644 --- a/app/pdump/meson.build +++ b/app/pdump/meson.build @@ -3,4 +3,4 @@ sources = files('main.c') allow_experimental_apis = true -deps = ['ethdev', 'kvargs', 'pdump'] +deps += ['ethdev', 'kvargs', 'pdump'] diff --git a/app/proc-info/meson.build b/app/proc-info/meson.build index 9c148e36e..a52b2ee4a 100644 --- a/app/proc-info/meson.build +++ b/app/proc-info/meson.build @@ -3,4 +3,4 @@ sources = files('main.c') allow_experimental_apis = true -deps = ['ethdev', 'metrics'] +deps += ['ethdev', 'metrics'] diff --git a/app/test-bbdev/meson.build b/app/test-bbdev/meson.build index 653907ded..eb8cc0499 100644 --- a/app/test-bbdev/meson.build +++ b/app/test-bbdev/meson.build @@ -6,4 +6,4 @@ sources = files('main.c', 'test_bbdev_perf.c', 'test_bbdev_vector.c') allow_experimental_apis = true -deps = ['bbdev', 'bus_vdev'] +deps += ['bbdev', 'bus_vdev'] diff --git a/app/test-crypto-perf/meson.build b/app/test-crypto-perf/meson.build index eacd7a0f5..d735b186f 100644 --- a/app/test-crypto-perf/meson.build +++ b/app/test-crypto-perf/meson.build @@ -12,4 +12,4 @@ sources = files('cperf_ops.c', 'cperf_test_vectors.c', 'cperf_test_verify.c', 'main.c') -deps = ['cryptodev'] +deps += ['cryptodev'] diff --git a/app/test-pmd/meson.build b/app/test-pmd/meson.build index cd66618e9..6006c60f9 100644 --- a/app/test-pmd/meson.build +++ b/app/test-pmd/meson.build @@ -24,7 +24,7 @@ sources = files('cmdline.c', 'txonly.c', 'util.c') -deps = ['ethdev', 'gro', 'gso', 'cmdline', 'metrics', 'meter', 'bus_pci'] +deps += ['ethdev', 'gro', 'gso', 'cmdline', 'metrics', 'meter', 'bus_pci'] if dpdk_conf.has('RTE_LIBRTE_PDUMP') deps += 'pdump' endif diff --git a/config/meson.build b/config/meson.build index 6f9228c87..275f00b60 100644 --- a/config/meson.build +++ b/config/meson.build @@ -21,6 +21,9 @@ toolchain = cc.get_id() dpdk_conf.set_quoted('RTE_TOOLCHAIN', toolchain) dpdk_conf.set('RTE_TOOLCHAIN_' + toolchain.to_upper(), 1) +add_project_link_arguments('-Wl,--no-as-needed', language: 'c') +dpdk_extra_ldflags += '-Wl,--no-as-needed' + # use pthreads add_project_link_arguments('-pthread', language: 'c') dpdk_extra_ldflags += '-pthread' diff --git a/lib/librte_telemetry/meson.build b/lib/librte_telemetry/meson.build index b3bbf5bd0..9492f544e 100644 --- a/lib/librte_telemetry/meson.build +++ b/lib/librte_telemetry/meson.build @@ -9,6 +9,7 @@ cflags += '-DALLOW_EXPERIMENTAL_API' jansson = cc.find_library('jansson', required: false) if jansson.found() ext_deps += jansson + dpdk_app_link_libraries += ['telemetry'] else build = false endif diff --git a/lib/meson.build b/lib/meson.build index 9d1f353d2..c0cc2d86e 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -132,6 +132,7 @@ foreach l:libraries dependencies: shared_deps) dpdk_libraries = [shared_lib] + dpdk_libraries + dpdk_static_libraries = [static_lib] + dpdk_static_libraries endif # sources.length() > 0 set_variable('shared_' + libname, shared_dep) diff --git a/meson.build b/meson.build index c9af33532..b1e6eab6a 100644 --- a/meson.build +++ b/meson.build @@ -12,8 +12,10 @@ project('DPDK', 'C', cc = meson.get_compiler('c') dpdk_conf = configuration_data() dpdk_libraries = [] +dpdk_static_libraries = [] dpdk_drivers = [] dpdk_extra_ldflags = [] +dpdk_app_link_libraries = [] driver_install_path = join_paths(get_option('libdir'), 'dpdk/drivers') eal_pmd_path = join_paths(get_option('prefix'), driver_install_path)