162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI Express I/O Virtualization (IOV) support 462306a36Sopenharmony_ci * Single Root IOV 1.0 562306a36Sopenharmony_ci * Address Translation Service 1.0 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include "pci.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define VIRTFN_ID_LEN 17 /* "virtfn%u\0" for 2^32 - 1 */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciint pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci if (!dev->is_physfn) 2262306a36Sopenharmony_ci return -EINVAL; 2362306a36Sopenharmony_ci return dev->bus->number + ((dev->devfn + dev->sriov->offset + 2462306a36Sopenharmony_ci dev->sriov->stride * vf_id) >> 8); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciint pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci if (!dev->is_physfn) 3062306a36Sopenharmony_ci return -EINVAL; 3162306a36Sopenharmony_ci return (dev->devfn + dev->sriov->offset + 3262306a36Sopenharmony_ci dev->sriov->stride * vf_id) & 0xff; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_iov_virtfn_devfn); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ciint pci_iov_vf_id(struct pci_dev *dev) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct pci_dev *pf; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (!dev->is_virtfn) 4162306a36Sopenharmony_ci return -EINVAL; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci pf = pci_physfn(dev); 4462306a36Sopenharmony_ci return (pci_dev_id(dev) - (pci_dev_id(pf) + pf->sriov->offset)) / 4562306a36Sopenharmony_ci pf->sriov->stride; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_iov_vf_id); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/** 5062306a36Sopenharmony_ci * pci_iov_get_pf_drvdata - Return the drvdata of a PF 5162306a36Sopenharmony_ci * @dev: VF pci_dev 5262306a36Sopenharmony_ci * @pf_driver: Device driver required to own the PF 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * This must be called from a context that ensures that a VF driver is attached. 5562306a36Sopenharmony_ci * The value returned is invalid once the VF driver completes its remove() 5662306a36Sopenharmony_ci * callback. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * Locking is achieved by the driver core. A VF driver cannot be probed until 5962306a36Sopenharmony_ci * pci_enable_sriov() is called and pci_disable_sriov() does not return until 6062306a36Sopenharmony_ci * all VF drivers have completed their remove(). 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * The PF driver must call pci_disable_sriov() before it begins to destroy the 6362306a36Sopenharmony_ci * drvdata. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_civoid *pci_iov_get_pf_drvdata(struct pci_dev *dev, struct pci_driver *pf_driver) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct pci_dev *pf_dev; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (!dev->is_virtfn) 7062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 7162306a36Sopenharmony_ci pf_dev = dev->physfn; 7262306a36Sopenharmony_ci if (pf_dev->driver != pf_driver) 7362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 7462306a36Sopenharmony_ci return pci_get_drvdata(pf_dev); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_iov_get_pf_drvdata); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Per SR-IOV spec sec 3.3.10 and 3.3.11, First VF Offset and VF Stride may 8062306a36Sopenharmony_ci * change when NumVFs changes. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Update iov->offset and iov->stride when NumVFs is written. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic inline void pci_iov_set_numvfs(struct pci_dev *dev, int nr_virtfn) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct pci_sriov *iov = dev->sriov; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn); 8962306a36Sopenharmony_ci pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &iov->offset); 9062306a36Sopenharmony_ci pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &iov->stride); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * The PF consumes one bus number. NumVFs, First VF Offset, and VF Stride 9562306a36Sopenharmony_ci * determine how many additional bus numbers will be consumed by VFs. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * Iterate over all valid NumVFs, validate offset and stride, and calculate 9862306a36Sopenharmony_ci * the maximum number of bus numbers that could ever be required. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistatic int compute_max_vf_buses(struct pci_dev *dev) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct pci_sriov *iov = dev->sriov; 10362306a36Sopenharmony_ci int nr_virtfn, busnr, rc = 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci for (nr_virtfn = iov->total_VFs; nr_virtfn; nr_virtfn--) { 10662306a36Sopenharmony_ci pci_iov_set_numvfs(dev, nr_virtfn); 10762306a36Sopenharmony_ci if (!iov->offset || (nr_virtfn > 1 && !iov->stride)) { 10862306a36Sopenharmony_ci rc = -EIO; 10962306a36Sopenharmony_ci goto out; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci busnr = pci_iov_virtfn_bus(dev, nr_virtfn - 1); 11362306a36Sopenharmony_ci if (busnr > iov->max_VF_buses) 11462306a36Sopenharmony_ci iov->max_VF_buses = busnr; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciout: 11862306a36Sopenharmony_ci pci_iov_set_numvfs(dev, 0); 11962306a36Sopenharmony_ci return rc; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct pci_bus *child; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (bus->number == busnr) 12762306a36Sopenharmony_ci return bus; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci child = pci_find_bus(pci_domain_nr(bus), busnr); 13062306a36Sopenharmony_ci if (child) 13162306a36Sopenharmony_ci return child; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci child = pci_add_new_bus(bus, NULL, busnr); 13462306a36Sopenharmony_ci if (!child) 13562306a36Sopenharmony_ci return NULL; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci pci_bus_insert_busn_res(child, busnr, busnr); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return child; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci if (physbus != virtbus && list_empty(&virtbus->devices)) 14562306a36Sopenharmony_ci pci_remove_bus(virtbus); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciresource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci if (!dev->is_physfn) 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return dev->sriov->barsz[resno - PCI_IOV_RESOURCES]; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void pci_read_vf_config_common(struct pci_dev *virtfn) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct pci_dev *physfn = virtfn->physfn; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * Some config registers are the same across all associated VFs. 16262306a36Sopenharmony_ci * Read them once from VF0 so we can skip reading them from the 16362306a36Sopenharmony_ci * other VFs. 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to 16662306a36Sopenharmony_ci * have the same Revision ID and Subsystem ID, but we assume they 16762306a36Sopenharmony_ci * do. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci pci_read_config_dword(virtfn, PCI_CLASS_REVISION, 17062306a36Sopenharmony_ci &physfn->sriov->class); 17162306a36Sopenharmony_ci pci_read_config_byte(virtfn, PCI_HEADER_TYPE, 17262306a36Sopenharmony_ci &physfn->sriov->hdr_type); 17362306a36Sopenharmony_ci pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID, 17462306a36Sopenharmony_ci &physfn->sriov->subsystem_vendor); 17562306a36Sopenharmony_ci pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID, 17662306a36Sopenharmony_ci &physfn->sriov->subsystem_device); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciint pci_iov_sysfs_link(struct pci_dev *dev, 18062306a36Sopenharmony_ci struct pci_dev *virtfn, int id) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci char buf[VIRTFN_ID_LEN]; 18362306a36Sopenharmony_ci int rc; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci sprintf(buf, "virtfn%u", id); 18662306a36Sopenharmony_ci rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); 18762306a36Sopenharmony_ci if (rc) 18862306a36Sopenharmony_ci goto failed; 18962306a36Sopenharmony_ci rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); 19062306a36Sopenharmony_ci if (rc) 19162306a36Sopenharmony_ci goto failed1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cifailed1: 19862306a36Sopenharmony_ci sysfs_remove_link(&dev->dev.kobj, buf); 19962306a36Sopenharmony_cifailed: 20062306a36Sopenharmony_ci return rc; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 20462306a36Sopenharmony_cistatic ssize_t sriov_vf_total_msix_show(struct device *dev, 20562306a36Sopenharmony_ci struct device_attribute *attr, 20662306a36Sopenharmony_ci char *buf) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 20962306a36Sopenharmony_ci u32 vf_total_msix = 0; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci device_lock(dev); 21262306a36Sopenharmony_ci if (!pdev->driver || !pdev->driver->sriov_get_vf_total_msix) 21362306a36Sopenharmony_ci goto unlock; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci vf_total_msix = pdev->driver->sriov_get_vf_total_msix(pdev); 21662306a36Sopenharmony_ciunlock: 21762306a36Sopenharmony_ci device_unlock(dev); 21862306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", vf_total_msix); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_vf_total_msix); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic ssize_t sriov_vf_msix_count_store(struct device *dev, 22362306a36Sopenharmony_ci struct device_attribute *attr, 22462306a36Sopenharmony_ci const char *buf, size_t count) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct pci_dev *vf_dev = to_pci_dev(dev); 22762306a36Sopenharmony_ci struct pci_dev *pdev = pci_physfn(vf_dev); 22862306a36Sopenharmony_ci int val, ret = 0; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (kstrtoint(buf, 0, &val) < 0) 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (val < 0) 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci device_lock(&pdev->dev); 23762306a36Sopenharmony_ci if (!pdev->driver || !pdev->driver->sriov_set_msix_vec_count) { 23862306a36Sopenharmony_ci ret = -EOPNOTSUPP; 23962306a36Sopenharmony_ci goto err_pdev; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci device_lock(&vf_dev->dev); 24362306a36Sopenharmony_ci if (vf_dev->driver) { 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * A driver is already attached to this VF and has configured 24662306a36Sopenharmony_ci * itself based on the current MSI-X vector count. Changing 24762306a36Sopenharmony_ci * the vector size could mess up the driver, so block it. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci ret = -EBUSY; 25062306a36Sopenharmony_ci goto err_dev; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ret = pdev->driver->sriov_set_msix_vec_count(vf_dev, val); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cierr_dev: 25662306a36Sopenharmony_ci device_unlock(&vf_dev->dev); 25762306a36Sopenharmony_cierr_pdev: 25862306a36Sopenharmony_ci device_unlock(&pdev->dev); 25962306a36Sopenharmony_ci return ret ? : count; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_cistatic DEVICE_ATTR_WO(sriov_vf_msix_count); 26262306a36Sopenharmony_ci#endif 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic struct attribute *sriov_vf_dev_attrs[] = { 26562306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 26662306a36Sopenharmony_ci &dev_attr_sriov_vf_msix_count.attr, 26762306a36Sopenharmony_ci#endif 26862306a36Sopenharmony_ci NULL, 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic umode_t sriov_vf_attrs_are_visible(struct kobject *kobj, 27262306a36Sopenharmony_ci struct attribute *a, int n) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 27562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!pdev->is_virtfn) 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return a->mode; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciconst struct attribute_group sriov_vf_dev_attr_group = { 28462306a36Sopenharmony_ci .attrs = sriov_vf_dev_attrs, 28562306a36Sopenharmony_ci .is_visible = sriov_vf_attrs_are_visible, 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciint pci_iov_add_virtfn(struct pci_dev *dev, int id) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci int i; 29162306a36Sopenharmony_ci int rc = -ENOMEM; 29262306a36Sopenharmony_ci u64 size; 29362306a36Sopenharmony_ci struct pci_dev *virtfn; 29462306a36Sopenharmony_ci struct resource *res; 29562306a36Sopenharmony_ci struct pci_sriov *iov = dev->sriov; 29662306a36Sopenharmony_ci struct pci_bus *bus; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id)); 29962306a36Sopenharmony_ci if (!bus) 30062306a36Sopenharmony_ci goto failed; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci virtfn = pci_alloc_dev(bus); 30362306a36Sopenharmony_ci if (!virtfn) 30462306a36Sopenharmony_ci goto failed0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci virtfn->devfn = pci_iov_virtfn_devfn(dev, id); 30762306a36Sopenharmony_ci virtfn->vendor = dev->vendor; 30862306a36Sopenharmony_ci virtfn->device = iov->vf_device; 30962306a36Sopenharmony_ci virtfn->is_virtfn = 1; 31062306a36Sopenharmony_ci virtfn->physfn = pci_dev_get(dev); 31162306a36Sopenharmony_ci virtfn->no_command_memory = 1; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (id == 0) 31462306a36Sopenharmony_ci pci_read_vf_config_common(virtfn); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci rc = pci_setup_device(virtfn); 31762306a36Sopenharmony_ci if (rc) 31862306a36Sopenharmony_ci goto failed1; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci virtfn->dev.parent = dev->dev.parent; 32162306a36Sopenharmony_ci virtfn->multifunction = 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 32462306a36Sopenharmony_ci res = &dev->resource[i + PCI_IOV_RESOURCES]; 32562306a36Sopenharmony_ci if (!res->parent) 32662306a36Sopenharmony_ci continue; 32762306a36Sopenharmony_ci virtfn->resource[i].name = pci_name(virtfn); 32862306a36Sopenharmony_ci virtfn->resource[i].flags = res->flags; 32962306a36Sopenharmony_ci size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); 33062306a36Sopenharmony_ci virtfn->resource[i].start = res->start + size * id; 33162306a36Sopenharmony_ci virtfn->resource[i].end = virtfn->resource[i].start + size - 1; 33262306a36Sopenharmony_ci rc = request_resource(res, &virtfn->resource[i]); 33362306a36Sopenharmony_ci BUG_ON(rc); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci pci_device_add(virtfn, virtfn->bus); 33762306a36Sopenharmony_ci rc = pci_iov_sysfs_link(dev, virtfn, id); 33862306a36Sopenharmony_ci if (rc) 33962306a36Sopenharmony_ci goto failed1; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci pci_bus_add_device(virtfn); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cifailed1: 34662306a36Sopenharmony_ci pci_stop_and_remove_bus_device(virtfn); 34762306a36Sopenharmony_ci pci_dev_put(dev); 34862306a36Sopenharmony_cifailed0: 34962306a36Sopenharmony_ci virtfn_remove_bus(dev->bus, bus); 35062306a36Sopenharmony_cifailed: 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return rc; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_civoid pci_iov_remove_virtfn(struct pci_dev *dev, int id) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci char buf[VIRTFN_ID_LEN]; 35862306a36Sopenharmony_ci struct pci_dev *virtfn; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), 36162306a36Sopenharmony_ci pci_iov_virtfn_bus(dev, id), 36262306a36Sopenharmony_ci pci_iov_virtfn_devfn(dev, id)); 36362306a36Sopenharmony_ci if (!virtfn) 36462306a36Sopenharmony_ci return; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci sprintf(buf, "virtfn%u", id); 36762306a36Sopenharmony_ci sysfs_remove_link(&dev->dev.kobj, buf); 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * pci_stop_dev() could have been called for this virtfn already, 37062306a36Sopenharmony_ci * so the directory for the virtfn may have been removed before. 37162306a36Sopenharmony_ci * Double check to avoid spurious sysfs warnings. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci if (virtfn->dev.kobj.sd) 37462306a36Sopenharmony_ci sysfs_remove_link(&virtfn->dev.kobj, "physfn"); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci pci_stop_and_remove_bus_device(virtfn); 37762306a36Sopenharmony_ci virtfn_remove_bus(dev->bus, virtfn->bus); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* balance pci_get_domain_bus_and_slot() */ 38062306a36Sopenharmony_ci pci_dev_put(virtfn); 38162306a36Sopenharmony_ci pci_dev_put(dev); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic ssize_t sriov_totalvfs_show(struct device *dev, 38562306a36Sopenharmony_ci struct device_attribute *attr, 38662306a36Sopenharmony_ci char *buf) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pci_sriov_get_totalvfs(pdev)); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic ssize_t sriov_numvfs_show(struct device *dev, 39462306a36Sopenharmony_ci struct device_attribute *attr, 39562306a36Sopenharmony_ci char *buf) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 39862306a36Sopenharmony_ci u16 num_vfs; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Serialize vs sriov_numvfs_store() so readers see valid num_VFs */ 40162306a36Sopenharmony_ci device_lock(&pdev->dev); 40262306a36Sopenharmony_ci num_vfs = pdev->sriov->num_VFs; 40362306a36Sopenharmony_ci device_unlock(&pdev->dev); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", num_vfs); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* 40962306a36Sopenharmony_ci * num_vfs > 0; number of VFs to enable 41062306a36Sopenharmony_ci * num_vfs = 0; disable all VFs 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * Note: SRIOV spec does not allow partial VF 41362306a36Sopenharmony_ci * disable, so it's all or none. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_cistatic ssize_t sriov_numvfs_store(struct device *dev, 41662306a36Sopenharmony_ci struct device_attribute *attr, 41762306a36Sopenharmony_ci const char *buf, size_t count) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 42062306a36Sopenharmony_ci int ret = 0; 42162306a36Sopenharmony_ci u16 num_vfs; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (kstrtou16(buf, 0, &num_vfs) < 0) 42462306a36Sopenharmony_ci return -EINVAL; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (num_vfs > pci_sriov_get_totalvfs(pdev)) 42762306a36Sopenharmony_ci return -ERANGE; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci device_lock(&pdev->dev); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (num_vfs == pdev->sriov->num_VFs) 43262306a36Sopenharmony_ci goto exit; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* is PF driver loaded */ 43562306a36Sopenharmony_ci if (!pdev->driver) { 43662306a36Sopenharmony_ci pci_info(pdev, "no driver bound to device; cannot configure SR-IOV\n"); 43762306a36Sopenharmony_ci ret = -ENOENT; 43862306a36Sopenharmony_ci goto exit; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* is PF driver loaded w/callback */ 44262306a36Sopenharmony_ci if (!pdev->driver->sriov_configure) { 44362306a36Sopenharmony_ci pci_info(pdev, "driver does not support SR-IOV configuration via sysfs\n"); 44462306a36Sopenharmony_ci ret = -ENOENT; 44562306a36Sopenharmony_ci goto exit; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (num_vfs == 0) { 44962306a36Sopenharmony_ci /* disable VFs */ 45062306a36Sopenharmony_ci ret = pdev->driver->sriov_configure(pdev, 0); 45162306a36Sopenharmony_ci goto exit; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* enable VFs */ 45562306a36Sopenharmony_ci if (pdev->sriov->num_VFs) { 45662306a36Sopenharmony_ci pci_warn(pdev, "%d VFs already enabled. Disable before enabling %d VFs\n", 45762306a36Sopenharmony_ci pdev->sriov->num_VFs, num_vfs); 45862306a36Sopenharmony_ci ret = -EBUSY; 45962306a36Sopenharmony_ci goto exit; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ret = pdev->driver->sriov_configure(pdev, num_vfs); 46362306a36Sopenharmony_ci if (ret < 0) 46462306a36Sopenharmony_ci goto exit; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (ret != num_vfs) 46762306a36Sopenharmony_ci pci_warn(pdev, "%d VFs requested; only %d enabled\n", 46862306a36Sopenharmony_ci num_vfs, ret); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ciexit: 47162306a36Sopenharmony_ci device_unlock(&pdev->dev); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (ret < 0) 47462306a36Sopenharmony_ci return ret; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return count; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic ssize_t sriov_offset_show(struct device *dev, 48062306a36Sopenharmony_ci struct device_attribute *attr, 48162306a36Sopenharmony_ci char *buf) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pdev->sriov->offset); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic ssize_t sriov_stride_show(struct device *dev, 48962306a36Sopenharmony_ci struct device_attribute *attr, 49062306a36Sopenharmony_ci char *buf) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pdev->sriov->stride); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic ssize_t sriov_vf_device_show(struct device *dev, 49862306a36Sopenharmony_ci struct device_attribute *attr, 49962306a36Sopenharmony_ci char *buf) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return sysfs_emit(buf, "%x\n", pdev->sriov->vf_device); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic ssize_t sriov_drivers_autoprobe_show(struct device *dev, 50762306a36Sopenharmony_ci struct device_attribute *attr, 50862306a36Sopenharmony_ci char *buf) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pdev->sriov->drivers_autoprobe); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic ssize_t sriov_drivers_autoprobe_store(struct device *dev, 51662306a36Sopenharmony_ci struct device_attribute *attr, 51762306a36Sopenharmony_ci const char *buf, size_t count) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 52062306a36Sopenharmony_ci bool drivers_autoprobe; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (kstrtobool(buf, &drivers_autoprobe) < 0) 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci pdev->sriov->drivers_autoprobe = drivers_autoprobe; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return count; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_totalvfs); 53162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sriov_numvfs); 53262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_offset); 53362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_stride); 53462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sriov_vf_device); 53562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sriov_drivers_autoprobe); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic struct attribute *sriov_pf_dev_attrs[] = { 53862306a36Sopenharmony_ci &dev_attr_sriov_totalvfs.attr, 53962306a36Sopenharmony_ci &dev_attr_sriov_numvfs.attr, 54062306a36Sopenharmony_ci &dev_attr_sriov_offset.attr, 54162306a36Sopenharmony_ci &dev_attr_sriov_stride.attr, 54262306a36Sopenharmony_ci &dev_attr_sriov_vf_device.attr, 54362306a36Sopenharmony_ci &dev_attr_sriov_drivers_autoprobe.attr, 54462306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 54562306a36Sopenharmony_ci &dev_attr_sriov_vf_total_msix.attr, 54662306a36Sopenharmony_ci#endif 54762306a36Sopenharmony_ci NULL, 54862306a36Sopenharmony_ci}; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic umode_t sriov_pf_attrs_are_visible(struct kobject *kobj, 55162306a36Sopenharmony_ci struct attribute *a, int n) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (!dev_is_pf(dev)) 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return a->mode; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ciconst struct attribute_group sriov_pf_dev_attr_group = { 56262306a36Sopenharmony_ci .attrs = sriov_pf_dev_attrs, 56362306a36Sopenharmony_ci .is_visible = sriov_pf_attrs_are_visible, 56462306a36Sopenharmony_ci}; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ciint __weak pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ciint __weak pcibios_sriov_disable(struct pci_dev *pdev) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci unsigned int i; 57962306a36Sopenharmony_ci int rc; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (dev->no_vf_scan) 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (i = 0; i < num_vfs; i++) { 58562306a36Sopenharmony_ci rc = pci_iov_add_virtfn(dev, i); 58662306a36Sopenharmony_ci if (rc) 58762306a36Sopenharmony_ci goto failed; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_cifailed: 59162306a36Sopenharmony_ci while (i--) 59262306a36Sopenharmony_ci pci_iov_remove_virtfn(dev, i); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return rc; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic int sriov_enable(struct pci_dev *dev, int nr_virtfn) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci int rc; 60062306a36Sopenharmony_ci int i; 60162306a36Sopenharmony_ci int nres; 60262306a36Sopenharmony_ci u16 initial; 60362306a36Sopenharmony_ci struct resource *res; 60462306a36Sopenharmony_ci struct pci_dev *pdev; 60562306a36Sopenharmony_ci struct pci_sriov *iov = dev->sriov; 60662306a36Sopenharmony_ci int bars = 0; 60762306a36Sopenharmony_ci int bus; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!nr_virtfn) 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (iov->num_VFs) 61362306a36Sopenharmony_ci return -EINVAL; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); 61662306a36Sopenharmony_ci if (initial > iov->total_VFs || 61762306a36Sopenharmony_ci (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs))) 61862306a36Sopenharmony_ci return -EIO; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs || 62162306a36Sopenharmony_ci (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) 62262306a36Sopenharmony_ci return -EINVAL; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci nres = 0; 62562306a36Sopenharmony_ci for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 62662306a36Sopenharmony_ci bars |= (1 << (i + PCI_IOV_RESOURCES)); 62762306a36Sopenharmony_ci res = &dev->resource[i + PCI_IOV_RESOURCES]; 62862306a36Sopenharmony_ci if (res->parent) 62962306a36Sopenharmony_ci nres++; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci if (nres != iov->nres) { 63262306a36Sopenharmony_ci pci_err(dev, "not enough MMIO resources for SR-IOV\n"); 63362306a36Sopenharmony_ci return -ENOMEM; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci bus = pci_iov_virtfn_bus(dev, nr_virtfn - 1); 63762306a36Sopenharmony_ci if (bus > dev->bus->busn_res.end) { 63862306a36Sopenharmony_ci pci_err(dev, "can't enable %d VFs (bus %02x out of range of %pR)\n", 63962306a36Sopenharmony_ci nr_virtfn, bus, &dev->bus->busn_res); 64062306a36Sopenharmony_ci return -ENOMEM; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (pci_enable_resources(dev, bars)) { 64462306a36Sopenharmony_ci pci_err(dev, "SR-IOV: IOV BARS not allocated\n"); 64562306a36Sopenharmony_ci return -ENOMEM; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (iov->link != dev->devfn) { 64962306a36Sopenharmony_ci pdev = pci_get_slot(dev->bus, iov->link); 65062306a36Sopenharmony_ci if (!pdev) 65162306a36Sopenharmony_ci return -ENODEV; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (!pdev->is_physfn) { 65462306a36Sopenharmony_ci pci_dev_put(pdev); 65562306a36Sopenharmony_ci return -ENOSYS; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci rc = sysfs_create_link(&dev->dev.kobj, 65962306a36Sopenharmony_ci &pdev->dev.kobj, "dep_link"); 66062306a36Sopenharmony_ci pci_dev_put(pdev); 66162306a36Sopenharmony_ci if (rc) 66262306a36Sopenharmony_ci return rc; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci iov->initial_VFs = initial; 66662306a36Sopenharmony_ci if (nr_virtfn < initial) 66762306a36Sopenharmony_ci initial = nr_virtfn; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci rc = pcibios_sriov_enable(dev, initial); 67062306a36Sopenharmony_ci if (rc) { 67162306a36Sopenharmony_ci pci_err(dev, "failure %d from pcibios_sriov_enable()\n", rc); 67262306a36Sopenharmony_ci goto err_pcibios; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci pci_iov_set_numvfs(dev, nr_virtfn); 67662306a36Sopenharmony_ci iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; 67762306a36Sopenharmony_ci pci_cfg_access_lock(dev); 67862306a36Sopenharmony_ci pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 67962306a36Sopenharmony_ci msleep(100); 68062306a36Sopenharmony_ci pci_cfg_access_unlock(dev); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci rc = sriov_add_vfs(dev, initial); 68362306a36Sopenharmony_ci if (rc) 68462306a36Sopenharmony_ci goto err_pcibios; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); 68762306a36Sopenharmony_ci iov->num_VFs = nr_virtfn; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cierr_pcibios: 69262306a36Sopenharmony_ci iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 69362306a36Sopenharmony_ci pci_cfg_access_lock(dev); 69462306a36Sopenharmony_ci pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 69562306a36Sopenharmony_ci ssleep(1); 69662306a36Sopenharmony_ci pci_cfg_access_unlock(dev); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci pcibios_sriov_disable(dev); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (iov->link != dev->devfn) 70162306a36Sopenharmony_ci sysfs_remove_link(&dev->dev.kobj, "dep_link"); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci pci_iov_set_numvfs(dev, 0); 70462306a36Sopenharmony_ci return rc; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic void sriov_del_vfs(struct pci_dev *dev) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct pci_sriov *iov = dev->sriov; 71062306a36Sopenharmony_ci int i; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci for (i = 0; i < iov->num_VFs; i++) 71362306a36Sopenharmony_ci pci_iov_remove_virtfn(dev, i); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void sriov_disable(struct pci_dev *dev) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct pci_sriov *iov = dev->sriov; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!iov->num_VFs) 72162306a36Sopenharmony_ci return; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci sriov_del_vfs(dev); 72462306a36Sopenharmony_ci iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 72562306a36Sopenharmony_ci pci_cfg_access_lock(dev); 72662306a36Sopenharmony_ci pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 72762306a36Sopenharmony_ci ssleep(1); 72862306a36Sopenharmony_ci pci_cfg_access_unlock(dev); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci pcibios_sriov_disable(dev); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (iov->link != dev->devfn) 73362306a36Sopenharmony_ci sysfs_remove_link(&dev->dev.kobj, "dep_link"); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci iov->num_VFs = 0; 73662306a36Sopenharmony_ci pci_iov_set_numvfs(dev, 0); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int sriov_init(struct pci_dev *dev, int pos) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci int i, bar64; 74262306a36Sopenharmony_ci int rc; 74362306a36Sopenharmony_ci int nres; 74462306a36Sopenharmony_ci u32 pgsz; 74562306a36Sopenharmony_ci u16 ctrl, total; 74662306a36Sopenharmony_ci struct pci_sriov *iov; 74762306a36Sopenharmony_ci struct resource *res; 74862306a36Sopenharmony_ci struct pci_dev *pdev; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); 75162306a36Sopenharmony_ci if (ctrl & PCI_SRIOV_CTRL_VFE) { 75262306a36Sopenharmony_ci pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); 75362306a36Sopenharmony_ci ssleep(1); 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci ctrl = 0; 75762306a36Sopenharmony_ci list_for_each_entry(pdev, &dev->bus->devices, bus_list) 75862306a36Sopenharmony_ci if (pdev->is_physfn) 75962306a36Sopenharmony_ci goto found; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci pdev = NULL; 76262306a36Sopenharmony_ci if (pci_ari_enabled(dev->bus)) 76362306a36Sopenharmony_ci ctrl |= PCI_SRIOV_CTRL_ARI; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cifound: 76662306a36Sopenharmony_ci pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); 76962306a36Sopenharmony_ci if (!total) 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); 77362306a36Sopenharmony_ci i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; 77462306a36Sopenharmony_ci pgsz &= ~((1 << i) - 1); 77562306a36Sopenharmony_ci if (!pgsz) 77662306a36Sopenharmony_ci return -EIO; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci pgsz &= ~(pgsz - 1); 77962306a36Sopenharmony_ci pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci iov = kzalloc(sizeof(*iov), GFP_KERNEL); 78262306a36Sopenharmony_ci if (!iov) 78362306a36Sopenharmony_ci return -ENOMEM; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci nres = 0; 78662306a36Sopenharmony_ci for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 78762306a36Sopenharmony_ci res = &dev->resource[i + PCI_IOV_RESOURCES]; 78862306a36Sopenharmony_ci /* 78962306a36Sopenharmony_ci * If it is already FIXED, don't change it, something 79062306a36Sopenharmony_ci * (perhaps EA or header fixups) wants it this way. 79162306a36Sopenharmony_ci */ 79262306a36Sopenharmony_ci if (res->flags & IORESOURCE_PCI_FIXED) 79362306a36Sopenharmony_ci bar64 = (res->flags & IORESOURCE_MEM_64) ? 1 : 0; 79462306a36Sopenharmony_ci else 79562306a36Sopenharmony_ci bar64 = __pci_read_base(dev, pci_bar_unknown, res, 79662306a36Sopenharmony_ci pos + PCI_SRIOV_BAR + i * 4); 79762306a36Sopenharmony_ci if (!res->flags) 79862306a36Sopenharmony_ci continue; 79962306a36Sopenharmony_ci if (resource_size(res) & (PAGE_SIZE - 1)) { 80062306a36Sopenharmony_ci rc = -EIO; 80162306a36Sopenharmony_ci goto failed; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci iov->barsz[i] = resource_size(res); 80462306a36Sopenharmony_ci res->end = res->start + resource_size(res) * total - 1; 80562306a36Sopenharmony_ci pci_info(dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n", 80662306a36Sopenharmony_ci i, res, i, total); 80762306a36Sopenharmony_ci i += bar64; 80862306a36Sopenharmony_ci nres++; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci iov->pos = pos; 81262306a36Sopenharmony_ci iov->nres = nres; 81362306a36Sopenharmony_ci iov->ctrl = ctrl; 81462306a36Sopenharmony_ci iov->total_VFs = total; 81562306a36Sopenharmony_ci iov->driver_max_VFs = total; 81662306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_SRIOV_VF_DID, &iov->vf_device); 81762306a36Sopenharmony_ci iov->pgsz = pgsz; 81862306a36Sopenharmony_ci iov->self = dev; 81962306a36Sopenharmony_ci iov->drivers_autoprobe = true; 82062306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); 82162306a36Sopenharmony_ci pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); 82262306a36Sopenharmony_ci if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) 82362306a36Sopenharmony_ci iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (pdev) 82662306a36Sopenharmony_ci iov->dev = pci_dev_get(pdev); 82762306a36Sopenharmony_ci else 82862306a36Sopenharmony_ci iov->dev = dev; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci dev->sriov = iov; 83162306a36Sopenharmony_ci dev->is_physfn = 1; 83262306a36Sopenharmony_ci rc = compute_max_vf_buses(dev); 83362306a36Sopenharmony_ci if (rc) 83462306a36Sopenharmony_ci goto fail_max_buses; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cifail_max_buses: 83962306a36Sopenharmony_ci dev->sriov = NULL; 84062306a36Sopenharmony_ci dev->is_physfn = 0; 84162306a36Sopenharmony_cifailed: 84262306a36Sopenharmony_ci for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 84362306a36Sopenharmony_ci res = &dev->resource[i + PCI_IOV_RESOURCES]; 84462306a36Sopenharmony_ci res->flags = 0; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci kfree(iov); 84862306a36Sopenharmony_ci return rc; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic void sriov_release(struct pci_dev *dev) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci BUG_ON(dev->sriov->num_VFs); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (dev != dev->sriov->dev) 85662306a36Sopenharmony_ci pci_dev_put(dev->sriov->dev); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci kfree(dev->sriov); 85962306a36Sopenharmony_ci dev->sriov = NULL; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic void sriov_restore_state(struct pci_dev *dev) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci int i; 86562306a36Sopenharmony_ci u16 ctrl; 86662306a36Sopenharmony_ci struct pci_sriov *iov = dev->sriov; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); 86962306a36Sopenharmony_ci if (ctrl & PCI_SRIOV_CTRL_VFE) 87062306a36Sopenharmony_ci return; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* 87362306a36Sopenharmony_ci * Restore PCI_SRIOV_CTRL_ARI before pci_iov_set_numvfs() because 87462306a36Sopenharmony_ci * it reads offset & stride, which depend on PCI_SRIOV_CTRL_ARI. 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ci ctrl &= ~PCI_SRIOV_CTRL_ARI; 87762306a36Sopenharmony_ci ctrl |= iov->ctrl & PCI_SRIOV_CTRL_ARI; 87862306a36Sopenharmony_ci pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, ctrl); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) 88162306a36Sopenharmony_ci pci_update_resource(dev, i + PCI_IOV_RESOURCES); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); 88462306a36Sopenharmony_ci pci_iov_set_numvfs(dev, iov->num_VFs); 88562306a36Sopenharmony_ci pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 88662306a36Sopenharmony_ci if (iov->ctrl & PCI_SRIOV_CTRL_VFE) 88762306a36Sopenharmony_ci msleep(100); 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci/** 89162306a36Sopenharmony_ci * pci_iov_init - initialize the IOV capability 89262306a36Sopenharmony_ci * @dev: the PCI device 89362306a36Sopenharmony_ci * 89462306a36Sopenharmony_ci * Returns 0 on success, or negative on failure. 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_ciint pci_iov_init(struct pci_dev *dev) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci int pos; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (!pci_is_pcie(dev)) 90162306a36Sopenharmony_ci return -ENODEV; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); 90462306a36Sopenharmony_ci if (pos) 90562306a36Sopenharmony_ci return sriov_init(dev, pos); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci return -ENODEV; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci/** 91162306a36Sopenharmony_ci * pci_iov_release - release resources used by the IOV capability 91262306a36Sopenharmony_ci * @dev: the PCI device 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_civoid pci_iov_release(struct pci_dev *dev) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci if (dev->is_physfn) 91762306a36Sopenharmony_ci sriov_release(dev); 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/** 92162306a36Sopenharmony_ci * pci_iov_remove - clean up SR-IOV state after PF driver is detached 92262306a36Sopenharmony_ci * @dev: the PCI device 92362306a36Sopenharmony_ci */ 92462306a36Sopenharmony_civoid pci_iov_remove(struct pci_dev *dev) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct pci_sriov *iov = dev->sriov; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (!dev->is_physfn) 92962306a36Sopenharmony_ci return; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci iov->driver_max_VFs = iov->total_VFs; 93262306a36Sopenharmony_ci if (iov->num_VFs) 93362306a36Sopenharmony_ci pci_warn(dev, "driver left SR-IOV enabled after remove\n"); 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci/** 93762306a36Sopenharmony_ci * pci_iov_update_resource - update a VF BAR 93862306a36Sopenharmony_ci * @dev: the PCI device 93962306a36Sopenharmony_ci * @resno: the resource number 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * Update a VF BAR in the SR-IOV capability of a PF. 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_civoid pci_iov_update_resource(struct pci_dev *dev, int resno) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL; 94662306a36Sopenharmony_ci struct resource *res = dev->resource + resno; 94762306a36Sopenharmony_ci int vf_bar = resno - PCI_IOV_RESOURCES; 94862306a36Sopenharmony_ci struct pci_bus_region region; 94962306a36Sopenharmony_ci u16 cmd; 95062306a36Sopenharmony_ci u32 new; 95162306a36Sopenharmony_ci int reg; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* 95462306a36Sopenharmony_ci * The generic pci_restore_bars() path calls this for all devices, 95562306a36Sopenharmony_ci * including VFs and non-SR-IOV devices. If this is not a PF, we 95662306a36Sopenharmony_ci * have nothing to do. 95762306a36Sopenharmony_ci */ 95862306a36Sopenharmony_ci if (!iov) 95962306a36Sopenharmony_ci return; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &cmd); 96262306a36Sopenharmony_ci if ((cmd & PCI_SRIOV_CTRL_VFE) && (cmd & PCI_SRIOV_CTRL_MSE)) { 96362306a36Sopenharmony_ci dev_WARN(&dev->dev, "can't update enabled VF BAR%d %pR\n", 96462306a36Sopenharmony_ci vf_bar, res); 96562306a36Sopenharmony_ci return; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* 96962306a36Sopenharmony_ci * Ignore unimplemented BARs, unused resource slots for 64-bit 97062306a36Sopenharmony_ci * BARs, and non-movable resources, e.g., those described via 97162306a36Sopenharmony_ci * Enhanced Allocation. 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_ci if (!res->flags) 97462306a36Sopenharmony_ci return; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (res->flags & IORESOURCE_UNSET) 97762306a36Sopenharmony_ci return; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci if (res->flags & IORESOURCE_PCI_FIXED) 98062306a36Sopenharmony_ci return; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci pcibios_resource_to_bus(dev->bus, ®ion, res); 98362306a36Sopenharmony_ci new = region.start; 98462306a36Sopenharmony_ci new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci reg = iov->pos + PCI_SRIOV_BAR + 4 * vf_bar; 98762306a36Sopenharmony_ci pci_write_config_dword(dev, reg, new); 98862306a36Sopenharmony_ci if (res->flags & IORESOURCE_MEM_64) { 98962306a36Sopenharmony_ci new = region.start >> 16 >> 16; 99062306a36Sopenharmony_ci pci_write_config_dword(dev, reg + 4, new); 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ciresource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, 99562306a36Sopenharmony_ci int resno) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci return pci_iov_resource_size(dev, resno); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci/** 100162306a36Sopenharmony_ci * pci_sriov_resource_alignment - get resource alignment for VF BAR 100262306a36Sopenharmony_ci * @dev: the PCI device 100362306a36Sopenharmony_ci * @resno: the resource number 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * Returns the alignment of the VF BAR found in the SR-IOV capability. 100662306a36Sopenharmony_ci * This is not the same as the resource size which is defined as 100762306a36Sopenharmony_ci * the VF BAR size multiplied by the number of VFs. The alignment 100862306a36Sopenharmony_ci * is just the VF BAR size. 100962306a36Sopenharmony_ci */ 101062306a36Sopenharmony_ciresource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci return pcibios_iov_resource_alignment(dev, resno); 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci/** 101662306a36Sopenharmony_ci * pci_restore_iov_state - restore the state of the IOV capability 101762306a36Sopenharmony_ci * @dev: the PCI device 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_civoid pci_restore_iov_state(struct pci_dev *dev) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci if (dev->is_physfn) 102262306a36Sopenharmony_ci sriov_restore_state(dev); 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci/** 102662306a36Sopenharmony_ci * pci_vf_drivers_autoprobe - set PF property drivers_autoprobe for VFs 102762306a36Sopenharmony_ci * @dev: the PCI device 102862306a36Sopenharmony_ci * @auto_probe: set VF drivers auto probe flag 102962306a36Sopenharmony_ci */ 103062306a36Sopenharmony_civoid pci_vf_drivers_autoprobe(struct pci_dev *dev, bool auto_probe) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci if (dev->is_physfn) 103362306a36Sopenharmony_ci dev->sriov->drivers_autoprobe = auto_probe; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci/** 103762306a36Sopenharmony_ci * pci_iov_bus_range - find bus range used by Virtual Function 103862306a36Sopenharmony_ci * @bus: the PCI bus 103962306a36Sopenharmony_ci * 104062306a36Sopenharmony_ci * Returns max number of buses (exclude current one) used by Virtual 104162306a36Sopenharmony_ci * Functions. 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_ciint pci_iov_bus_range(struct pci_bus *bus) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci int max = 0; 104662306a36Sopenharmony_ci struct pci_dev *dev; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 104962306a36Sopenharmony_ci if (!dev->is_physfn) 105062306a36Sopenharmony_ci continue; 105162306a36Sopenharmony_ci if (dev->sriov->max_VF_buses > max) 105262306a36Sopenharmony_ci max = dev->sriov->max_VF_buses; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci return max ? max - bus->number : 0; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci/** 105962306a36Sopenharmony_ci * pci_enable_sriov - enable the SR-IOV capability 106062306a36Sopenharmony_ci * @dev: the PCI device 106162306a36Sopenharmony_ci * @nr_virtfn: number of virtual functions to enable 106262306a36Sopenharmony_ci * 106362306a36Sopenharmony_ci * Returns 0 on success, or negative on failure. 106462306a36Sopenharmony_ci */ 106562306a36Sopenharmony_ciint pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci might_sleep(); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (!dev->is_physfn) 107062306a36Sopenharmony_ci return -ENOSYS; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci return sriov_enable(dev, nr_virtfn); 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_enable_sriov); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci/** 107762306a36Sopenharmony_ci * pci_disable_sriov - disable the SR-IOV capability 107862306a36Sopenharmony_ci * @dev: the PCI device 107962306a36Sopenharmony_ci */ 108062306a36Sopenharmony_civoid pci_disable_sriov(struct pci_dev *dev) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci might_sleep(); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (!dev->is_physfn) 108562306a36Sopenharmony_ci return; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci sriov_disable(dev); 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_disable_sriov); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci/** 109262306a36Sopenharmony_ci * pci_num_vf - return number of VFs associated with a PF device_release_driver 109362306a36Sopenharmony_ci * @dev: the PCI device 109462306a36Sopenharmony_ci * 109562306a36Sopenharmony_ci * Returns number of VFs, or 0 if SR-IOV is not enabled. 109662306a36Sopenharmony_ci */ 109762306a36Sopenharmony_ciint pci_num_vf(struct pci_dev *dev) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci if (!dev->is_physfn) 110062306a36Sopenharmony_ci return 0; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci return dev->sriov->num_VFs; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_num_vf); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci/** 110762306a36Sopenharmony_ci * pci_vfs_assigned - returns number of VFs are assigned to a guest 110862306a36Sopenharmony_ci * @dev: the PCI device 110962306a36Sopenharmony_ci * 111062306a36Sopenharmony_ci * Returns number of VFs belonging to this device that are assigned to a guest. 111162306a36Sopenharmony_ci * If device is not a physical function returns 0. 111262306a36Sopenharmony_ci */ 111362306a36Sopenharmony_ciint pci_vfs_assigned(struct pci_dev *dev) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct pci_dev *vfdev; 111662306a36Sopenharmony_ci unsigned int vfs_assigned = 0; 111762306a36Sopenharmony_ci unsigned short dev_id; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* only search if we are a PF */ 112062306a36Sopenharmony_ci if (!dev->is_physfn) 112162306a36Sopenharmony_ci return 0; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* 112462306a36Sopenharmony_ci * determine the device ID for the VFs, the vendor ID will be the 112562306a36Sopenharmony_ci * same as the PF so there is no need to check for that one 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci dev_id = dev->sriov->vf_device; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* loop through all the VFs to see if we own any that are assigned */ 113062306a36Sopenharmony_ci vfdev = pci_get_device(dev->vendor, dev_id, NULL); 113162306a36Sopenharmony_ci while (vfdev) { 113262306a36Sopenharmony_ci /* 113362306a36Sopenharmony_ci * It is considered assigned if it is a virtual function with 113462306a36Sopenharmony_ci * our dev as the physical function and the assigned bit is set 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_ci if (vfdev->is_virtfn && (vfdev->physfn == dev) && 113762306a36Sopenharmony_ci pci_is_dev_assigned(vfdev)) 113862306a36Sopenharmony_ci vfs_assigned++; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci vfdev = pci_get_device(dev->vendor, dev_id, vfdev); 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci return vfs_assigned; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_vfs_assigned); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci/** 114862306a36Sopenharmony_ci * pci_sriov_set_totalvfs -- reduce the TotalVFs available 114962306a36Sopenharmony_ci * @dev: the PCI PF device 115062306a36Sopenharmony_ci * @numvfs: number that should be used for TotalVFs supported 115162306a36Sopenharmony_ci * 115262306a36Sopenharmony_ci * Should be called from PF driver's probe routine with 115362306a36Sopenharmony_ci * device's mutex held. 115462306a36Sopenharmony_ci * 115562306a36Sopenharmony_ci * Returns 0 if PF is an SRIOV-capable device and 115662306a36Sopenharmony_ci * value of numvfs valid. If not a PF return -ENOSYS; 115762306a36Sopenharmony_ci * if numvfs is invalid return -EINVAL; 115862306a36Sopenharmony_ci * if VFs already enabled, return -EBUSY. 115962306a36Sopenharmony_ci */ 116062306a36Sopenharmony_ciint pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci if (!dev->is_physfn) 116362306a36Sopenharmony_ci return -ENOSYS; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (numvfs > dev->sriov->total_VFs) 116662306a36Sopenharmony_ci return -EINVAL; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci /* Shouldn't change if VFs already enabled */ 116962306a36Sopenharmony_ci if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE) 117062306a36Sopenharmony_ci return -EBUSY; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci dev->sriov->driver_max_VFs = numvfs; 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci/** 117862306a36Sopenharmony_ci * pci_sriov_get_totalvfs -- get total VFs supported on this device 117962306a36Sopenharmony_ci * @dev: the PCI PF device 118062306a36Sopenharmony_ci * 118162306a36Sopenharmony_ci * For a PCIe device with SRIOV support, return the PCIe 118262306a36Sopenharmony_ci * SRIOV capability value of TotalVFs or the value of driver_max_VFs 118362306a36Sopenharmony_ci * if the driver reduced it. Otherwise 0. 118462306a36Sopenharmony_ci */ 118562306a36Sopenharmony_ciint pci_sriov_get_totalvfs(struct pci_dev *dev) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci if (!dev->is_physfn) 118862306a36Sopenharmony_ci return 0; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci return dev->sriov->driver_max_VFs; 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci/** 119562306a36Sopenharmony_ci * pci_sriov_configure_simple - helper to configure SR-IOV 119662306a36Sopenharmony_ci * @dev: the PCI device 119762306a36Sopenharmony_ci * @nr_virtfn: number of virtual functions to enable, 0 to disable 119862306a36Sopenharmony_ci * 119962306a36Sopenharmony_ci * Enable or disable SR-IOV for devices that don't require any PF setup 120062306a36Sopenharmony_ci * before enabling SR-IOV. Return value is negative on error, or number of 120162306a36Sopenharmony_ci * VFs allocated on success. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ciint pci_sriov_configure_simple(struct pci_dev *dev, int nr_virtfn) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci int rc; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci might_sleep(); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (!dev->is_physfn) 121062306a36Sopenharmony_ci return -ENODEV; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (pci_vfs_assigned(dev)) { 121362306a36Sopenharmony_ci pci_warn(dev, "Cannot modify SR-IOV while VFs are assigned\n"); 121462306a36Sopenharmony_ci return -EPERM; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (nr_virtfn == 0) { 121862306a36Sopenharmony_ci sriov_disable(dev); 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci rc = sriov_enable(dev, nr_virtfn); 122362306a36Sopenharmony_ci if (rc < 0) 122462306a36Sopenharmony_ci return rc; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci return nr_virtfn; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_sriov_configure_simple); 1229