From a42cd34f106869d5afc26f5b5db7e0cb2f73ae97 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Wed, 13 Apr 2016 18:03:20 +0200 Subject: Rework of DPDK PCI device uio driver binding process This is complete rework of DPDK PCI initialization. It drops previous scheme where lspci/route/awk/sed are used and instead sysfs is solely used for discovering Ethernet PCI devices. Criteria for blacklisting device is changed from exsiting routing table entry to simple interface state obtained by SIOCGIFFLAGS ioctl(). It checks for IFF_UP flag, so as long as interface is declared up and even when carrier is down interface will be blacklisted. Change-Id: I59961ddcf1c19c728934e7fe746f343983741bf1 Signed-off-by: Damjan Marion --- vnet/vnet/devices/dpdk/init.c | 358 +++++++----------------------------------- 1 file changed, 57 insertions(+), 301 deletions(-) (limited to 'vnet') diff --git a/vnet/vnet/devices/dpdk/init.c b/vnet/vnet/devices/dpdk/init.c index 179386970cc..716377c198f 100644 --- a/vnet/vnet/devices/dpdk/init.c +++ b/vnet/vnet/devices/dpdk/init.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -723,196 +725,62 @@ dpdk_lib_init (dpdk_main_t * dm) return 0; } -static clib_error_t * -write_sys_fs (char * file_name, char * fmt, ...) -{ - u8 * s; - int fd; - - fd = open (file_name, O_WRONLY); - if (fd < 0) - return clib_error_return_unix (0, "open `%s'", file_name); - - va_list va; - va_start (va, fmt); - s = va_format (0, fmt, &va); - va_end (va); - vec_add1 (s, 0); // terminate c string - - if (write (fd, s, vec_len (s)) < 0) - return clib_error_return_unix (0, "write '%s' to '%s'", s, file_name); - - vec_free (s); - close (fd); - return 0; -} - -#define VIRTIO_PCI_NAME "virtio-pci" - -static clib_error_t * dpdk_bind_eth_kernel_drivers (vlib_main_t * vm, - char * pci_dev_id, - char * kernel_driver) +static void +dpdk_bind_devices_to_uio (dpdk_main_t * dm) { - dpdk_main_t * dm = &dpdk_main; - unformat_input_t _in; - unformat_input_t * in = &_in; - clib_error_t * error = 0; - u8 * line = 0, * modcmd = 0, * path = 0; - u8 * pci_vid = 0, *pci_did = 0, * devname = 0; - char *driver_name = kernel_driver; - FILE * fp; - - /* - * Bail out now if we're not running as root. - * This allows non-privileged use of the packet generator, etc. - */ - if (geteuid() != 0) - return 0; - - /* - * Get all ethernet pci device numbers for the device type specified. - */ - modcmd = format (0, "lspci -nDd %s | grep 0200 | " - "awk '{ print $1, $3 }'%c", pci_dev_id, 0); - if ((fp = popen ((const char *)modcmd, "r")) == NULL) - { - error = clib_error_return_unix (0, - "Unable to get %s ethernet pci devices.", - pci_dev_id); - goto done; - } - - vec_validate (line, BUFSIZ); - vec_validate (path, BUFSIZ); - while (fgets ((char *)line, BUFSIZ, fp) != NULL) - { - struct stat st; - u8 bind_uio = 1; - line[strlen ((char *)line) - 1] = 0; // chomp trailing newline. - - unformat_init_string (in, (char *)line, strlen((char *)line) + 1); - unformat(in, "%s %s:%s", &devname, &pci_vid, &pci_did); - unformat_free (in); - - /* - * Blacklist all ethernet interfaces in the - * linux IP routing tables (route --inet --inet6) - */ - if (strstr ((char *)dm->eth_if_blacklist, (char *)devname)) - continue; - - /* - * If there are any devices whitelisted, then blacklist all devices - * which are not explicitly whitelisted. - */ - if (dm->eth_if_whitelist && - !strstr ((char *)dm->eth_if_whitelist, (char *)devname)) - continue; - -#ifdef NETMAP - /* - * Optimistically open the device as a netmap device. - */ - if (eth_nm_open((char *)devname)) + linux_pci_main_t * pm = &linux_pci_main; + clib_error_t * error; + vlib_pci_device_t * d; + pci_config_header_t * c; + u8 * pci_addr = 0; + + pool_foreach (d, pm->pci_devs, ({ + c = &d->config0.header; + vec_reset_length (pci_addr); + pci_addr = format (pci_addr, "%U%c", format_vlib_pci_addr, &d->bus_address, 0); + + if (c->device_class != PCI_CLASS_NETWORK_ETHERNET) + continue; + + /* if whitelist exists process only whitelisted devices */ + if (dm->eth_if_whitelist && + !strstr ((char *) dm->eth_if_whitelist, (char *) pci_addr)) + continue; + + /* virtio */ + if (c->vendor_id == 0x1af4 && c->device_id == 0x1000) + ; + /* vmxnet3 */ + else if (c->vendor_id == 0x15ad && c->device_id == 0x07b0) + ; + /* all Intel devices */ + else if (c->vendor_id == 0x8086) + ; + /* Cisco VIC */ + else if (c->vendor_id == 0x1137 && c->device_id == 0x0043) + ; + /* Chelsio T4/T5 */ + else if (c->vendor_id == 0x1425 && (c->device_id & 0xe000) == 0x4000) + ; + else + { + clib_warning ("Unsupported Ethernet PCI device 0x%04x:0x%04x found " + "at PCI address %s\n", (u16) c->vendor_id, (u16) c->device_id, + pci_addr); continue; -#endif - - _vec_len (path) = 0; - path = format (path, "/sys/bus/pci/devices/%s/driver/unbind%c", - devname, 0); - - /* - * If the device is bound to a driver... - */ - if (stat ((const char *)path, &st) == 0) - { - u8 * device_path; - - /* - * If the interface is not a virtio... - */ - if (!driver_name || strcmp(driver_name, VIRTIO_PCI_NAME)) - { - /* - * If it is already bound to driver, don't unbind/bind it. - */ - device_path = format (0, "/sys/bus/pci/drivers/%s/%s/device%c", - driver_name, devname, 0); - if (stat ((const char *)device_path, &st) == 0) - bind_uio = 0; - - vec_free (device_path); - } - - /* - * unbind it from the current driver - */ - if (bind_uio) - { - _vec_len (path) -= 1; - path = format (path, "%c", 0); - error = write_sys_fs ((char *)path, "%s", devname); - if (error) - goto done; - } - } - - /* - * DAW-FIXME: The following bind/unbind dance is necessary for the dpdk - * virtio poll-mode driver to work. - */ - - if (driver_name && !strcmp(driver_name, VIRTIO_PCI_NAME)) - { - /* - * bind interface to the native kernel module - */ - _vec_len (path) = 0; - path = format (path, "/sys/bus/pci/drivers/%s/bind%c", - driver_name, 0); - error = write_sys_fs ((char *)path, "%s", devname); - if (error) - goto done; + } - /* - * unbind interface from the native kernel module - */ - _vec_len (path) -= 5; - path = format (path, "unbind%c", 0); - error = write_sys_fs ((char *)path, "%s", devname); - if (error) - goto done; - } + error = vlib_pci_bind_to_uio (d, (char *) dm->uio_driver_name); - /* - * bind the interface to igb_uio - */ - if (bind_uio) - { - _vec_len (path) = 0; - path = format (path, "/sys/bus/pci/drivers/%s/new_id%c", driver_name, 0); - error = write_sys_fs ((char *) path, "%s %s", pci_vid, pci_did); - - _vec_len (path) = 0; - path = format (path, "/sys/bus/pci/drivers/%s/bind%c", driver_name, 0); - error = write_sys_fs ((char *) path, "%s", devname); - if (error) - { - error = 0; - continue; - } - } - } - - done: - vec_free (line); - vec_free (path); - vec_free (devname); - vec_free (pci_vid); - vec_free (pci_did); - vec_free (modcmd); - pclose (fp); - return error; + if (error) + { + if (!dm->eth_if_whitelist) + dm->eth_if_blacklist = format (dm->eth_if_blacklist, "%U ", + format_vlib_pci_addr, &d->bus_address); + clib_error_report (error); + } + })); + vec_free (pci_addr); } static u32 @@ -956,7 +824,6 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) u8 * s, * tmp = 0; u8 * pci_dev_id = 0; u8 * rte_cmd = 0, * ethname = 0; - FILE * rte_fp; u32 log_level; int ret, i; char * fmt; @@ -1214,91 +1081,6 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) } } - /* - * Blacklist all ethernet interfaces in the linux IP routing tables. - */ - dm->eth_if_blacklist = format (0, "%c", 0); - rte_cmd = format (0, "route --inet --inet6 -n|awk '{print $7}'|sort -u|" - "egrep $(echo $(ls -1d /sys/class/net/*/device|" - "cut -d/ -f5)|sed -s 's/ /|/g')%c", 0); - if ((rte_fp = popen ((const char *)rte_cmd, "r")) == NULL) - { - error = clib_error_return_unix (0, "Unable to find blacklist ethernet" - " interface(s) in linux routing tables."); - goto rte_cmd_err; - - } - - vec_validate (ethname, BUFSIZ); - while (fgets ((char *)ethname, BUFSIZ, rte_fp) != NULL) - { - FILE *rlnk_fp; - u8 * rlnk_cmd = 0, * devname = 0; - - ethname[strlen ((char *)ethname) - 1] = 0; // chomp trailing newline. - - rlnk_cmd = format (0, "readlink /sys/class/net/%s%c", - ethname, 0); - - if ((rlnk_fp = popen ((const char *)rlnk_cmd, "r")) == NULL) - { - error = clib_error_return_unix (0, "Unable to read %s link.", - ethname); - goto rlnk_cmd_err; - } - - vec_validate (devname, BUFSIZ); - while (fgets ((char *)devname, BUFSIZ, rlnk_fp) != NULL) - { - char * pci_id = 0; - - /* - * Extract the device PCI ID name from the link. It is the first - * PCI ID searching backwards from the end of the link pathname. - * For example: - * readlink /sys/class/net/eth0 - * ../../devices/pci0000:00/0000:00:0a.0/virtio4/net/eth0 - */ - for (pci_id = (char *)((devname + strlen((char *)devname))); - ((u8 *)pci_id > devname) && *pci_id != '.'; pci_id--) - ; - - /* - * Verify that the field found is a valid PCI ID. - */ - if ((*(pci_id - 1) == '.') || ((u8 *)(pci_id - 11) < devname) || - (*(pci_id - 11) != '/') || (*(pci_id - 3) != ':') || - (*(pci_id - 6) != ':')) - { - devname[strlen ((char *)devname) - 1] = 0; // chomp trailing newline. - clib_warning ("Unable to extract %s PCI ID (0x%llx \"%s\") " - "from 0x%llx \"%s\"", ethname, pci_id, pci_id, - devname, devname); - continue; - } - - pci_id[2] = 0; - pci_id -= 10; - - /* Don't blacklist any interfaces which have been whitelisted. - */ - if (dm->eth_if_whitelist && - strstr ((char *)dm->eth_if_whitelist, (char *)pci_id)) - continue; - - _vec_len (dm->eth_if_blacklist) -= 1; // chomp trailing NULL. - dm->eth_if_blacklist = format (dm->eth_if_blacklist, " %s%c", - pci_id, 0); - } - - rlnk_cmd_err: - pclose (rlnk_fp); - vec_free (rlnk_cmd); - vec_free (devname); - } - - rte_cmd_err: - pclose (rte_fp); vec_free (rte_cmd); vec_free (ethname); @@ -1336,6 +1118,9 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) dm->eal_init_args[4] = tmp; } + if (no_pci == 0 && geteuid() == 0) + dpdk_bind_devices_to_uio(dm); + /* * If there are whitelisted devices, * add the whitelist option & device list to the dpdk arg list... @@ -1365,35 +1150,6 @@ dpdk_config (vlib_main_t * vm, unformat_input_t * input) vec_add1 (dm->eal_init_args, pci_dev_id); } - if (no_pci == 0) - { - /* - * Bind Virtio pci devices to the igb_uio kernel driver. - */ - error = dpdk_bind_eth_kernel_drivers (vm, "1af4:1000", VIRTIO_PCI_NAME); - if (error) - return error; - - /* - * Bind vmxnet3 pci devices to the igb_uio kernel driver. - */ - error = dpdk_bind_eth_kernel_drivers (vm, "15ad:07b0", - (char *) dm->uio_driver_name); - if (error) - return error; - - /* - * Bind Intel ethernet pci devices to igb_uio kernel driver. - */ - error = dpdk_bind_eth_kernel_drivers (vm, "8086:", - (char *) dm->uio_driver_name); - /* - * Bind Cisco VIC ethernet pci devices to igb_uio kernel driver. - */ - error = dpdk_bind_eth_kernel_drivers (vm, "1137:0043", - (char *) dm->uio_driver_name); - } - /* set master-lcore */ tmp = format (0, "--master-lcore%c", 0); vec_add1 (dm->eal_init_args, tmp); -- cgit 1.2.3-korg