X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvlib%2Flinux%2Fpci.c;h=69d26fd7d64e5e39ec4035395bc2e71fb94be035;hb=38c619115;hp=d968fe266958b6ebfbb90e3ba6cbf69c1810741d;hpb=5714a49f14b93fc51185c6d37af101d24fc5dbd2;p=vpp.git diff --git a/src/vlib/linux/pci.c b/src/vlib/linux/pci.c index d968fe26695..69d26fd7d64 100644 --- a/src/vlib/linux/pci.c +++ b/src/vlib/linux/pci.c @@ -61,12 +61,17 @@ static const char *sysfs_pci_drv_path = "/sys/bus/pci/drivers"; static char *sysfs_mod_vfio_noiommu = "/sys/module/vfio/parameters/enable_unsafe_noiommu_mode"; -#define pci_log_debug(vm, dev, f, ...) \ - vlib_log(VLIB_LOG_LEVEL_DEBUG, pci_main.log_default, "%U: " f, \ - format_vlib_pci_addr, vlib_pci_get_addr(vm, dev->handle), ## __VA_ARGS__) -#define pci_log_err(vm, dev, f, ...) \ - vlib_log(VLIB_LOG_LEVEL_ERR, pci_main.log_default, "%U: " f, \ - format_vlib_pci_addr, vlib_pci_get_addr(vm, dev->handle), ## __VA_ARGS__) +VLIB_REGISTER_LOG_CLASS (pci_log, static) = { + .class_name = "pci", + .subclass_name = "linux", +}; + +#define log_debug(p, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_DEBUG, pci_log.class, "%U: " f, \ + format_vlib_pci_log, p->handle, ##__VA_ARGS__) +#define log_err(p, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_ERR, pci_log.class, "%U: " f, format_vlib_pci_log, \ + p->handle, ##__VA_ARGS__) typedef struct { @@ -233,32 +238,14 @@ vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr, /* 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, &di->config_data, sizeof (di->config_data)) < - sizeof (di->config0)) + if (read (fd, &di->config, sizeof (di->config)) < + sizeof (vlib_pci_config_hdr_t)) { err = clib_error_return_unix (0, "read `%s'", f); close (fd); goto error; } - { - static pci_config_header_t all_ones; - if (all_ones.vendor_id == 0) - clib_memset (&all_ones, ~0, sizeof (all_ones)); - - if (!memcmp (&di->config0.header, &all_ones, sizeof (all_ones))) - { - err = clib_error_return (0, "invalid PCI config for `%s'", f); - close (fd); - goto error; - } - } - - if (di->config0.header.header_type == 0) - pci_config_type0_little_to_host (&di->config0); - else - pci_config_type1_little_to_host (&di->config1); - di->numa_node = -1; vec_reset_length (f); f = format (f, "%v/numa_node%c", dev_dir_name, 0); @@ -301,15 +288,19 @@ vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr, di->device_id = tmp; vec_reset_length (f); - f = format (f, "%v/driver%c", dev_dir_name, 0); - di->driver_name = clib_sysfs_link_to_name ((char *) f); + f = format (f, "%v/revision%c", dev_dir_name, 0); + err = clib_sysfs_read ((char *) f, "0x%x", &tmp); + if (err) + goto error; + di->revision = tmp; + + di->driver_name = + clib_file_get_resolved_basename ("%v/driver", dev_dir_name); if (!di->driver_name) di->driver_name = format (0, "%c", 0); di->iommu_group = -1; - vec_reset_length (f); - f = format (f, "%v/iommu_group%c", dev_dir_name, 0); - tmpstr = clib_sysfs_link_to_name ((char *) f); + tmpstr = clib_file_get_resolved_basename ("%v/iommu_group", dev_dir_name); if (tmpstr) { di->iommu_group = atoi ((char *) tmpstr); @@ -348,7 +339,7 @@ vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr, break; len = (tag[2] << 8) | tag[1]; - vec_alloc (data, len); + vec_validate (data, len - 1); if (read (fd, data, len) != len) { @@ -389,8 +380,8 @@ vlib_pci_get_device_root_bus (vlib_pci_addr_t *addr, vlib_pci_addr_t *root_bus) { u8 *rel_path = 0, *abs_path = 0, *link_path = 0; unformat_input_t input; - u32 fd = open (sysfs_pci_dev_path, O_RDONLY); - u32 size = 0; + int fd = open (sysfs_pci_dev_path, O_RDONLY); + ssize_t size = 0; u32 domain = 0, bus; clib_error_t *err = NULL; @@ -453,8 +444,8 @@ directory_exists (char *path) } clib_error_t * -vlib_pci_bind_to_uio (vlib_main_t * vm, vlib_pci_addr_t * addr, - char *uio_drv_name) +vlib_pci_bind_to_uio (vlib_main_t *vm, vlib_pci_addr_t *addr, + char *uio_drv_name, int force) { clib_error_t *error = 0; u8 *s = 0, *driver_name = 0; @@ -486,7 +477,7 @@ vlib_pci_bind_to_uio (vlib_main_t * vm, vlib_pci_addr_t * addr, "is bound to IOMMU group and " "vfio-pci driver is not loaded", format_vlib_pci_addr, addr); - goto done; + goto err0; } else uio_drv_name = "vfio-pci"; @@ -507,92 +498,94 @@ vlib_pci_bind_to_uio (vlib_main_t * vm, vlib_pci_addr_t * addr, error = clib_error_return (0, "Skipping PCI device %U: missing " "kernel VFIO or UIO driver", format_vlib_pci_addr, addr); - goto done; + goto err0; } clib_error_free (error); } } - s = format (s, "%v/driver%c", dev_dir_name, 0); - driver_name = clib_sysfs_link_to_name ((char *) s); - vec_reset_length (s); + driver_name = clib_file_get_resolved_basename ("%v/driver", dev_dir_name); if (driver_name && ((strcmp ("vfio-pci", (char *) driver_name) == 0) || (strcmp ("uio_pci_generic", (char *) driver_name) == 0) || (strcmp ("igb_uio", (char *) driver_name) == 0))) - goto done; - - /* 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, addr, 0); + goto err0; - if (!dir) + if (!force) { - error = clib_error_return (0, "Skipping PCI device %U: failed to " - "read /sys/class/net", - format_vlib_pci_addr, addr); - goto done; - } + /* walk trough all linux interfaces and if interface belonging to + this device is found check if interface is admin up */ + dir = opendir ("/sys/class/net"); + s = format (s, "%U%c", format_vlib_pci_addr, addr, 0); - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) - { - error = clib_error_return_unix (0, "socket"); - goto done; - } + if (!dir) + { + error = clib_error_return (0, + "Skipping PCI device %U: failed to " + "read /sys/class/net", + format_vlib_pci_addr, addr); + goto err0; + } - while ((e = readdir (dir))) - { - struct ifreq ifr; - struct ethtool_drvinfo drvinfo; + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + error = clib_error_return_unix (0, "socket"); + goto err1; + } - if (e->d_name[0] == '.') /* skip . and .. */ - continue; + while ((e = readdir (dir))) + { + struct ifreq ifr; + struct ethtool_drvinfo drvinfo; - clib_memset (&ifr, 0, sizeof ifr); - clib_memset (&drvinfo, 0, sizeof drvinfo); - ifr.ifr_data = (char *) &drvinfo; - clib_strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name) - 1); + if (e->d_name[0] == '.') /* skip . and .. */ + continue; - drvinfo.cmd = ETHTOOL_GDRVINFO; - if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) - { - /* Some interfaces (eg "lo") don't support this ioctl */ - if ((errno != ENOTSUP) && (errno != ENODEV)) - clib_unix_warning ("ioctl fetch intf %s bus info error", - e->d_name); - continue; - } + clib_memset (&ifr, 0, sizeof ifr); + clib_memset (&drvinfo, 0, sizeof drvinfo); + ifr.ifr_data = (char *) &drvinfo; + clib_strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name) - 1); - if (strcmp ((char *) s, drvinfo.bus_info)) - continue; + drvinfo.cmd = ETHTOOL_GDRVINFO; + if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) + { + /* Some interfaces (eg "lo") don't support this ioctl */ + if ((errno != ENOTSUP) && (errno != ENODEV)) + clib_unix_warning ("ioctl fetch intf %s bus info error", + e->d_name); + continue; + } - clib_memset (&ifr, 0, sizeof (ifr)); - clib_strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name) - 1); + if (strcmp ((char *) s, drvinfo.bus_info)) + continue; - if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0) - { - error = clib_error_return_unix (0, "ioctl fetch intf %s flags", - e->d_name); - close (fd); - goto done; - } + clib_memset (&ifr, 0, sizeof (ifr)); + clib_strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name) - 1); - if (ifr.ifr_flags & IFF_UP) - { - vlib_log (VLIB_LOG_LEVEL_WARNING, pci_main.log_default, - "Skipping PCI device %U as host " - "interface %s is up", format_vlib_pci_addr, addr, - e->d_name); - close (fd); - goto done; + if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0) + { + error = clib_error_return_unix (0, "ioctl fetch intf %s flags", + e->d_name); + close (fd); + goto err1; + } + + if (ifr.ifr_flags & IFF_UP) + { + vlib_log (VLIB_LOG_LEVEL_WARNING, pci_main.log_default, + "Skipping PCI device %U as host " + "interface %s is up", + format_vlib_pci_addr, addr, e->d_name); + close (fd); + goto err1; + } } - } - close (fd); - vec_reset_length (s); + close (fd); + vec_reset_length (s); + } s = format (s, "%v/driver/unbind%c", dev_dir_name, 0); clib_sysfs_write ((char *) s, "%U", format_vlib_pci_addr, addr); @@ -624,8 +617,9 @@ vlib_pci_bind_to_uio (vlib_main_t * vm, vlib_pci_addr_t * addr, vec_reset_length (s); } -done: +err1: closedir (dir); +err0: vec_free (s); vec_free (dev_dir_name); vec_free (driver_name); @@ -665,13 +659,12 @@ vfio_set_irqs (vlib_main_t * vm, linux_pci_device_t * p, u32 index, u32 start, return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_IRQ_INFO) " "'%U'", format_vlib_pci_addr, &p->addr); - pci_log_debug (vm, p, "%s index:%u count:%u flags: %s%s%s%s(0x%x)", - __func__, ii.index, ii.count, - ii.flags & VFIO_IRQ_INFO_EVENTFD ? "eventfd " : "", - ii.flags & VFIO_IRQ_INFO_MASKABLE ? "maskable " : "", - ii.flags & VFIO_IRQ_INFO_AUTOMASKED ? "automasked " : "", - ii.flags & VFIO_IRQ_INFO_NORESIZE ? "noresize " : "", - ii.flags); + log_debug (p, "%s index:%u count:%u flags: %s%s%s%s(0x%x)", __func__, + ii.index, ii.count, + ii.flags & VFIO_IRQ_INFO_EVENTFD ? "eventfd " : "", + ii.flags & VFIO_IRQ_INFO_MASKABLE ? "maskable " : "", + ii.flags & VFIO_IRQ_INFO_AUTOMASKED ? "automasked " : "", + ii.flags & VFIO_IRQ_INFO_NORESIZE ? "noresize " : "", ii.flags); if (ii.count < start + count) return clib_error_return_unix (0, "vfio_set_irq: unexistng interrupt on " @@ -859,13 +852,12 @@ vlib_pci_register_intx_handler (vlib_main_t * vm, vlib_pci_dev_handle_t h, if (ioctl (p->fd, VFIO_DEVICE_GET_IRQ_INFO, &ii) < 0) return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_IRQ_INFO) '" "%U'", format_vlib_pci_addr, &p->addr); - pci_log_debug (vm, p, "%s index:%u count:%u flags: %s%s%s%s(0x%x)", - __func__, ii.index, ii.count, - ii.flags & VFIO_IRQ_INFO_EVENTFD ? "eventfd " : "", - ii.flags & VFIO_IRQ_INFO_MASKABLE ? "maskable " : "", - ii.flags & VFIO_IRQ_INFO_AUTOMASKED ? "automasked " : "", - ii.flags & VFIO_IRQ_INFO_NORESIZE ? "noresize " : "", - ii.flags); + log_debug ( + p, "%s index:%u count:%u flags: %s%s%s%s(0x%x)", __func__, ii.index, + ii.count, ii.flags & VFIO_IRQ_INFO_EVENTFD ? "eventfd " : "", + ii.flags & VFIO_IRQ_INFO_MASKABLE ? "maskable " : "", + ii.flags & VFIO_IRQ_INFO_AUTOMASKED ? "automasked " : "", + ii.flags & VFIO_IRQ_INFO_NORESIZE ? "noresize " : "", ii.flags); if (ii.count != 1) return clib_error_return (0, "INTx interrupt does not exist on device" "'%U'", format_vlib_pci_addr, &p->addr); @@ -893,6 +885,27 @@ vlib_pci_register_intx_handler (vlib_main_t * vm, vlib_pci_dev_handle_t h, return 0; } +clib_error_t * +vlib_pci_unregister_intx_handler (vlib_main_t *vm, vlib_pci_dev_handle_t h) +{ + linux_pci_device_t *p = linux_pci_get_device (h); + linux_pci_irq_t *irq = &p->intx_irq; + + if (irq->intx_handler == 0) + return 0; + + clib_file_del_by_index (&file_main, irq->clib_file_index); + if (p->type == LINUX_PCI_DEVICE_TYPE_VFIO) + { + close (irq->fd); + irq->fd = -1; + } + + irq->intx_handler = 0; + + return 0; +} + clib_error_t * vlib_pci_register_msix_handler (vlib_main_t * vm, vlib_pci_dev_handle_t h, u32 start, u32 count, @@ -950,6 +963,33 @@ error: return err; } +clib_error_t * +vlib_pci_unregister_msix_handler (vlib_main_t *vm, vlib_pci_dev_handle_t h, + u32 start, u32 count) +{ + clib_error_t *err = 0; + linux_pci_device_t *p = linux_pci_get_device (h); + u32 i; + + if (p->type != LINUX_PCI_DEVICE_TYPE_VFIO) + return clib_error_return (0, "vfio driver is needed for MSI-X interrupt " + "support"); + + for (i = start; i < start + count; i++) + { + linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, i); + + if (irq->fd != -1) + { + clib_file_del_by_index (&file_main, irq->clib_file_index); + close (irq->fd); + irq->fd = -1; + } + } + + return err; +} + clib_error_t * vlib_pci_enable_msix_irq (vlib_main_t * vm, vlib_pci_dev_handle_t h, u16 start, u16 count) @@ -1037,7 +1077,7 @@ add_device_vfio (vlib_main_t * vm, linux_pci_device_t * p, goto error; } - pci_log_debug (vm, p, "%s %U", __func__, format_vfio_region_info, ®); + log_debug (p, "%s %U", __func__, format_vfio_region_info, ®); p->config_offset = reg.offset; p->config_fd = p->fd; @@ -1172,7 +1212,7 @@ vlib_pci_region (vlib_main_t * vm, vlib_pci_dev_handle_t h, u32 bar, int *fd, _fd = p->fd; _size = r->size; _offset = r->offset; - pci_log_debug (vm, p, "%s %U", __func__, format_vfio_region_info, r); + log_debug (p, "%s %U", __func__, format_vfio_region_info, r); clib_mem_free (r); } else @@ -1192,29 +1232,25 @@ vlib_pci_map_region_int (vlib_main_t * vm, vlib_pci_dev_handle_t h, linux_pci_device_t *p = linux_pci_get_device (h); int fd = -1; clib_error_t *error; - int flags = MAP_SHARED; u64 size = 0, offset = 0; - u16 command; + vlib_pci_config_reg_command_t command; - pci_log_debug (vm, p, "map region %u to va %p", bar, addr); + log_debug (p, "map region %u to va %p", bar, addr); - if ((error = vlib_pci_read_config_u16 (vm, h, 4, &command))) + if ((error = vlib_pci_read_config_u16 (vm, h, 4, &command.as_u16))) return error; - if (!(command & PCI_COMMAND_MEMORY)) + if (!(command.mem_space)) { - pci_log_debug (vm, p, "setting memory enable bit"); - command |= PCI_COMMAND_MEMORY; - if ((error = vlib_pci_write_config_u16 (vm, h, 4, &command))) + log_debug (p, "setting memory enable bit"); + command.mem_space = 1; + if ((error = vlib_pci_write_config_u16 (vm, h, 4, &command.as_u16))) return error; } if ((error = vlib_pci_region (vm, h, bar, &fd, &size, &offset))) return error; - if (p->type == LINUX_PCI_DEVICE_TYPE_UIO && addr != 0) - flags |= MAP_FIXED; - *result = clib_mem_vm_map_shared (addr, size, fd, offset, "PCIe %U region %u", format_vlib_pci_addr, vlib_pci_get_addr (vm, h), bar); @@ -1320,12 +1356,19 @@ vlib_pci_device_open (vlib_main_t * vm, vlib_pci_addr_t * addr, if (err) return err; - for (i = ids; i->vendor_id != 0; i++) - if (i->vendor_id == di->vendor_id && i->device_id == di->device_id) - break; - if (i->vendor_id == 0) - return clib_error_return (0, "Wrong vendor or device id"); + if (ids) + { + for (i = ids; i->vendor_id != 0; i++) + if (i->vendor_id == di->vendor_id && i->device_id == di->device_id) + break; + + if (i->vendor_id == 0) + { + vlib_pci_free_device_info (di); + return clib_error_return (0, "Wrong vendor or device id"); + } + } pool_get (lpm->linux_pci_devices, p); p->handle = p - lpm->linux_pci_devices; @@ -1338,9 +1381,8 @@ vlib_pci_device_open (vlib_main_t * vm, vlib_pci_addr_t * addr, */ p->io_fd = -1; - pci_log_debug (vm, p, "open vid:0x%04x did:0x%04x driver:%s iommu_group:%d", - di->vendor_id, di->device_id, di->driver_name, - di->iommu_group); + log_debug (p, "open vid:0x%04x did:0x%04x driver:%s iommu_group:%d", + di->vendor_id, di->device_id, di->driver_name, di->iommu_group); if (clib_strncmp ("vfio-pci", (char *) di->driver_name, 8) == 0) err = add_device_vfio (vm, p, di, 0); @@ -1358,7 +1400,7 @@ error: vlib_pci_free_device_info (di); if (err) { - pci_log_err (vm, p, "%U", format_clib_error, err); + log_err (p, "%U", format_clib_error, err); clib_memset (p, 0, sizeof (linux_pci_device_t)); pool_put (lpm->linux_pci_devices, p); }