diff options
-rw-r--r-- | drivers/pci/iov.c | 87 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 172 | ||||
-rw-r--r-- | drivers/pci/pci.h | 8 | ||||
-rw-r--r-- | drivers/pci/probe.c | 1 | ||||
-rw-r--r-- | include/linux/pci.h | 11 |
5 files changed, 245 insertions, 34 deletions
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index aeccc911abb..bafd2bbcaf6 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -106,7 +106,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) virtfn->resource[i].name = pci_name(virtfn); virtfn->resource[i].flags = res->flags; size = resource_size(res); - do_div(size, iov->total); + do_div(size, iov->total_VFs); virtfn->resource[i].start = res->start + size * id; virtfn->resource[i].end = virtfn->resource[i].start + size - 1; rc = request_resource(res, &virtfn->resource[i]); @@ -194,7 +194,7 @@ static int sriov_migration(struct pci_dev *dev) u16 status; struct pci_sriov *iov = dev->sriov; - if (!iov->nr_virtfn) + if (!iov->num_VFs) return 0; if (!(iov->cap & PCI_SRIOV_CAP_VFM)) @@ -216,7 +216,7 @@ static void sriov_migration_task(struct work_struct *work) u16 status; struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); - for (i = iov->initial; i < iov->nr_virtfn; i++) { + for (i = iov->initial_VFs; i < iov->num_VFs; i++) { state = readb(iov->mstate + i); if (state == PCI_SRIOV_VFM_MI) { writeb(PCI_SRIOV_VFM_AV, iov->mstate + i); @@ -244,7 +244,7 @@ static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn) resource_size_t pa; struct pci_sriov *iov = dev->sriov; - if (nr_virtfn <= iov->initial) + if (nr_virtfn <= iov->initial_VFs) return 0; pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table); @@ -294,15 +294,15 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) if (!nr_virtfn) return 0; - if (iov->nr_virtfn) + if (iov->num_VFs) return -EINVAL; pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); - if (initial > iov->total || - (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total))) + if (initial > iov->total_VFs || + (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs))) return -EIO; - if (nr_virtfn < 0 || nr_virtfn > iov->total || + if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs || (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) return -EINVAL; @@ -359,7 +359,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) msleep(100); pci_cfg_access_unlock(dev); - iov->initial = initial; + iov->initial_VFs = initial; if (nr_virtfn < initial) initial = nr_virtfn; @@ -376,7 +376,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) } kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); - iov->nr_virtfn = nr_virtfn; + iov->num_VFs = nr_virtfn; return 0; @@ -401,13 +401,13 @@ static void sriov_disable(struct pci_dev *dev) int i; struct pci_sriov *iov = dev->sriov; - if (!iov->nr_virtfn) + if (!iov->num_VFs) return; if (iov->cap & PCI_SRIOV_CAP_VFM) sriov_disable_migration(dev); - for (i = 0; i < iov->nr_virtfn; i++) + for (i = 0; i < iov->num_VFs; i++) virtfn_remove(dev, i, 0); iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); @@ -419,7 +419,7 @@ static void sriov_disable(struct pci_dev *dev) if (iov->link != dev->devfn) sysfs_remove_link(&dev->dev.kobj, "dep_link"); - iov->nr_virtfn = 0; + iov->num_VFs = 0; } static int sriov_init(struct pci_dev *dev, int pos) @@ -496,7 +496,7 @@ found: iov->pos = pos; iov->nres = nres; iov->ctrl = ctrl; - iov->total = total; + iov->total_VFs = total; iov->offset = offset; iov->stride = stride; iov->pgsz = pgsz; @@ -529,7 +529,7 @@ failed: static void sriov_release(struct pci_dev *dev) { - BUG_ON(dev->sriov->nr_virtfn); + BUG_ON(dev->sriov->num_VFs); if (dev != dev->sriov->dev) pci_dev_put(dev->sriov->dev); @@ -554,7 +554,7 @@ static void sriov_restore_state(struct pci_dev *dev) pci_update_resource(dev, i); pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); - pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn); + pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs); pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); if (iov->ctrl & PCI_SRIOV_CTRL_VFE) msleep(100); @@ -661,7 +661,7 @@ int pci_iov_bus_range(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { if (!dev->is_physfn) continue; - busnr = virtfn_bus(dev, dev->sriov->total - 1); + busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1); if (busnr > max) max = busnr; } @@ -729,9 +729,56 @@ EXPORT_SYMBOL_GPL(pci_sriov_migration); */ int pci_num_vf(struct pci_dev *dev) { - if (!dev || !dev->is_physfn) + if (!dev->is_physfn) return 0; - else - return dev->sriov->nr_virtfn; + + return dev->sriov->num_VFs; } EXPORT_SYMBOL_GPL(pci_num_vf); + +/** + * pci_sriov_set_totalvfs -- reduce the TotalVFs available + * @dev: the PCI PF device + * numvfs: number that should be used for TotalVFs supported + * + * Should be called from PF driver's probe routine with + * device's mutex held. + * + * Returns 0 if PF is an SRIOV-capable device and + * value of numvfs valid. If not a PF with VFS, return -EINVAL; + * if VFs already enabled, return -EBUSY. + */ +int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) +{ + if (!dev->is_physfn || (numvfs > dev->sriov->total_VFs)) + return -EINVAL; + + /* Shouldn't change if VFs already enabled */ + if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE) + return -EBUSY; + else + dev->sriov->driver_max_VFs = numvfs; + + return 0; +} +EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs); + +/** + * pci_sriov_get_totalvfs -- get total VFs supported on this devic3 + * @dev: the PCI PF device + * + * For a PCIe device with SRIOV support, return the PCIe + * SRIOV capability value of TotalVFs or the value of driver_max_VFs + * if the driver reduced it. Otherwise, -EINVAL. + */ +int pci_sriov_get_totalvfs(struct pci_dev *dev) +{ + if (!dev->is_physfn) + return -EINVAL; + + if (dev->sriov->driver_max_VFs) + return dev->sriov->driver_max_VFs; + + return dev->sriov->total_VFs; +} +EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 02d107b1528..2f21991b8bc 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -404,6 +404,106 @@ static ssize_t d3cold_allowed_show(struct device *dev, } #endif +#ifdef CONFIG_PCI_IOV +static ssize_t sriov_totalvfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev)); +} + + +static ssize_t sriov_numvfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%u\n", pdev->sriov->num_VFs); +} + +/* + * num_vfs > 0; number of vfs to enable + * num_vfs = 0; disable all vfs + * + * Note: SRIOV spec doesn't allow partial VF + * disable, so its all or none. + */ +static ssize_t sriov_numvfs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int num_vfs_enabled = 0; + int num_vfs; + int ret = 0; + u16 total; + + if (kstrtoint(buf, 0, &num_vfs) < 0) + return -EINVAL; + + /* is PF driver loaded w/callback */ + if (!pdev->driver || !pdev->driver->sriov_configure) { + dev_info(&pdev->dev, + "Driver doesn't support SRIOV configuration via sysfs\n"); + return -ENOSYS; + } + + /* if enabling vf's ... */ + total = pci_sriov_get_totalvfs(pdev); + /* Requested VFs to enable < totalvfs and none enabled already */ + if ((num_vfs > 0) && (num_vfs <= total)) { + if (pdev->sriov->num_VFs == 0) { + num_vfs_enabled = + pdev->driver->sriov_configure(pdev, num_vfs); + if ((num_vfs_enabled >= 0) && + (num_vfs_enabled != num_vfs)) { + dev_warn(&pdev->dev, + "Only %d VFs enabled\n", + num_vfs_enabled); + return count; + } else if (num_vfs_enabled < 0) + /* error code from driver callback */ + return num_vfs_enabled; + } else if (num_vfs == pdev->sriov->num_VFs) { + dev_warn(&pdev->dev, + "%d VFs already enabled; no enable action taken\n", + num_vfs); + return count; + } else { + dev_warn(&pdev->dev, + "%d VFs already enabled. Disable before enabling %d VFs\n", + pdev->sriov->num_VFs, num_vfs); + return -EINVAL; + } + } + + /* disable vfs */ + if (num_vfs == 0) { + if (pdev->sriov->num_VFs != 0) { + ret = pdev->driver->sriov_configure(pdev, 0); + return ret ? ret : count; + } else { + dev_warn(&pdev->dev, + "All VFs disabled; no disable action taken\n"); + return count; + } + } + + dev_err(&pdev->dev, + "Invalid value for number of VFs to enable: %d\n", num_vfs); + + return -EINVAL; +} + +static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs); +static struct device_attribute sriov_numvfs_attr = + __ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP), + sriov_numvfs_show, sriov_numvfs_store); +#endif /* CONFIG_PCI_IOV */ + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), @@ -1303,29 +1403,20 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) pdev->rom_attr = attr; } - if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { - retval = device_create_file(&pdev->dev, &vga_attr); - if (retval) - goto err_rom_file; - } - /* add platform-specific attributes */ retval = pcibios_add_platform_entries(pdev); if (retval) - goto err_vga_file; + goto err_rom_file; /* add sysfs entries for various capabilities */ retval = pci_create_capabilities_sysfs(pdev); if (retval) - goto err_vga_file; + goto err_rom_file; pci_create_firmware_label_files(pdev); return 0; -err_vga_file: - if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) - device_remove_file(&pdev->dev, &vga_attr); err_rom_file: if (rom_size) { sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); @@ -1411,3 +1502,62 @@ static int __init pci_sysfs_init(void) } late_initcall(pci_sysfs_init); + +static struct attribute *pci_dev_dev_attrs[] = { + &vga_attr.attr, + NULL, +}; + +static umode_t pci_dev_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + if (a == &vga_attr.attr) + if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA) + return 0; + + return a->mode; +} + +#ifdef CONFIG_PCI_IOV +static struct attribute *sriov_dev_attrs[] = { + &sriov_totalvfs_attr.attr, + &sriov_numvfs_attr.attr, + NULL, +}; + +static umode_t sriov_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + if (!dev_is_pf(dev)) + return 0; + + return a->mode; +} + +static struct attribute_group sriov_dev_attr_group = { + .attrs = sriov_dev_attrs, + .is_visible = sriov_attrs_are_visible, +}; +#endif /* CONFIG_PCI_IOV */ + +static struct attribute_group pci_dev_attr_group = { + .attrs = pci_dev_dev_attrs, + .is_visible = pci_dev_attrs_are_visible, +}; + +static const struct attribute_group *pci_dev_attr_groups[] = { + &pci_dev_attr_group, +#ifdef CONFIG_PCI_IOV + &sriov_dev_attr_group, +#endif + NULL, +}; + +struct device_type pci_dev_type = { + .groups = pci_dev_attr_groups, +}; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index bacbcba69cf..3c9443ee00e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -157,6 +157,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev) } extern struct device_attribute pci_dev_attrs[]; extern struct device_attribute pcibus_dev_attrs[]; +extern struct device_type pci_dev_type; #ifdef CONFIG_HOTPLUG extern struct bus_attribute pci_bus_attrs[]; #else @@ -232,13 +233,14 @@ struct pci_sriov { int nres; /* number of resources */ u32 cap; /* SR-IOV Capabilities */ u16 ctrl; /* SR-IOV Control */ - u16 total; /* total VFs associated with the PF */ - u16 initial; /* initial VFs associated with the PF */ - u16 nr_virtfn; /* number of VFs available */ + u16 total_VFs; /* total VFs associated with the PF */ + u16 initial_VFs; /* initial VFs associated with the PF */ + u16 num_VFs; /* number of VFs available */ u16 offset; /* first VF Routing ID offset */ u16 stride; /* following VF stride */ u32 pgsz; /* page size for BAR alignment */ u8 link; /* Function Dependency Link */ + u16 driver_max_VFs; /* max num VFs driver supports */ struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ struct mutex lock; /* lock for VF bus */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 59cf1ba3493..293af5a6f91 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -975,6 +975,7 @@ int pci_setup_device(struct pci_dev *dev) dev->sysdata = dev->bus->sysdata; dev->dev.parent = dev->bus->bridge; dev->dev.bus = &pci_bus_type; + dev->dev.type = &pci_dev_type; dev->hdr_type = hdr_type & 0x7f; dev->multifunction = !!(hdr_type & 0x80); dev->error_state = pci_channel_io_normal; diff --git a/include/linux/pci.h b/include/linux/pci.h index 9253af697ca..5b37d643b6b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -573,6 +573,7 @@ struct pci_driver { int (*resume_early) (struct pci_dev *dev); int (*resume) (struct pci_dev *dev); /* Device woken up */ void (*shutdown) (struct pci_dev *dev); + int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */ const struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; @@ -1613,6 +1614,8 @@ extern int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); extern void pci_disable_sriov(struct pci_dev *dev); extern irqreturn_t pci_sriov_migration(struct pci_dev *dev); extern int pci_num_vf(struct pci_dev *dev); +extern int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); +extern int pci_sriov_get_totalvfs(struct pci_dev *dev); #else static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) { @@ -1629,6 +1632,14 @@ static inline int pci_num_vf(struct pci_dev *dev) { return 0; } +static inline int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) +{ + return 0; +} +static inline int pci_sriov_get_totalvfs(struct pci_dev *dev) +{ + return 0; +} #endif #if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE) |