diff options
Diffstat (limited to 'vlib/vlib/unix/pci.c')
-rw-r--r-- | vlib/vlib/unix/pci.c | 235 |
1 files changed, 131 insertions, 104 deletions
diff --git a/vlib/vlib/unix/pci.c b/vlib/vlib/unix/pci.c index 02c37f72707..75241f3f1c6 100644 --- a/vlib/vlib/unix/pci.c +++ b/vlib/vlib/unix/pci.c @@ -46,87 +46,105 @@ #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <linux/ethtool.h> +#include <linux/sockios.h> + linux_pci_main_t linux_pci_main; -static clib_error_t * -foreach_directory_file (char * dir_name, - clib_error_t * (* f) (void * arg, u8 * path_name, u8 * file_name), - void * arg, - int scan_dirs) +clib_error_t * +vlib_pci_bind_to_uio (vlib_pci_device_t * d, char * uio_driver_name) { - DIR * d; - struct dirent * e; clib_error_t * error = 0; - u8 * s, * t; + u8 *s = 0; + DIR *dir = 0; + struct dirent *e; + int fd; + pci_config_header_t * c; + u8 * dev_dir_name = format(0, "/sys/bus/pci/devices/%U", + format_vlib_pci_addr, &d->bus_address); + + c = &d->config0.header; + + /* if uio sub-directory exists, we are fine, device is + already bound to UIO driver */ + s = format (s, "%v/uio%c", dev_dir_name, 0); + if (access ( (char *) s, F_OK) == 0) + goto done; + vec_reset_length (s); + + /* walk trough all linux interfaces and if interface belonging to + this device is founf check if interface is admin up */ + dir = opendir ("/sys/class/net"); + s = format(s, "%U%c", format_vlib_pci_addr, &d->bus_address, 0); - d = opendir (dir_name); - if (! d) + if (!dir) { - /* System has no PCI bus. */ - if (errno == ENOENT) - return 0; - return clib_error_return_unix (0, "open `%s'", dir_name); + error = clib_error_return (0, "Skipping PCI device %U: failed to " + "read /sys/class/net", + format_vlib_pci_addr, &d->bus_address); + goto done; } - s = t = 0; - while (1) + fd = socket(PF_INET, SOCK_DGRAM, 0); + + while((e = readdir (dir))) { - e = readdir (d); - if (! e) - break; - if (scan_dirs) - { - if (e->d_type == DT_DIR - && (! strcmp (e->d_name, ".") - || ! strcmp (e->d_name, ".."))) - continue; - } - else - { - if (e->d_type == DT_DIR) - continue; - } + struct ifreq ifr; + struct ethtool_drvinfo drvinfo; - s = format (s, "%s/%s", dir_name, e->d_name); - t = format (t, "%s", e->d_name); - error = f (arg, s, t); - _vec_len (s) = 0; - _vec_len (t) = 0; + if (e->d_name[0] == '.') /* skip . and .. */ + continue; - if (error) - break; - } + memset(&ifr, 0, sizeof ifr); + memset(&drvinfo, 0, sizeof drvinfo); + ifr.ifr_data = (char *) &drvinfo; + strncpy(ifr.ifr_name, e->d_name, IFNAMSIZ); + drvinfo.cmd = ETHTOOL_GDRVINFO; + ioctl (fd, SIOCETHTOOL, &ifr); - vec_free (s); - closedir (d); + if (strcmp ((char *) s, drvinfo.bus_info)) + continue; - return error; -} + memset (&ifr, 0, sizeof(ifr)); + strncpy (ifr.ifr_name, e->d_name, IFNAMSIZ); + ioctl (fd, SIOCGIFFLAGS, &ifr); + close (fd); -static clib_error_t * -write_sys_fs (char * file_name, char * fmt, ...) -{ - u8 * s; - int fd; + if (ifr.ifr_flags & IFF_UP) + { + error = clib_error_return (0, "Skipping PCI device %U as host " + "interface %s is up", + format_vlib_pci_addr, &d->bus_address, + e->d_name); + goto done; + } + } - fd = open (file_name, O_WRONLY); - if (fd < 0) - return clib_error_return_unix (0, "open `%s'", file_name); + close (fd); + vec_reset_length (s); + + s = format (s, "%v/driver/unbind%c", dev_dir_name, 0); + write_sys_fs ((char *) s, "%U", format_vlib_pci_addr, &d->bus_address); + vec_reset_length (s); - va_list va; - va_start (va, fmt); - s = va_format (0, fmt, &va); - va_end (va); + s = format (s, "/sys/bus/pci/drivers/%s/new_id%c", uio_driver_name, 0); + write_sys_fs ((char *) s, "0x%04x 0x%04x", c->vendor_id, c->device_id); + vec_reset_length (s); - if (write (fd, s, vec_len (s)) < 0) - return clib_error_return_unix (0, "write `%s'", file_name); + s = format (s, "/sys/bus/pci/drivers/%s/bind%c", uio_driver_name, 0); + write_sys_fs ((char *) s, "%U", format_vlib_pci_addr, &d->bus_address); +done: + closedir (dir); vec_free (s); - close (fd); - return 0; + vec_free (dev_dir_name); + return error; } + static clib_error_t * scan_uio_dir (void * arg, u8 * path_name, u8 * file_name) { @@ -149,7 +167,7 @@ static clib_error_t * linux_pci_uio_read_ready (unix_file_t * uf) linux_pci_device_t * l; u32 li = uf->private_data; - l = pool_elt_at_index (pm->pci_devices, li); + l = pool_elt_at_index (pm->linux_pci_devices, li); vlib_node_set_interrupt_pending (vm, l->device_input_node_index); /* Let node know which device is interrupting. */ @@ -176,7 +194,7 @@ static uword pci_resource_size (uword os_handle, uword resource) struct stat b; uword result = 0; - p = pool_elt_at_index (pm->pci_devices, os_handle); + p = pool_elt_at_index (pm->linux_pci_devices, os_handle); file_name = format (0, "%v/resource%d%c", p->dev_dir_name, resource, 0); if (stat ((char *) file_name, &b) >= 0) @@ -193,7 +211,7 @@ void os_add_pci_disable_interrupts_reg (uword os_handle, u32 resource, char * file_name; clib_error_t * error; - l = pool_elt_at_index (pm->pci_devices, os_handle); + l = pool_elt_at_index (pm->linux_pci_devices, os_handle); ASSERT (resource == 0); ASSERT (reg_offset < pci_resource_size (os_handle, resource)); file_name = (char *) format (0, "%s/disable_interrupt_regs%c", l->dev_dir_name, 0); @@ -203,7 +221,7 @@ void os_add_pci_disable_interrupts_reg (uword os_handle, u32 resource, vec_free (file_name); } -static void add_device (pci_device_t * dev, linux_pci_device_t * pdev) +static void add_device (vlib_pci_device_t * dev, linux_pci_device_t * pdev) { linux_pci_main_t * pm = &linux_pci_main; linux_pci_device_t * l; @@ -213,30 +231,12 @@ static void add_device (pci_device_t * dev, linux_pci_device_t * pdev) c = &dev->config0.header; - pool_get (pm->pci_devices, l); + pool_get (pm->linux_pci_devices, l); l[0] = pdev[0]; l->dev_dir_name = vec_dup (l->dev_dir_name); - /* Parse bus, dev, function from directory name. */ - { - unformat_input_t input; - - unformat_init_string (&input, (char *) l->dev_dir_name, - vec_len (l->dev_dir_name)); - - if (! unformat (&input, "/sys/bus/pci/devices/%x:%x:%x.%x", - &x[0], &x[1], &x[2], &x[3])) - abort (); - - unformat_free (&input); - - l->bus_address.bus = x[1]; - l->bus_address.slot_function = (x[2] << 3) | x[3]; - dev->bus_address = l->bus_address; - } - - dev->os_handle = l - pm->pci_devices; + dev->os_handle = l - pm->linux_pci_devices; error = write_sys_fs ("/sys/bus/pci/drivers/uio_pci_dma/new_id", "%x %x", c->vendor_id, c->device_id); @@ -269,7 +269,7 @@ static void add_device (pci_device_t * dev, linux_pci_device_t * pdev) template.read_function = linux_pci_uio_read_ready; template.file_descriptor = l->uio_fd; template.error_function = linux_pci_uio_error_ready; - template.private_data = l - pm->pci_devices; + template.private_data = l - pm->linux_pci_devices; /* To be filled in by driver. */ l->device_input_node_index = ~0; @@ -305,7 +305,7 @@ os_read_write_pci_config (uword os_handle, linux_pci_device_t * p; int n; - p = pool_elt_at_index (pm->pci_devices, os_handle); + p = pool_elt_at_index (pm->linux_pci_devices, os_handle); if (address != lseek (p->config_fd, address, SEEK_SET)) return clib_error_return_unix (0, "seek offset %d", address); @@ -338,7 +338,7 @@ os_map_pci_resource_internal (uword os_handle, int flags = MAP_SHARED; error = 0; - p = pool_elt_at_index (pm->pci_devices, os_handle); + p = pool_elt_at_index (pm->linux_pci_devices, os_handle); file_name = format (0, "%v/resource%d%c", p->dev_dir_name, resource, 0); fd = open ((char *) file_name, O_RDWR); @@ -404,9 +404,9 @@ void os_free_pci_device (uword os_handle) linux_pci_main_t * pm = &linux_pci_main; linux_pci_device_t * l; - l = pool_elt_at_index (pm->pci_devices, os_handle); + l = pool_elt_at_index (pm->linux_pci_devices, os_handle); linux_pci_device_free (l); - pool_put (pm->pci_devices, l); + pool_put (pm->linux_pci_devices, l); } u8 * format_os_pci_handle (u8 * s, va_list * va) @@ -415,10 +415,9 @@ u8 * format_os_pci_handle (u8 * s, va_list * va) uword os_pci_handle = va_arg (*va, uword); linux_pci_device_t * l; - l = pool_elt_at_index (pm->pci_devices, os_pci_handle); + l = pool_elt_at_index (pm->linux_pci_devices, os_pci_handle); return format (s, "%x/%x/%x", l->bus_address.bus, - (l->bus_address.slot_function >> 3), - (l->bus_address.slot_function & 0x7)); + l->bus_address.slot, l->bus_address.function); } static inline pci_device_registration_t * @@ -450,17 +449,17 @@ static inline u8 kernel_driver_installed (pci_device_registration_t *r) static clib_error_t * init_device_from_registered (vlib_main_t * vm, - pci_device_t * dev, + vlib_pci_device_t * dev, linux_pci_device_t * pdev) { - unix_main_t * um = vlib_unix_get_main(); + linux_pci_main_t * lpm = &linux_pci_main; pci_device_registration_t * r; pci_device_id_t * i; pci_config_header_t * c; c = &dev->config0.header; - r = um->pci_device_registrations; + r = lpm->pci_device_registrations; while (r) { @@ -490,7 +489,7 @@ init_device_from_registered (vlib_main_t * vm, static clib_error_t * init_device (vlib_main_t * vm, - pci_device_t * dev, + vlib_pci_device_t * dev, linux_pci_device_t * pdev) { return init_device_from_registered (vm, dev, pdev); @@ -500,10 +499,11 @@ static clib_error_t * scan_device (void * arg, u8 * dev_dir_name, u8 * ignored) { vlib_main_t * vm = arg; + linux_pci_main_t * pm = &linux_pci_main; int fd; u8 * f; clib_error_t * error = 0; - pci_device_t dev = {0}; + vlib_pci_device_t * dev; linux_pci_device_t pdev = {0}; f = format (0, "%v/config%c", dev_dir_name, 0); @@ -519,11 +519,14 @@ scan_device (void * arg, u8 * dev_dir_name, u8 * ignored) goto done; } + pool_get (pm->pci_devs, dev); + /* You can only read more that 64 bytes of config space as root; so we try to read the full space but fall back to just the first 64 bytes. */ - if (read (fd, &dev.config_data, sizeof (dev.config_data)) != sizeof (dev.config_data) - && read (fd, &dev.config0, sizeof (dev.config0)) != sizeof (dev.config0)) + if (read (fd, &dev->config_data, sizeof (dev->config_data)) != sizeof (dev->config_data) + && read (fd, &dev->config0, sizeof (dev->config0)) != sizeof (dev->config0)) { + pool_put (pm->pci_devs, dev); error = clib_error_return_unix (0, "read `%s'", f); goto done; } @@ -532,23 +535,44 @@ scan_device (void * arg, u8 * dev_dir_name, u8 * ignored) static pci_config_header_t all_ones; if (all_ones.vendor_id == 0) memset (&all_ones, ~0, sizeof (all_ones)); - - if (! memcmp (&dev.config0.header, &all_ones, sizeof (all_ones))) + + if (! memcmp (&dev->config0.header, &all_ones, sizeof (all_ones))) { + pool_put (pm->pci_devs, dev); error = clib_error_return (0, "invalid PCI config for `%s'", f); goto done; } } - if (dev.config0.header.header_type == 0) - pci_config_type0_little_to_host (&dev.config0); + if (dev->config0.header.header_type == 0) + pci_config_type0_little_to_host (&dev->config0); else - pci_config_type1_little_to_host (&dev.config1); + pci_config_type1_little_to_host (&dev->config1); + + /* Parse bus, dev, function from directory name. */ + { + unformat_input_t input; + + unformat_init_string (&input, (char *) dev_dir_name, + vec_len (dev_dir_name)); + + if (! unformat (&input, "/sys/bus/pci/devices/%U", + unformat_vlib_pci_addr, &dev->bus_address)) + abort (); + + unformat_free (&input); + + pdev.bus_address = dev->bus_address; + } + pdev.config_fd = fd; pdev.dev_dir_name = dev_dir_name; - error = init_device (vm, &dev, &pdev); + hash_set(pm->pci_dev_index_by_pci_addr, dev->bus_address.as_u32, + dev - pm->pci_devs); + + error = init_device (vm, dev, &pdev); done: vec_free (f); @@ -565,6 +589,9 @@ clib_error_t * pci_bus_init (vlib_main_t * vm) if ((error = vlib_call_init_function (vm, unix_input_init))) return error; + ASSERT(sizeof(vlib_pci_addr_t) == sizeof(u32)); + pm->pci_dev_index_by_pci_addr = hash_create (0, sizeof (uword)); + error = foreach_directory_file ("/sys/bus/pci/devices", scan_device, vm, /* scan_dirs */ 0); /* Complain and continue. might not be root, etc. */ |