From: Ruben Kerkhof Date: Fri, 24 Feb 2017 16:19:21 +0000 (+0100) Subject: Merge pull request #2157 from maryamtahhan/dpdkevents_upstream X-Git-Tag: collectd-5.8.0~224 X-Git-Url: https://git.octo.it/?p=collectd.git;a=commitdiff_plain;h=9fdcbb61f627ea6badaaaec9196c3fb983f7ba93;hp=030932537269922a2e8bfbbdc2f28668274b55f3 Merge pull request #2157 from maryamtahhan/dpdkevents_upstream Plugin for getting DPDK ports link status and keep alive events. --- diff --git a/Makefile.am b/Makefile.am index 805733e6..efc16f7c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -766,6 +766,14 @@ dns_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBPCAP_LDFLAGS) dns_la_LIBADD = $(BUILD_WITH_LIBPCAP_LIBS) endif +if BUILD_PLUGIN_DPDKEVENTS +pkglib_LTLIBRARIES += dpdkevents.la +dpdkevents_la_SOURCES = src/dpdkevents.c src/utils_dpdk.c src/utils_dpdk.h +dpdkevents_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBDPDK_CPPFLAGS) +dpdkevents_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(LIBDPDK_LDFLAGS) +dpdkevents_la_LIBADD = -ldpdk +endif + if BUILD_PLUGIN_DPDKSTAT pkglib_LTLIBRARIES += dpdkstat.la dpdkstat_la_SOURCES = src/dpdkstat.c src/utils_dpdk.c src/utils_dpdk.h diff --git a/configure.ac b/configure.ac index 572bedec..0154d829 100644 --- a/configure.ac +++ b/configure.ac @@ -2700,30 +2700,46 @@ AC_ARG_VAR([LIBDPDK_LDFLAGS], [Linker flags for libdpdk]) AC_ARG_WITH([libdpdk], [AS_HELP_STRING([--without-libdpdk], [Disable libdpdk.])]) -if test "x$with_libdpdk" != "xno" -then - if test "x$LIBDPDK_CPPFLAGS" = "x" - then - LIBDPDK_CPPFLAGS="-I/usr/include/dpdk" - fi - SAVE_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$LIBDPDK_CPPFLAGS $CPPFLAGS" - AC_CHECK_HEADERS([rte_config.h], - [with_libdpdk="yes"], - [with_libdpdk="no (rte_config.h not found)"] - ) - CPPFLAGS="$SAVE_CPPFLAGS" +if test "x$with_libdpdk" != "xno"; then + if test "x$LIBDPDK_CPPFLAGS" = "x"; then + LIBDPDK_CPPFLAGS="-I/usr/include/dpdk" + fi + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$LIBDPDK_CPPFLAGS $CPPFLAGS" + AC_CHECK_HEADERS([rte_config.h], + [ + with_libdpdk="yes" + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM( + [[ + #include + #if RTE_VERSION < RTE_VERSION_NUM(16,7,0,0) + #error "required DPDK >= 16.07" + #endif + ]], + [[ + return 0; + ]] + ) + ], + [dpdk_keepalive="yes"], + [dpdk_keepalive="no (DPDK version < 16.07)"] + ) + ], + [with_libdpdk="no (rte_config.h not found)"] + ) + CPPFLAGS="$SAVE_CPPFLAGS" fi -if test "x$with_libdpdk" = "xyes" -then - SAVE_LDFLAGS="$LDFLAGS" - LDFLAGS="$LIBDPDK_LDFLAGS $LDFLAGS" - AC_CHECK_LIB([dpdk], [rte_eal_init], - [with_libdpdk="yes"], - [with_libdpdk="no (symbol 'rte_eal_init' not found)"] - ) - LDFLAGS="$SAVE_LDFLAGS" +if test "x$with_libdpdk" = "xyes"; then + SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="$LIBDPDK_LDFLAGS $LDFLAGS" + AC_CHECK_LIB([dpdk], [rte_eal_init], + [with_libdpdk="yes"], + [with_libdpdk="no (symbol 'rte_eal_init' not found)"] + ) + LDFLAGS="$SAVE_LDFLAGS" fi # }}} @@ -5886,6 +5902,7 @@ plugin_curl_xml="no" plugin_df="no" plugin_disk="no" plugin_drbd="no" +plugin_dpdkevents="no" plugin_dpdkstat="no" plugin_entropy="no" plugin_ethstat="no" @@ -6236,6 +6253,7 @@ fi if test "x$with_libdpdk" = "xyes" then + plugin_dpdkevents="$dpdk_keepalive" plugin_dpdkstat="yes" fi @@ -6284,6 +6302,7 @@ AC_PLUGIN([dbi], [$with_libdbi], [General database st AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics]) AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics]) AC_PLUGIN([dns], [$with_libpcap], [DNS traffic analysis]) +AC_PLUGIN([dpdkevents], [$plugin_dpdkevents], [Events from DPDK]) AC_PLUGIN([dpdkstat], [$plugin_dpdkstat], [Stats from DPDK]) AC_PLUGIN([drbd], [$plugin_drbd], [DRBD statistics]) AC_PLUGIN([email], [yes], [EMail statistics]) @@ -6709,6 +6728,7 @@ AC_MSG_RESULT([ dbi . . . . . . . . . $enable_dbi]) AC_MSG_RESULT([ df . . . . . . . . . $enable_df]) AC_MSG_RESULT([ disk . . . . . . . . $enable_disk]) AC_MSG_RESULT([ dns . . . . . . . . . $enable_dns]) +AC_MSG_RESULT([ dpdkevents. . . . . . $enable_dpdkevents]) AC_MSG_RESULT([ dpdkstat . . . . . . $enable_dpdkstat]) AC_MSG_RESULT([ drbd . . . . . . . . $enable_drbd]) AC_MSG_RESULT([ email . . . . . . . . $enable_email]) diff --git a/src/collectd.conf.in b/src/collectd.conf.in index e5567793..dc7d6eb6 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -114,6 +114,7 @@ #@BUILD_PLUGIN_DF_TRUE@LoadPlugin df #@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk #@BUILD_PLUGIN_DNS_TRUE@LoadPlugin dns +#@BUILD_PLUGIN_DPDKEVENTS_TRUE@LoadPlugin dpdkevents #@BUILD_PLUGIN_DPDKSTAT_TRUE@LoadPlugin dpdkstat #@BUILD_PLUGIN_DRBD_TRUE@LoadPlugin drbd #@BUILD_PLUGIN_EMAIL_TRUE@LoadPlugin email @@ -525,6 +526,28 @@ # SelectNumericQueryTypes true # +# +# +# Coremask "0x1" +# MemoryChannels "4" +# ProcessType "secondary" +# FilePrefix "rte" +# +# +# SendEventsOnUpdate true +# EnabledPortMask 0xffff +# PortName "interface1" +# PortName "interface2" +# SendNotification false +# +# +# SendEventsOnUpdate true +# LCoreMask "0xf" +# KeepAliveShmName "/dpdk_keepalive_shm_name" +# SendNotification false +# +# + # # # Coremask "0x2" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 730696a3..6d927d26 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -2383,6 +2383,128 @@ Enabled by default, collects unknown (and thus presented as numeric only) query =back +=head2 Plugin C + +The I collects events from DPDK such as link status of +network ports and Keep Alive status of DPDK logical cores. +In order to get Keep Alive events following requirements must be met: +- DPDK >= 16.07 +- support for Keep Alive implemented in DPDK application. More details can +be found here: http://dpdk.org/doc/guides/sample_app_ug/keep_alive.html + +B + + + + Coremask "0x1" + MemoryChannels "4" + ProcessType "secondary" + FilePrefix "rte" + + + SendEventsOnUpdate true + EnabledPortMask 0xffff + PortName "interface1" + PortName "interface2" + SendNotification false + + + SendEventsOnUpdate true + LCoreMask "0xf" + KeepAliveShmName "/dpdk_keepalive_shm_name" + SendNotification false + + + +B + + +=head3 The EAL block + +=over 5 + +=item B I + +=item B I + +Number of memory channels per processor socket. + +=item B I + +The type of DPDK process instance. + +=item B I + +The prefix text used for hugepage filenames. The filename will be set to +/var/run/._config where prefix is what is passed in by the user. + +=back + +=head3 The Event block + +The B block defines configuration for specific event. It accepts a +single argument which specifies the name of the event. + +=head4 Link Status event + +=over 5 + +=item B I + +If set to true link status value will be dispatched only when it is +different from previously read value. This is an optional argument - default +value is true. + +=item B I + +A hexidecimal bit mask of the DPDK ports which should be enabled. A mask +of 0x0 means that all ports will be disabled. A bitmask of all F's means +that all ports will be enabled. This is an optional argument - by default +all ports are enabled. + +=item B I + +A string containing an optional name for the enabled DPDK ports. Each PortName +option should contain only one port name; specify as many PortName options as +desired. Default naming convention will be used if PortName is blank. If there +are less PortName options than there are enabled ports, the default naming +convention will be used for the additional ports. + +=item B I + +If set to true, link status notifications are sent, instead of link status +being collected as a statistic. This is an optional argument - default +value is false. + +=back + +=head4 Keep Alive event + +=over 5 + +=item B I + +If set to true keep alive value will be dispatched only when it is +different from previously read value. This is an optional argument - default +value is true. + +=item B I + +An hexadecimal bit mask of the logical cores to monitor keep alive state. + +=item B I + +Shared memory name identifier that is used by secondary process to monitor +the keep alive cores state. + +=item B I + +If set to true, keep alive notifications are sent, instead of keep alive +information being collected as a statistic. This is an optional +argument - default value is false. + +=back + =head2 Plugin C The I collects information about DPDK interfaces using the diff --git a/src/dpdkevents.c b/src/dpdkevents.c new file mode 100644 index 00000000..6be6bc04 --- /dev/null +++ b/src/dpdkevents.c @@ -0,0 +1,584 @@ +/* + * collectd - src/dpdkevents.c + * MIT License + * + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Maryam Tahhan + * Harry van Haaren + * Serhiy Pshyk + * Kim-Marie Jones + * Krzysztof Matczak + */ + +#include "collectd.h" + +#include "common.h" +#include "plugin.h" + +#include "semaphore.h" +#include "sys/mman.h" +#include "utils_dpdk.h" +#include "utils_time.h" + +#include +#include +#include +#include + +#define DPDK_EVENTS_PLUGIN "dpdkevents" +#define DPDK_EVENTS_NAME "dpdk_collectd_events" +#define ETH_LINK_NA 0xFF + +#define INT64_BIT_SIZE 64 +#define KEEPALIVE_PLUGIN_INSTANCE "keepalive" +#define RTE_KEEPALIVE_SHM_NAME "/dpdk_keepalive_shm_name" + +typedef struct dpdk_keepalive_shm_s { + sem_t core_died; + enum rte_keepalive_state core_state[RTE_KEEPALIVE_MAXCORES]; + uint64_t core_last_seen_times[RTE_KEEPALIVE_MAXCORES]; +} dpdk_keepalive_shm_t; + +typedef struct dpdk_ka_monitor_s { + cdtime_t read_time; + int lcore_state; +} dpdk_ka_monitor_t; + +typedef struct dpdk_link_status_config_s { + int enabled; + int send_updated; + uint32_t enabled_port_mask; + char port_name[RTE_MAX_ETHPORTS][DATA_MAX_NAME_LEN]; + int notify; +} dpdk_link_status_config_t; + +typedef struct dpdk_keep_alive_config_s { + int enabled; + int send_updated; + uint128_t lcore_mask; + dpdk_keepalive_shm_t *shm; + char shm_name[DATA_MAX_NAME_LEN]; + int notify; +} dpdk_keep_alive_config_t; + +typedef struct dpdk_events_config_s { + cdtime_t interval; + dpdk_link_status_config_t link_status; + dpdk_keep_alive_config_t keep_alive; +} dpdk_events_config_t; + +typedef struct dpdk_link_info_s { + cdtime_t read_time; + int status_updated; + int link_status; +} dpdk_link_info_t; + +typedef struct dpdk_events_ctx_s { + dpdk_events_config_t config; + uint32_t nb_ports; + dpdk_link_info_t link_info[RTE_MAX_ETHPORTS]; + dpdk_ka_monitor_t core_info[RTE_KEEPALIVE_MAXCORES]; +} dpdk_events_ctx_t; + +#define DPDK_EVENTS_CTX_GET(a) ((dpdk_events_ctx_t *)dpdk_helper_priv_get(a)) + +#define DPDK_EVENTS_TRACE() \ + DEBUG("%s:%s:%d pid=%u", DPDK_EVENTS_PLUGIN, __FUNCTION__, __LINE__, getpid()) + +static dpdk_helper_ctx_t *g_hc; + +static int dpdk_event_keep_alive_shm_create(void) { + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(g_hc); + char *shm_name; + + if (strlen(ec->config.keep_alive.shm_name)) { + shm_name = ec->config.keep_alive.shm_name; + } else { + shm_name = RTE_KEEPALIVE_SHM_NAME; + WARNING(DPDK_EVENTS_PLUGIN ": Keep alive shared memory identifier is not " + "specified, using default one: %s", + shm_name); + } + + char errbuf[ERR_BUF_SIZE]; + int fd = shm_open(shm_name, O_RDWR, 0); + if (fd < 0) { + ERROR(DPDK_EVENTS_PLUGIN ": Failed to open %s as SHM:%s. Is DPDK KA " + "primary application running?", + shm_name, sstrerror(errno, errbuf, sizeof(errbuf))); + return errno; + } else { + ec->config.keep_alive.shm = + (dpdk_keepalive_shm_t *)mmap(0, sizeof(*(ec->config.keep_alive.shm)), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + close(fd); + if (ec->config.keep_alive.shm == MAP_FAILED) { + ERROR(DPDK_EVENTS_PLUGIN ": Failed to mmap KA SHM:%s", + sstrerror(errno, errbuf, sizeof(errbuf))); + return errno; + } + } + + return 0; +} + +static void dpdk_events_default_config(void) { + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(g_hc); + + ec->config.interval = plugin_get_interval(); + + /* Link Status */ + ec->config.link_status.enabled = 0; + ec->config.link_status.enabled_port_mask = ~0; + ec->config.link_status.send_updated = 1; + ec->config.link_status.notify = 0; + + for (int i = 0; i < RTE_MAX_ETHPORTS; i++) { + ec->config.link_status.port_name[i][0] = 0; + } + + /* Keep Alive */ + ec->config.keep_alive.enabled = 0; + ec->config.keep_alive.send_updated = 1; + ec->config.keep_alive.notify = 0; + memset(&ec->config.keep_alive.lcore_mask, 0, + sizeof(ec->config.keep_alive.lcore_mask)); + memset(&ec->config.keep_alive.shm_name, 0, + sizeof(ec->config.keep_alive.shm_name)); +} + +static int dpdk_events_preinit(void) { + DPDK_EVENTS_TRACE(); + + if (g_hc != NULL) { + /* already initialized if config callback was called before init callback */ + DEBUG("dpdk_events_preinit: helper already initialized."); + return 0; + } + + int ret = + dpdk_helper_init(DPDK_EVENTS_NAME, sizeof(dpdk_events_ctx_t), &g_hc); + if (ret != 0) { + ERROR(DPDK_EVENTS_PLUGIN ": failed to initialize %s helper(error: %s)", + DPDK_EVENTS_NAME, strerror(ret)); + return ret; + } + + dpdk_events_default_config(); + + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(g_hc); + for (int i = 0; i < RTE_MAX_ETHPORTS; i++) { + ec->link_info[i].link_status = ETH_LINK_NA; + } + + for (int i = 0; i < RTE_KEEPALIVE_MAXCORES; i++) { + ec->core_info[i].lcore_state = ETH_LINK_NA; + } + + return ret; +} + +static int dpdk_events_link_status_config(dpdk_events_ctx_t *ec, + oconfig_item_t *ci) { + ec->config.link_status.enabled = 1; + + DEBUG(DPDK_EVENTS_PLUGIN ": Subscribed for Link Status Events."); + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("EnabledPortMask", child->key) == 0) { + ec->config.link_status.enabled_port_mask = + (uint32_t)child->values[0].value.number; + DEBUG(DPDK_EVENTS_PLUGIN ": LinkStatus:Enabled Port Mask 0x%X", + ec->config.link_status.enabled_port_mask); + } else if (strcasecmp("SendEventsOnUpdate", child->key) == 0) { + ec->config.link_status.send_updated = child->values[0].value.boolean; + DEBUG(DPDK_EVENTS_PLUGIN ": LinkStatus:SendEventsOnUpdate %d", + (int)child->values[0].value.boolean); + } else if (strcasecmp("SendNotification", child->key) == 0) { + ec->config.link_status.notify = child->values[0].value.boolean; + DEBUG(DPDK_EVENTS_PLUGIN ": LinkStatus:SendNotification %d", + (int)child->values[0].value.boolean); + } + } + + int port_num = 0; + + /* parse port names after EnabledPortMask was parsed */ + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + if (strcasecmp("PortName", child->key) == 0) { + while (!(ec->config.link_status.enabled_port_mask & (1 << port_num))) + port_num++; + ssnprintf(ec->config.link_status.port_name[port_num], DATA_MAX_NAME_LEN, + "%s", child->values[0].value.string); + DEBUG(DPDK_EVENTS_PLUGIN ": LinkStatus:Port %d Name: %s", port_num, + ec->config.link_status.port_name[port_num]); + port_num++; + } + } + + return 0; +} + +static int dpdk_events_keep_alive_config(dpdk_events_ctx_t *ec, + oconfig_item_t *ci) { + ec->config.keep_alive.enabled = 1; + DEBUG(DPDK_EVENTS_PLUGIN ": Subscribed for Keep Alive Events."); + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp("SendEventsOnUpdate", child->key) == 0) { + ec->config.keep_alive.send_updated = child->values[0].value.boolean; + DEBUG(DPDK_EVENTS_PLUGIN ": KeepAlive:SendEventsOnUpdate %d", + (int)child->values[0].value.boolean); + } else if (strcasecmp("LCoreMask", child->key) == 0) { + char lcore_mask[DATA_MAX_NAME_LEN]; + ssnprintf(lcore_mask, sizeof(lcore_mask), "%s", + child->values[0].value.string); + ec->config.keep_alive.lcore_mask = + str_to_uint128(lcore_mask, strlen(lcore_mask)); + DEBUG(DPDK_EVENTS_PLUGIN ": KeepAlive:LCoreMask 0x%" PRIX64 "%" PRIX64 "", + ec->config.keep_alive.lcore_mask.high, + ec->config.keep_alive.lcore_mask.low); + } else if (strcasecmp("KeepAliveShmName", child->key) == 0) { + ssnprintf(ec->config.keep_alive.shm_name, + sizeof(ec->config.keep_alive.shm_name), "%s", + child->values[0].value.string); + DEBUG(DPDK_EVENTS_PLUGIN ": KeepAlive:KeepAliveShmName %s", + ec->config.keep_alive.shm_name); + } else if (strcasecmp("SendNotification", child->key) == 0) { + ec->config.keep_alive.notify = child->values[0].value.boolean; + DEBUG(DPDK_EVENTS_PLUGIN ": KeepAlive:SendNotification %d", + (int)child->values[0].value.boolean); + } + } + + return 0; +} + +static int dpdk_events_config(oconfig_item_t *ci) { + DPDK_EVENTS_TRACE(); + + int ret = dpdk_events_preinit(); + if (ret) + return ret; + + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(g_hc); + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + if (strcasecmp("EAL", child->key) == 0) { + dpdk_helper_eal_config_parse(g_hc, child); + } else if (strcasecmp("Event", child->key) == 0) { + if (strcasecmp(child->values[0].value.string, "link_status") == 0) { + dpdk_events_link_status_config(ec, child); + } else if (strcasecmp(child->values[0].value.string, "keep_alive") == 0) { + dpdk_events_keep_alive_config(ec, child); + } else { + ERROR(DPDK_EVENTS_PLUGIN ": The selected event \"%s\" is unknown.", + child->values[0].value.string); + } + } + } + + if (!ec->config.keep_alive.enabled && !ec->config.link_status.enabled) { + ERROR(DPDK_EVENTS_PLUGIN ": At least one type of events should be " + "configured for collecting. Plugin misconfigured"); + return -1; + } + + return ret; +} + +static int dpdk_helper_link_status_get(dpdk_helper_ctx_t *phc) { + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(phc); + + /* get Link Status values from DPDK */ + uint8_t nb_ports = rte_eth_dev_count(); + if (nb_ports == 0) { + DPDK_CHILD_LOG("dpdkevent-helper: No DPDK ports available. " + "Check bound devices to DPDK driver.\n"); + return -ENODEV; + } + ec->nb_ports = nb_ports > RTE_MAX_ETHPORTS ? RTE_MAX_ETHPORTS : nb_ports; + + for (int i = 0; i < ec->nb_ports; i++) { + if (ec->config.link_status.enabled_port_mask & (1 << i)) { + struct rte_eth_link link; + ec->link_info[i].read_time = cdtime(); + rte_eth_link_get_nowait(i, &link); + if ((link.link_status == ETH_LINK_NA) || + (link.link_status != ec->link_info[i].link_status)) { + ec->link_info[i].link_status = link.link_status; + ec->link_info[i].status_updated = 1; + DPDK_CHILD_LOG(" === PORT %d Link Status: %s\n", i, + link.link_status ? "UP" : "DOWN"); + } + } + } + + return 0; +} + +/* this function is called from helper context */ +int dpdk_helper_command_handler(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd) { + if (phc == NULL) { + DPDK_CHILD_LOG(DPDK_EVENTS_PLUGIN ": Invalid argument(phc)\n"); + return -EINVAL; + } + + if (cmd != DPDK_CMD_GET_EVENTS) { + DPDK_CHILD_LOG(DPDK_EVENTS_PLUGIN ": Unknown command (cmd=%d)\n", cmd); + return -EINVAL; + } + + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(phc); + int ret = 0; + if (ec->config.link_status.enabled) + ret = dpdk_helper_link_status_get(phc); + + return ret; +} + +static void dpdk_events_notification_dispatch(int severity, + const char *plugin_instance, + cdtime_t time, const char *msg) { + notification_t n = { + .severity = severity, .time = time, .plugin = DPDK_EVENTS_PLUGIN}; + sstrncpy(n.host, hostname_g, sizeof(n.host)); + sstrncpy(n.plugin_instance, plugin_instance, sizeof(n.plugin_instance)); + sstrncpy(n.message, msg, sizeof(n.message)); + plugin_dispatch_notification(&n); +} + +static void dpdk_events_gauge_submit(const char *plugin_instance, + const char *type_instance, gauge_t value, + cdtime_t time) { + value_list_t vl = {.values = &(value_t){.gauge = value}, + .values_len = 1, + .time = time, + .plugin = DPDK_EVENTS_PLUGIN, + .type = "gauge", + .meta = NULL}; + sstrncpy(vl.host, hostname_g, sizeof(vl.host)); + sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + plugin_dispatch_values(&vl); +} + +static int dpdk_events_link_status_dispatch(dpdk_helper_ctx_t *phc) { + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(phc); + DEBUG(DPDK_EVENTS_PLUGIN ": %s:%d ports=%u", __FUNCTION__, __LINE__, + ec->nb_ports); + + /* dispatch Link Status values to collectd */ + for (int i = 0; i < ec->nb_ports; i++) { + if (ec->config.link_status.enabled_port_mask & (1 << i)) { + if (!ec->config.link_status.send_updated || + ec->link_info[i].status_updated) { + + DEBUG(DPDK_EVENTS_PLUGIN ": Dispatch PORT %d Link Status: %s", i, + ec->link_info[i].link_status ? "UP" : "DOWN"); + + char dev_name[DATA_MAX_NAME_LEN]; + if (ec->config.link_status.port_name[i][0] != 0) { + ssnprintf(dev_name, sizeof(dev_name), "%s", + ec->config.link_status.port_name[i]); + } else { + ssnprintf(dev_name, sizeof(dev_name), "port.%d", i); + } + + if (ec->config.link_status.notify) { + int sev = ec->link_info[i].link_status ? NOTIF_OKAY : NOTIF_WARNING; + char msg[DATA_MAX_NAME_LEN]; + ssnprintf(msg, sizeof(msg), "Link Status: %s", + ec->link_info[i].link_status ? "UP" : "DOWN"); + dpdk_events_notification_dispatch(sev, dev_name, + ec->link_info[i].read_time, msg); + } else { + dpdk_events_gauge_submit(dev_name, "link_status", + (gauge_t)ec->link_info[i].link_status, + ec->link_info[i].read_time); + } + ec->link_info[i].status_updated = 0; + } + } + } + + return 0; +} + +static void dpdk_events_keep_alive_dispatch(dpdk_helper_ctx_t *phc) { + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(phc); + + /* dispatch Keep Alive values to collectd */ + for (int i = 0; i < RTE_KEEPALIVE_MAXCORES; i++) { + if (i < INT64_BIT_SIZE) { + if (!(ec->config.keep_alive.lcore_mask.low & ((uint64_t)1 << i))) + continue; + } else if (i >= INT64_BIT_SIZE && i < INT64_BIT_SIZE * 2) { + if (!(ec->config.keep_alive.lcore_mask.high & + ((uint64_t)1 << (i - INT64_BIT_SIZE)))) + continue; + } else { + WARNING(DPDK_EVENTS_PLUGIN + ": %s:%d Core id %u is out of 0 to %u range, skipping", + __FUNCTION__, __LINE__, i, INT64_BIT_SIZE * 2); + continue; + } + + char core_name[DATA_MAX_NAME_LEN]; + ssnprintf(core_name, sizeof(core_name), "lcore%u", i); + + if (!ec->config.keep_alive.send_updated || + (ec->core_info[i].lcore_state != + ec->config.keep_alive.shm->core_state[i])) { + ec->core_info[i].lcore_state = ec->config.keep_alive.shm->core_state[i]; + ec->core_info[i].read_time = cdtime(); + + if (ec->config.keep_alive.notify) { + char msg[DATA_MAX_NAME_LEN]; + int sev; + + switch (ec->config.keep_alive.shm->core_state[i]) { + case RTE_KA_STATE_ALIVE: + sev = NOTIF_OKAY; + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: ALIVE", i); + break; + case RTE_KA_STATE_MISSING: + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: MISSING", i); + sev = NOTIF_WARNING; + break; + case RTE_KA_STATE_DEAD: + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: DEAD", i); + sev = NOTIF_FAILURE; + break; + case RTE_KA_STATE_UNUSED: + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: UNUSED", i); + sev = NOTIF_OKAY; + break; + case RTE_KA_STATE_GONE: + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: GONE", i); + sev = NOTIF_FAILURE; + break; + case RTE_KA_STATE_DOZING: + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: DOZING", i); + sev = NOTIF_OKAY; + break; + case RTE_KA_STATE_SLEEP: + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: SLEEP", i); + sev = NOTIF_OKAY; + break; + default: + ssnprintf(msg, sizeof(msg), "lcore %u Keep Alive Status: UNKNOWN", i); + sev = NOTIF_FAILURE; + } + + dpdk_events_notification_dispatch(sev, KEEPALIVE_PLUGIN_INSTANCE, + ec->core_info[i].read_time, msg); + } else { + dpdk_events_gauge_submit(KEEPALIVE_PLUGIN_INSTANCE, core_name, + ec->config.keep_alive.shm->core_state[i], + ec->core_info[i].read_time); + } + } + } +} + +static int dpdk_events_read(user_data_t *ud) { + DPDK_EVENTS_TRACE(); + + if (g_hc == NULL) { + ERROR(DPDK_EVENTS_PLUGIN ": plugin not initialized."); + return -1; + } + + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(g_hc); + + if (ec->config.link_status.enabled) { + int cmd_res = 0; + int ret = dpdk_helper_command(g_hc, DPDK_CMD_GET_EVENTS, &cmd_res, + ec->config.interval); + if (cmd_res == 0 && ret == 0) { + dpdk_events_link_status_dispatch(g_hc); + } + } + + if (ec->config.keep_alive.enabled) { + dpdk_events_keep_alive_dispatch(g_hc); + } + + return 0; +} + +static int dpdk_events_init(void) { + DPDK_EVENTS_TRACE(); + + int ret = dpdk_events_preinit(); + if (ret) + return ret; + + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(g_hc); + + if (ec->config.keep_alive.enabled) { + ret = dpdk_event_keep_alive_shm_create(); + if (ret) { + ERROR(DPDK_EVENTS_PLUGIN ": %s : error %d in ka_shm_create()", + __FUNCTION__, ret); + return ret; + } + } + return 0; +} + +static int dpdk_events_shutdown(void) { + DPDK_EVENTS_TRACE(); + int ret; + + dpdk_events_ctx_t *ec = DPDK_EVENTS_CTX_GET(g_hc); + if (ec->config.keep_alive.enabled) { + ret = munmap(ec->config.keep_alive.shm, sizeof(dpdk_keepalive_shm_t)); + if (ret) { + ERROR(DPDK_EVENTS_PLUGIN ": munmap KA monitor returned %d", ret); + return ret; + } + } + + ret = dpdk_helper_shutdown(g_hc); + g_hc = NULL; + if (ret) + ERROR(DPDK_EVENTS_PLUGIN ": failed to cleanup %s helper", DPDK_EVENTS_NAME); + + return ret; +} + +void module_register(void) { + plugin_register_init(DPDK_EVENTS_PLUGIN, dpdk_events_init); + plugin_register_complex_config(DPDK_EVENTS_PLUGIN, dpdk_events_config); + plugin_register_complex_read(NULL, DPDK_EVENTS_PLUGIN, dpdk_events_read, 0, + NULL); + plugin_register_shutdown(DPDK_EVENTS_PLUGIN, dpdk_events_shutdown); +}