From cd5b32ee33ca37828fbcba51c988e13e82645583 Mon Sep 17 00:00:00 2001 From: Intel Date: Wed, 18 Sep 2013 12:00:00 +0200 Subject: [PATCH] eal: allow to whitelist devices The new option --use-device is a PCI whitelist. It is the opposite to -b option. Signed-off-by: Intel --- lib/librte_eal/common/eal_common_pci.c | 28 ++- lib/librte_eal/common/eal_common_whitelist.c | 196 +++++++++++++++++++ lib/librte_eal/common/include/eal_private.h | 38 ++++ lib/librte_eal/linuxapp/eal/Makefile | 1 + lib/librte_eal/linuxapp/eal/eal.c | 23 ++- 5 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 lib/librte_eal/common/eal_common_whitelist.c diff --git a/lib/librte_eal/common/eal_common_pci.c b/lib/librte_eal/common/eal_common_pci.c index b042b00a0b..e882458c1c 100644 --- a/lib/librte_eal/common/eal_common_pci.c +++ b/lib/librte_eal/common/eal_common_pci.c @@ -75,6 +75,8 @@ #include #include #include +#include +#include #include "eal_private.h" @@ -135,6 +137,24 @@ pci_probe_all_drivers(struct rte_pci_device *dev) return -1; } +/* + * Check if a device is ok to use according to whitelist rules. + */ +static int +pcidev_is_whitelisted(struct rte_pci_device *dev) +{ + char buf[16]; + if (dev->addr.domain == 0) { + rte_snprintf(buf, sizeof(buf), PCI_SHORT_PRI_FMT, dev->addr.bus, + dev->addr.devid, dev->addr.function); + if (eal_dev_is_whitelisted(buf, NULL)) + return 1; + } + rte_snprintf(buf, sizeof(buf), PCI_PRI_FMT, dev->addr.domain,dev->addr.bus, + dev->addr.devid, dev->addr.function); + return eal_dev_is_whitelisted(buf, NULL); +} + /* * Scan the content of the PCI bus, and call the devinit() function for * all registered drivers that have a matching entry in its id_table @@ -146,7 +166,13 @@ rte_eal_pci_probe(void) struct rte_pci_device *dev = NULL; TAILQ_FOREACH(dev, &device_list, next) - pci_probe_all_drivers(dev); + if (!eal_dev_whitelist_exists()) + pci_probe_all_drivers(dev); + else if (pcidev_is_whitelisted(dev) && pci_probe_all_drivers(dev) < 0 ) + rte_exit(EXIT_FAILURE, "Requested device " PCI_PRI_FMT + " cannot be used\n", dev->addr.domain,dev->addr.bus, + dev->addr.devid, dev->addr.function); + return 0; } diff --git a/lib/librte_eal/common/eal_common_whitelist.c b/lib/librte_eal/common/eal_common_whitelist.c new file mode 100644 index 0000000000..beb26e8ddf --- /dev/null +++ b/lib/librte_eal/common/eal_common_whitelist.c @@ -0,0 +1,196 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2010-2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/** + * This file provides functions for managing a whitelist of devices. An EAL + * command-line parameter should be used for specifying what devices to + * whitelist, and the functions here should be called in handling that + * parameter. Then when scanning the PCI bus, the is_whitelisted() function + * can be used to query the previously set up whitelist. + */ +#include +#include +#include +#include +#include +#include +#include "eal_private.h" + +static char dev_list_str[4096]; +static size_t dev_list_str_len = 0; + +/* + * structure for storing a whitelist entry. Unlike for blacklists, we may + * in future use this for dummy NICs not backed by a physical device, e.g. + * backed by a file-system object instead, so we store the device path/addr + * as a string, rather than as a PCI Bus-Device-Function. + */ +static struct whitelist_entry { + const char *device_str; + const char *device_params; +} whitelist[RTE_MAX_ETHPORTS] = { {NULL, NULL} }; + +static unsigned num_wl_entries = 0; + +/* store a whitelist parameter for later parsing */ +int +eal_dev_whitelist_add_entry(const char *entry) +{ + dev_list_str_len += rte_snprintf(&dev_list_str[dev_list_str_len], + sizeof(dev_list_str)-dev_list_str_len, "%s,", entry); + /* check for strings that won't fit (snprintf doesn't go beyond buffer) */ + if (dev_list_str_len >= sizeof(dev_list_str)) { + dev_list_str_len = sizeof(dev_list_str) - 1; + return -1; + } + + return 0; +} + +/* check if a whitelist has been set up */ +int +eal_dev_whitelist_exists(void) +{ + return !!dev_list_str_len; +} + +/* sanity checks a whitelist entry to ensure device is correct */ +static int +is_valid_wl_entry(const char *device_str, size_t dev_buf_len) +{ + char buf[16]; + unsigned i; + struct rte_pci_addr pci_addr = { .domain = 0 }; + + if (eal_parse_pci_BDF(device_str, &pci_addr) == 0) { + size_t n = rte_snprintf(buf, sizeof(buf), PCI_SHORT_PRI_FMT, + pci_addr.bus, pci_addr.devid, pci_addr.function); + return (n == dev_buf_len) && (!strncmp(buf, device_str, dev_buf_len)); + } + if (eal_parse_pci_DomBDF(device_str, &pci_addr) == 0) { + size_t n = rte_snprintf(buf, sizeof(buf), PCI_PRI_FMT, pci_addr.domain, + pci_addr.bus, pci_addr.devid, pci_addr.function); + return (n == dev_buf_len) && (!strncmp(buf, device_str, dev_buf_len)); + } + return 0; +} + +/* + * parse a whitelist string into a set of valid devices. To be called once + * all parameters have been added to the whitelist string. + */ +int +eal_dev_whitelist_parse(void) +{ + char *devs[RTE_MAX_ETHPORTS + 1] = { NULL }; + int i, num_devs; + unsigned dev_name_len, j; + + if (!eal_dev_whitelist_exists()) + return -1; + + /* strip off any extra commas */ + if (dev_list_str[dev_list_str_len - 1] == ',') + dev_list_str[--dev_list_str_len] = '\0'; + + /* split on commas into separate device entries */ + num_devs = rte_strsplit(dev_list_str, sizeof(dev_list_str), devs, + RTE_MAX_ETHPORTS+1, ','); + if (num_devs > RTE_MAX_ETHPORTS) { + RTE_LOG(ERR, EAL, "Error, too many devices specified. " + "[RTE_MAX_ETHPORTS = %u]\n", (unsigned)RTE_MAX_ETHPORTS); + return -1; + } + + size_t buf_len_rem = sizeof(dev_list_str); /* for tracking buffer length */ + for (i = 0; i < num_devs; i++) { + char *dev_n_params[2]; /* possibly split device name from params*/ + + size_t curr_len = strnlen(devs[i], buf_len_rem); + buf_len_rem-= (curr_len + 1); + + int split_res = rte_strsplit(devs[i], curr_len, dev_n_params, 2, ';'); + + /* device names go lower case, i.e. '00:0A.0' wouldn't work + * while '00:0a.0' would. */ + dev_name_len = strnlen(dev_n_params[0], curr_len); + for (j = 0; j < dev_name_len; j++) + dev_n_params[0][j] = (char)tolower((int)dev_n_params[0][j]); + + switch (split_res) { + case 2: + whitelist[i].device_params = dev_n_params[1]; /* fallthrough */ + case 1: + whitelist[i].device_str = dev_n_params[0]; + break; + default: /* should never ever occur */ + rte_panic("Fatal error parsing whitelist [--use-device] options\n"); + } + + if (!is_valid_wl_entry(whitelist[i].device_str, + strnlen(whitelist[i].device_str, curr_len))) { + RTE_LOG(ERR, EAL, "Error parsing device identifier: '%s'\n", + whitelist[i].device_str); + return -1; + } + } + num_wl_entries = num_devs; + return 0; +} + +/* check if a device is on the whitelist */ +int +eal_dev_is_whitelisted(const char *device_str, const char **params) +{ + unsigned i; + if (!eal_dev_whitelist_exists()) + return 0; /* return false if no whitelist set up */ + + for (i = 0; i < num_wl_entries; i++) + if (strncmp(device_str, whitelist[i].device_str, 32) == 0) { + if (params != NULL) + *params = whitelist[i].device_params; + return 1; + } + + return 0; +} + +/* clear the whole whitelist */ +void +eal_dev_whitelist_clear(void) +{ + dev_list_str[0] = '\0'; + dev_list_str_len = num_wl_entries = 0; +} diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 4d0044475f..7553bd9c73 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -174,4 +174,42 @@ int rte_eal_intr_init(void); */ int rte_eal_alarm_init(void); +/** + * When parsing command-line args add a new entry to the whitelist string + * to be parsed, i.e. this is used to join multiple --use-device flags together + * + * This function is private to EAL + */ +int eal_dev_whitelist_add_entry(const char *entry); + +/** + * Returns true if whitelist parameters are available for parsing, + * false otherwise. + * + * This function is private to EAL + */ +int eal_dev_whitelist_exists(void); + +/** + * Parse the combined whitelist options into a single white-list for later use. + * + * This function is private to EAL + */ +int eal_dev_whitelist_parse(void); + +/** + * Check if a particular device is whitelisted or not. Returns true if the + * device is in the whitelist. + * + * This function is private to EAL + */ +int eal_dev_is_whitelisted(const char *device_str, const char **params); + +/** + * This function clears the whitelist settings. + * + * This function is private to the EAL. + */ +void eal_dev_whitelist_clear(void); + #endif /* _EAL_PRIVATE_H_ */ diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile index 3024adf92d..8a9bca22ea 100644 --- a/lib/librte_eal/linuxapp/eal/Makefile +++ b/lib/librte_eal/linuxapp/eal/Makefile @@ -66,6 +66,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_tailqs.c SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_errno.c SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_cpuflags.c SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_hexdump.c +SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_common_whitelist.c CFLAGS_eal.o := -D_GNU_SOURCE CFLAGS_eal_thread.o := -D_GNU_SOURCE diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index 93a6d82991..8640e55bb8 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -87,6 +87,7 @@ #define OPT_NO_HUGE "no-huge" #define OPT_FILE_PREFIX "file-prefix" #define OPT_SOCKET_MEM "socket-mem" +#define OPT_USE_DEVICE "use-device" #define OPT_SYSLOG "syslog" #define RTE_EAL_BLACKLIST_SIZE 0x100 @@ -335,6 +336,9 @@ eal_usage(const char *prgname) " --"OPT_HUGE_DIR" : directory where hugetlbfs is mounted\n" " --"OPT_PROC_TYPE" : type of this process\n" " --"OPT_FILE_PREFIX": prefix for hugepage filenames\n" + " --"OPT_USE_DEVICE": use the specified ethernet device(s) only." + "Use comma-separate <[domain:]bus:devid.func> values.\n" + " [NOTE: Cannot be used with -b option]\n" " --"OPT_VMWARE_TSC_MAP": use VMware TSC map instead of " "native RDTSC\n" "\nEAL options for DEBUG use only:\n" @@ -589,6 +593,7 @@ eal_parse_args(int argc, char **argv) {OPT_PROC_TYPE, 1, 0, 0}, {OPT_FILE_PREFIX, 1, 0, 0}, {OPT_SOCKET_MEM, 1, 0, 0}, + {OPT_USE_DEVICE, 1, 0, 0}, {OPT_SYSLOG, 1, NULL, 0}, {0, 0, 0, 0} }; @@ -705,6 +710,9 @@ eal_parse_args(int argc, char **argv) return -1; } } + else if (!strcmp(lgopts[option_index].name, OPT_USE_DEVICE)) { + eal_dev_whitelist_add_entry(optarg); + } else if (!strcmp(lgopts[option_index].name, OPT_SYSLOG)) { if (eal_parse_syslog(optarg) < 0) { RTE_LOG(ERR, EAL, "invalid parameters for --" @@ -762,8 +770,21 @@ eal_parse_args(int argc, char **argv) return -1; } - if (blacklist_index > 0) + /* if no blacklist, parse a whitelist */ + if (blacklist_index > 0) { + if (eal_dev_whitelist_exists()) { + RTE_LOG(ERR, EAL, "Error: blacklist [-b] and whitelist " + "[--use-device] options cannot be used at the same time\n"); + eal_usage(prgname); + return -1; + } rte_eal_pci_set_blacklist(eal_dev_blacklist, blacklist_index); + } else { + if (eal_dev_whitelist_exists() && eal_dev_whitelist_parse() < 0) { + RTE_LOG(ERR,EAL, "Error parsing whitelist[--use-device] options\n"); + return -1; + } + } if (optind >= 0) argv[optind-1] = prgname; -- 2.20.1