X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Futils_dpdk.c;h=b6056466a0ad8336bdd8035911ff5209c1cf424f;hb=5af4545d64498b66b1ce6d57b163dde8a5dacda9;hp=f9636ac574d8c04c136a5d9ee7ad3ae8df211ee7;hpb=1e8a95944f39cf4d552f2afed8e26b93febd50ec;p=collectd.git diff --git a/src/utils_dpdk.c b/src/utils_dpdk.c index f9636ac5..b6056466 100644 --- a/src/utils_dpdk.c +++ b/src/utils_dpdk.c @@ -1,15 +1,15 @@ -/** +/* * collectd - src/utils_dpdk.c * MIT License * * Copyright(c) 2016 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: + * 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. @@ -18,15 +18,17 @@ * 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. + * 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 + * Taras Chornyi * Serhiy Pshyk - **/ + * Krzysztof Matczak + */ #include "collectd.h" @@ -36,13 +38,17 @@ #include #include +#include #include "common.h" #include "utils_dpdk.h" #define DPDK_DEFAULT_RTE_CONFIG "/var/run/.rte_config" -#define DPDK_EAL_ARGC 5 -#define DPDK_MAX_BUFFER_SIZE (4096 * 4) +#define DPDK_EAL_ARGC 10 +// Complete trace should fit into 1024 chars. Trace contain some headers +// and text together with traced data from pipe. This is the reason why +// we need to limit DPDK_MAX_BUFFER_SIZE value. +#define DPDK_MAX_BUFFER_SIZE 896 #define DPDK_CDM_DEFAULT_TIMEOUT 10000 enum DPDK_HELPER_STATUS { @@ -55,9 +61,7 @@ enum DPDK_HELPER_STATUS { }; #define DPDK_HELPER_TRACE(_name) \ - DEBUG("%s:%s:%d pid=%lu", _name, __FUNCTION__, __LINE__, (long)getpid()) - -#define DPDK_HELPER_USE_PIPES + DEBUG("%s:%s:%d pid=%ld", _name, __FUNCTION__, __LINE__, (long)getpid()) struct dpdk_helper_ctx_s { @@ -65,16 +69,14 @@ struct dpdk_helper_ctx_s { int eal_initialized; size_t shm_size; - const char *shm_name; + char shm_name[DATA_MAX_NAME_LEN]; sem_t sema_cmd_start; sem_t sema_cmd_complete; cdtime_t cmd_wait_time; pid_t pid; -#ifdef DPDK_HELPER_USE_PIPES int pipes[2]; -#endif /* DPDK_HELPER_USE_PIPES */ int status; int cmd; @@ -84,7 +86,7 @@ struct dpdk_helper_ctx_s { }; static int dpdk_shm_init(const char *name, size_t size, void **map); -static int dpdk_shm_cleanup(const char *name, size_t size, void *map); +static void dpdk_shm_cleanup(const char *name, size_t size, void *map); static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc); static int dpdk_helper_worker(dpdk_helper_ctx_t *phc); @@ -104,11 +106,10 @@ static void dpdk_helper_config_default(dpdk_helper_ctx_t *phc) { DPDK_HELPER_TRACE(phc->shm_name); - ssnprintf(phc->eal_config.coremask, DATA_MAX_NAME_LEN, "%s", "0xf"); - ssnprintf(phc->eal_config.memory_channels, DATA_MAX_NAME_LEN, "%s", "1"); - ssnprintf(phc->eal_config.process_type, DATA_MAX_NAME_LEN, "%s", "secondary"); - ssnprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, "%s", - DPDK_DEFAULT_RTE_CONFIG); + snprintf(phc->eal_config.coremask, DATA_MAX_NAME_LEN, "%s", "0xf"); + snprintf(phc->eal_config.memory_channels, DATA_MAX_NAME_LEN, "%s", "1"); + snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, "%s", + DPDK_DEFAULT_RTE_CONFIG); } int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) { @@ -124,7 +125,7 @@ int dpdk_helper_eal_config_set(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) { return -EINVAL; } - memcpy(&phc->eal_config, ec, sizeof(dpdk_eal_config_t)); + memcpy(&phc->eal_config, ec, sizeof(phc->eal_config)); return 0; } @@ -142,7 +143,7 @@ int dpdk_helper_eal_config_get(dpdk_helper_ctx_t *phc, dpdk_eal_config_t *ec) { return -EINVAL; } - memcpy(ec, &phc->eal_config, sizeof(dpdk_eal_config_t)); + memcpy(ec, &phc->eal_config, sizeof(*ec)); return 0; } @@ -160,34 +161,55 @@ int dpdk_helper_eal_config_parse(dpdk_helper_ctx_t *phc, oconfig_item_t *ci) { return -EINVAL; } + int status = 0; for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; + if (strcasecmp("Coremask", child->key) == 0) { - cf_util_get_string_buffer(child, phc->eal_config.coremask, - sizeof(phc->eal_config.coremask)); + status = cf_util_get_string_buffer(child, phc->eal_config.coremask, + sizeof(phc->eal_config.coremask)); DEBUG("dpdk_common: EAL:Coremask %s", phc->eal_config.coremask); } else if (strcasecmp("MemoryChannels", child->key) == 0) { - cf_util_get_string_buffer(child, phc->eal_config.memory_channels, - sizeof(phc->eal_config.memory_channels)); + status = + cf_util_get_string_buffer(child, phc->eal_config.memory_channels, + sizeof(phc->eal_config.memory_channels)); DEBUG("dpdk_common: EAL:Memory Channels %s", phc->eal_config.memory_channels); } else if (strcasecmp("SocketMemory", child->key) == 0) { - cf_util_get_string_buffer(child, phc->eal_config.socket_memory, - sizeof(phc->eal_config.socket_memory)); + status = cf_util_get_string_buffer(child, phc->eal_config.socket_memory, + sizeof(phc->eal_config.socket_memory)); DEBUG("dpdk_common: EAL:Socket memory %s", phc->eal_config.socket_memory); - } else if (strcasecmp("ProcessType", child->key) == 0) { - cf_util_get_string_buffer(child, phc->eal_config.process_type, - sizeof(phc->eal_config.process_type)); - DEBUG("dpdk_common: EAL:Process type %s", phc->eal_config.process_type); - } else if ((strcasecmp("FilePrefix", child->key) == 0) && - (child->values[0].type == OCONFIG_TYPE_STRING)) { - ssnprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, - "/var/run/.%s_config", child->values[0].value.string); - DEBUG("dpdk_common: EAL:File prefix %s", phc->eal_config.file_prefix); + } else if (strcasecmp("FilePrefix", child->key) == 0) { + char prefix[DATA_MAX_NAME_LEN]; + + status = cf_util_get_string_buffer(child, prefix, sizeof(prefix)); + if (status == 0) { + snprintf(phc->eal_config.file_prefix, DATA_MAX_NAME_LEN, + "/var/run/.%s_config", prefix); + DEBUG("dpdk_common: EAL:File prefix %s", phc->eal_config.file_prefix); + } + } else if (strcasecmp("LogLevel", child->key) == 0) { + status = cf_util_get_string_buffer(child, phc->eal_config.log_level, + sizeof(phc->eal_config.log_level)); + DEBUG("dpdk_common: EAL:LogLevel %s", phc->eal_config.log_level); + } else if (strcasecmp("RteDriverLibPath", child->key) == 0) { + status = cf_util_get_string_buffer( + child, phc->eal_config.rte_driver_lib_path, + sizeof(phc->eal_config.rte_driver_lib_path)); + DEBUG("dpdk_common: EAL:RteDriverLibPath %s", + phc->eal_config.rte_driver_lib_path); + } else { + ERROR("dpdk_common: Invalid '%s' configuration option", child->key); + status = -EINVAL; + } + + if (status != 0) { + ERROR("dpdk_common: Parsing EAL configuration failed"); + break; } } - return 0; + return status; } static int dpdk_shm_init(const char *name, size_t size, void **map) { @@ -197,60 +219,57 @@ static int dpdk_shm_init(const char *name, size_t size, void **map) { int fd = shm_open(name, O_CREAT | O_TRUNC | O_RDWR, 0666); if (fd < 0) { - WARNING("dpdk_shm_init: Failed to open %s as SHM:%s\n", name, + WARNING("dpdk_shm_init: Failed to open %s as SHM:%s", name, sstrerror(errno, errbuf, sizeof(errbuf))); - goto fail; + *map = NULL; + return -1; } int ret = ftruncate(fd, size); if (ret != 0) { - WARNING("dpdk_shm_init: Failed to resize SHM:%s\n", + WARNING("dpdk_shm_init: Failed to resize SHM:%s", sstrerror(errno, errbuf, sizeof(errbuf))); - goto fail_close; + close(fd); + *map = NULL; + dpdk_shm_cleanup(name, size, NULL); + return -1; } *map = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (*map == MAP_FAILED) { - WARNING("dpdk_shm_init:Failed to mmap SHM:%s\n", + WARNING("dpdk_shm_init:Failed to mmap SHM:%s", sstrerror(errno, errbuf, sizeof(errbuf))); - goto fail_close; + close(fd); + *map = NULL; + dpdk_shm_cleanup(name, size, NULL); + return -1; } - /* - * Close the file descriptor, the shared memory object still exists - * and can only be removed by calling shm_unlink(). - */ + /* File descriptor no longer needed for shared memory operations */ close(fd); - memset(*map, 0, size); return 0; - -fail_close: - close(fd); -fail: - *map = NULL; - return -1; } -static int dpdk_shm_cleanup(const char *name, size_t size, void *map) { +static void dpdk_shm_cleanup(const char *name, size_t size, void *map) { DPDK_HELPER_TRACE(name); + char errbuf[ERR_BUF_SIZE]; - int ret = munmap(map, size); - if (ret) { - ERROR("munmap returned %d\n", ret); - } + /* + * Call shm_unlink first, as 'name' might be no longer accessible after munmap + */ + if (shm_unlink(name)) + ERROR("shm_unlink failure %s", sstrerror(errno, errbuf, sizeof(errbuf))); - ret = shm_unlink(name); - if (ret) { - ERROR("shm_unlink returned %d\n", ret); + if (map != NULL) { + if (munmap(map, size)) + ERROR("munmap failure %s", sstrerror(errno, errbuf, sizeof(errbuf))); } - - return 0; } -inline void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc) { +void *dpdk_helper_priv_get(dpdk_helper_ctx_t *phc) { if (phc) - return (void *)phc->priv_data; + return phc->priv_data; return NULL; } @@ -261,12 +280,11 @@ int dpdk_helper_data_size_get(dpdk_helper_ctx_t *phc) { return -EINVAL; } - return (phc->shm_size - sizeof(dpdk_helper_ctx_t)); + return phc->shm_size - sizeof(dpdk_helper_ctx_t); } int dpdk_helper_init(const char *name, size_t data_size, dpdk_helper_ctx_t **pphc) { - int err = 0; dpdk_helper_ctx_t *phc = NULL; size_t shm_size = sizeof(dpdk_helper_ctx_t) + data_size; char errbuf[ERR_BUF_SIZE]; @@ -286,30 +304,32 @@ int dpdk_helper_init(const char *name, size_t data_size, /* Allocate dpdk_helper_ctx_t and * initialize a POSIX SHared Memory (SHM) object. */ - err = dpdk_shm_init(name, shm_size, (void **)&phc); + int err = dpdk_shm_init(name, shm_size, (void **)&phc); if (err != 0) { return -errno; } err = sem_init(&phc->sema_cmd_start, 1, 0); if (err != 0) { - ERROR("sema_cmd_start semaphore init failed: %s\n", + ERROR("sema_cmd_start semaphore init failed: %s", sstrerror(errno, errbuf, sizeof(errbuf))); + int errno_m = errno; dpdk_shm_cleanup(name, shm_size, (void *)phc); - return -errno; + return -errno_m; } err = sem_init(&phc->sema_cmd_complete, 1, 0); if (err != 0) { - ERROR("sema_cmd_complete semaphore init failed: %s\n", + ERROR("sema_cmd_complete semaphore init failed: %s", sstrerror(errno, errbuf, sizeof(errbuf))); sem_destroy(&phc->sema_cmd_start); + int errno_m = errno; dpdk_shm_cleanup(name, shm_size, (void *)phc); - return -errno; + return -errno_m; } phc->shm_size = shm_size; - phc->shm_name = name; + sstrncpy(phc->shm_name, name, sizeof(phc->shm_name)); dpdk_helper_config_default(phc); @@ -318,17 +338,13 @@ int dpdk_helper_init(const char *name, size_t data_size, return 0; } -int dpdk_helper_shutdown(dpdk_helper_ctx_t *phc) { - if (phc == NULL) { - ERROR("%s:Invalid argument(phc)", __FUNCTION__); - return -EINVAL; - } +void dpdk_helper_shutdown(dpdk_helper_ctx_t *phc) { + if (phc == NULL) + return; DPDK_HELPER_TRACE(phc->shm_name); -#ifdef DPDK_HELPER_USE_PIPES close(phc->pipes[1]); -#endif if (phc->status != DPDK_HELPER_NOT_INITIALIZED) { dpdk_helper_exit_command(phc, DPDK_HELPER_GRACEFUL_QUIT); @@ -337,8 +353,6 @@ int dpdk_helper_shutdown(dpdk_helper_ctx_t *phc) { sem_destroy(&phc->sema_cmd_start); sem_destroy(&phc->sema_cmd_complete); dpdk_shm_cleanup(phc->shm_name, phc->shm_size, (void *)phc); - - return 0; } static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { @@ -353,7 +367,6 @@ static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { phc->eal_initialized = 0; phc->cmd_wait_time = MS_TO_CDTIME_T(DPDK_CDM_DEFAULT_TIMEOUT); -#ifdef DPDK_HELPER_USE_PIPES /* * Create a pipe for helper stdout back to collectd. This is necessary for * logging EAL failures, as rte_eal_init() calls rte_panic(). @@ -366,7 +379,7 @@ static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { } if (pipe(phc->pipes) != 0) { - DEBUG("dpdk_helper_spawn: Could not create helper pipe: %s\n", + DEBUG("dpdk_helper_spawn: Could not create helper pipe: %s", sstrerror(errno, errbuf, sizeof(errbuf))); return -1; } @@ -383,28 +396,23 @@ static int dpdk_helper_spawn(dpdk_helper_ctx_t *phc) { WARNING("dpdk_helper_spawn: error setting up pipes: %s", sstrerror(errno, errbuf, sizeof(errbuf))); } -#endif /* DPDK_HELPER_USE_PIPES */ pid_t pid = fork(); if (pid > 0) { phc->pid = pid; -#ifdef DPDK_HELPER_USE_PIPES close(phc->pipes[1]); -#endif /* DPDK_HELPER_USE_PIPES */ DEBUG("%s:dpdk_helper_spawn: helper pid %lu", phc->shm_name, (long)phc->pid); } else if (pid == 0) { -#ifdef DPDK_HELPER_USE_PIPES /* Replace stdout with a pipe to collectd. */ close(phc->pipes[0]); close(STDOUT_FILENO); dup2(phc->pipes[1], STDOUT_FILENO); -#endif /* DPDK_HELPER_USE_PIPES */ DPDK_CHILD_TRACE(phc->shm_name); dpdk_helper_worker(phc); exit(0); } else { - ERROR("dpdk_helper_start: Failed to fork helper process: %s\n", + ERROR("dpdk_helper_start: Failed to fork helper process: %s", sstrerror(errno, errbuf, sizeof(errbuf))); return -1; } @@ -417,9 +425,7 @@ static int dpdk_helper_exit(dpdk_helper_ctx_t *phc, DPDK_CHILD_LOG("%s:%s:%d %s\n", phc->shm_name, __FUNCTION__, __LINE__, dpdk_helper_status_str(status)); -#ifdef DPDK_HELPER_USE_PIPES close(phc->pipes[1]); -#endif /* DPDK_HELPER_USE_PIPES */ phc->status = status; @@ -433,9 +439,7 @@ static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, char errbuf[ERR_BUF_SIZE]; DPDK_HELPER_TRACE(phc->shm_name); -#ifdef DPDK_HELPER_USE_PIPES close(phc->pipes[1]); -#endif /* DPDK_HELPER_USE_PIPES */ if (phc->status == DPDK_HELPER_ALIVE_SENDING_EVENTS) { phc->status = status; @@ -449,7 +453,7 @@ static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, int err = kill(phc->pid, SIGKILL); if (err) { - ERROR("%s error sending kill to helper: %s\n", __FUNCTION__, + ERROR("%s error sending kill to helper: %s", __FUNCTION__, sstrerror(errno, errbuf, sizeof(errbuf))); } } @@ -460,7 +464,7 @@ static int dpdk_helper_exit_command(dpdk_helper_ctx_t *phc, int err = kill(phc->pid, SIGKILL); if (err) { - ERROR("%s error sending kill to helper: %s\n", __FUNCTION__, + ERROR("%s error sending kill to helper: %s", __FUNCTION__, sstrerror(errno, errbuf, sizeof(errbuf))); } } @@ -479,7 +483,6 @@ static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc) { /* EAL config must be initialized */ assert(phc->eal_config.coremask[0] != 0); assert(phc->eal_config.memory_channels[0] != 0); - assert(phc->eal_config.process_type[0] != 0); assert(phc->eal_config.file_prefix[0] != 0); argp[argc++] = "collectd-dpdk"; @@ -501,7 +504,16 @@ static int dpdk_helper_eal_init(dpdk_helper_ctx_t *phc) { } argp[argc++] = "--proc-type"; - argp[argc++] = phc->eal_config.process_type; + argp[argc++] = "secondary"; + + if (strcasecmp(phc->eal_config.log_level, "") != 0) { + argp[argc++] = "--log-level"; + argp[argc++] = phc->eal_config.log_level; + } + if (strcasecmp(phc->eal_config.rte_driver_lib_path, "") != 0) { + argp[argc++] = "-d"; + argp[argc++] = phc->eal_config.rte_driver_lib_path; + } assert(argc <= (DPDK_EAL_ARGC * 2 + 1)); @@ -698,7 +710,6 @@ static int dpdk_helper_status_check(dpdk_helper_ctx_t *phc) { return 0; } -#ifdef DPDK_HELPER_USE_PIPES static void dpdk_helper_check_pipe(dpdk_helper_ctx_t *phc) { char buf[DPDK_MAX_BUFFER_SIZE]; char out[DPDK_MAX_BUFFER_SIZE]; @@ -708,6 +719,8 @@ static void dpdk_helper_check_pipe(dpdk_helper_ctx_t *phc) { .fd = phc->pipes[0], .events = POLLIN, }; int data_avail = poll(&fds, 1, 0); + DEBUG("%s:dpdk_helper_check_pipe: poll data_avail=%d", phc->shm_name, + data_avail); if (data_avail < 0) { if (errno != EINTR || errno != EAGAIN) { char errbuf[ERR_BUF_SIZE]; @@ -716,14 +729,15 @@ static void dpdk_helper_check_pipe(dpdk_helper_ctx_t *phc) { } } while (data_avail) { - int nbytes = read(phc->pipes[0], buf, sizeof(buf)); + int nbytes = read(phc->pipes[0], buf, (sizeof(buf) - 1)); + DEBUG("%s:dpdk_helper_check_pipe: read nbytes=%d", phc->shm_name, nbytes); if (nbytes <= 0) break; - sstrncpy(out, buf, nbytes); + buf[nbytes] = '\0'; + sstrncpy(out, buf, (nbytes + 1)); DEBUG("%s: helper process:\n%s", phc->shm_name, out); } } -#endif /* DPDK_HELPER_USE_PIPES */ int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result, cdtime_t cmd_wait_time) { @@ -737,13 +751,9 @@ int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result, phc->cmd_wait_time = cmd_wait_time; - int ret = 0; - - ret = dpdk_helper_status_check(phc); + int ret = dpdk_helper_status_check(phc); -#ifdef DPDK_HELPER_USE_PIPES dpdk_helper_check_pipe(phc); -#endif /* DPDK_HELPER_USE_PIPES */ if (ret != 0) { return ret; @@ -758,7 +768,7 @@ int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result, int err = sem_post(&phc->sema_cmd_start); if (err) { char errbuf[ERR_BUF_SIZE]; - ERROR("dpdk_helper_worker: error posting sema_cmd_start semaphore (%s)\n", + ERROR("dpdk_helper_worker: error posting sema_cmd_start semaphore (%s)", sstrerror(errno, errbuf, sizeof(errbuf))); } @@ -801,9 +811,7 @@ int dpdk_helper_command(dpdk_helper_ctx_t *phc, enum DPDK_CMD cmd, int *result, } } -#ifdef DPDK_HELPER_USE_PIPES dpdk_helper_check_pipe(phc); -#endif /* DPDK_HELPER_USE_PIPES */ DEBUG("%s: DPDK command complete (cmd=%d, result=%d)", phc->shm_name, phc->cmd, phc->cmd_result); @@ -831,18 +839,20 @@ uint128_t str_to_uint128(const char *str, int len) { uint128_t lcore_mask; int err = 0; - memset(&lcore_mask, 0, sizeof(uint128_t)); + memset(&lcore_mask, 0, sizeof(lcore_mask)); if (len <= 2 || strncmp(str, "0x", 2) != 0) { ERROR("%s Value %s should be represened in hexadecimal format", __FUNCTION__, str); return lcore_mask; } - + /* If str is <= 64 bit long ('0x' + 16 chars = 18 chars) then + * conversion is straightforward. Otherwise str is splitted into 64b long + * blocks */ if (len <= 18) { lcore_mask.low = strtoull_safe(str, &err); if (err) - goto parse_out; + return lcore_mask; } else { char low_str[DATA_MAX_NAME_LEN]; char high_str[DATA_MAX_NAME_LEN]; @@ -855,15 +865,32 @@ uint128_t str_to_uint128(const char *str, int len) { lcore_mask.low = strtoull_safe(low_str, &err); if (err) - goto parse_out; + return lcore_mask; lcore_mask.high = strtoull_safe(high_str, &err); if (err) { lcore_mask.low = 0; - goto parse_out; + return lcore_mask; } } - -parse_out: return lcore_mask; } + +uint8_t dpdk_helper_eth_dev_count() { + uint8_t ports = rte_eth_dev_count(); + if (ports == 0) { + ERROR( + "%s:%d: No DPDK ports available. Check bound devices to DPDK driver.\n", + __FUNCTION__, __LINE__); + return ports; + } + + if (ports > RTE_MAX_ETHPORTS) { + ERROR("%s:%d: Number of DPDK ports (%u) is greater than " + "RTE_MAX_ETHPORTS=%d. Ignoring extra ports\n", + __FUNCTION__, __LINE__, ports, RTE_MAX_ETHPORTS); + ports = RTE_MAX_ETHPORTS; + } + + return ports; +}