17328c8f4SBjorn Helgaas // SPDX-License-Identifier: GPL-2.0 2d1b054daSYu Zhao /* 3df62ab5eSBjorn Helgaas * PCI Express I/O Virtualization (IOV) support 4d1b054daSYu Zhao * Single Root IOV 1.0 5302b4215SYu Zhao * Address Translation Service 1.0 6df62ab5eSBjorn Helgaas * 7df62ab5eSBjorn Helgaas * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com> 8d1b054daSYu Zhao */ 9d1b054daSYu Zhao 10d1b054daSYu Zhao #include <linux/pci.h> 115a0e3ad6STejun Heo #include <linux/slab.h> 12363c75dbSPaul Gortmaker #include <linux/export.h> 13d1b054daSYu Zhao #include <linux/string.h> 14d1b054daSYu Zhao #include <linux/delay.h> 15d1b054daSYu Zhao #include "pci.h" 16d1b054daSYu Zhao 17dd7cc44dSYu Zhao #define VIRTFN_ID_LEN 16 18d1b054daSYu Zhao 19b07579c0SWei Yang int pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id) 20a28724b0SYu Zhao { 21b07579c0SWei Yang if (!dev->is_physfn) 22b07579c0SWei Yang return -EINVAL; 23a28724b0SYu Zhao return dev->bus->number + ((dev->devfn + dev->sriov->offset + 24b07579c0SWei Yang dev->sriov->stride * vf_id) >> 8); 25a28724b0SYu Zhao } 26a28724b0SYu Zhao 27b07579c0SWei Yang int pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id) 28a28724b0SYu Zhao { 29b07579c0SWei Yang if (!dev->is_physfn) 30b07579c0SWei Yang return -EINVAL; 31a28724b0SYu Zhao return (dev->devfn + dev->sriov->offset + 32b07579c0SWei Yang dev->sriov->stride * vf_id) & 0xff; 33a28724b0SYu Zhao } 34c3d5c2d9SLeon Romanovsky EXPORT_SYMBOL_GPL(pci_iov_virtfn_devfn); 35a28724b0SYu Zhao 36*21ca9fb6SJason Gunthorpe int pci_iov_vf_id(struct pci_dev *dev) 37*21ca9fb6SJason Gunthorpe { 38*21ca9fb6SJason Gunthorpe struct pci_dev *pf; 39*21ca9fb6SJason Gunthorpe 40*21ca9fb6SJason Gunthorpe if (!dev->is_virtfn) 41*21ca9fb6SJason Gunthorpe return -EINVAL; 42*21ca9fb6SJason Gunthorpe 43*21ca9fb6SJason Gunthorpe pf = pci_physfn(dev); 44*21ca9fb6SJason Gunthorpe return (((dev->bus->number << 8) + dev->devfn) - 45*21ca9fb6SJason Gunthorpe ((pf->bus->number << 8) + pf->devfn + pf->sriov->offset)) / 46*21ca9fb6SJason Gunthorpe pf->sriov->stride; 47*21ca9fb6SJason Gunthorpe } 48*21ca9fb6SJason Gunthorpe EXPORT_SYMBOL_GPL(pci_iov_vf_id); 49*21ca9fb6SJason Gunthorpe 50f59dca27SWei Yang /* 51f59dca27SWei Yang * Per SR-IOV spec sec 3.3.10 and 3.3.11, First VF Offset and VF Stride may 52f59dca27SWei Yang * change when NumVFs changes. 53f59dca27SWei Yang * 54f59dca27SWei Yang * Update iov->offset and iov->stride when NumVFs is written. 55f59dca27SWei Yang */ 56f59dca27SWei Yang static inline void pci_iov_set_numvfs(struct pci_dev *dev, int nr_virtfn) 57f59dca27SWei Yang { 58f59dca27SWei Yang struct pci_sriov *iov = dev->sriov; 59f59dca27SWei Yang 60f59dca27SWei Yang pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn); 61f59dca27SWei Yang pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &iov->offset); 62f59dca27SWei Yang pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &iov->stride); 63f59dca27SWei Yang } 64f59dca27SWei Yang 654449f079SWei Yang /* 664449f079SWei Yang * The PF consumes one bus number. NumVFs, First VF Offset, and VF Stride 674449f079SWei Yang * determine how many additional bus numbers will be consumed by VFs. 684449f079SWei Yang * 69ea9a8854SAlexander Duyck * Iterate over all valid NumVFs, validate offset and stride, and calculate 70ea9a8854SAlexander Duyck * the maximum number of bus numbers that could ever be required. 714449f079SWei Yang */ 72ea9a8854SAlexander Duyck static int compute_max_vf_buses(struct pci_dev *dev) 734449f079SWei Yang { 744449f079SWei Yang struct pci_sriov *iov = dev->sriov; 75ea9a8854SAlexander Duyck int nr_virtfn, busnr, rc = 0; 764449f079SWei Yang 77ea9a8854SAlexander Duyck for (nr_virtfn = iov->total_VFs; nr_virtfn; nr_virtfn--) { 784449f079SWei Yang pci_iov_set_numvfs(dev, nr_virtfn); 79ea9a8854SAlexander Duyck if (!iov->offset || (nr_virtfn > 1 && !iov->stride)) { 80ea9a8854SAlexander Duyck rc = -EIO; 81ea9a8854SAlexander Duyck goto out; 824449f079SWei Yang } 834449f079SWei Yang 84ea9a8854SAlexander Duyck busnr = pci_iov_virtfn_bus(dev, nr_virtfn - 1); 85ea9a8854SAlexander Duyck if (busnr > iov->max_VF_buses) 86ea9a8854SAlexander Duyck iov->max_VF_buses = busnr; 87ea9a8854SAlexander Duyck } 88ea9a8854SAlexander Duyck 89ea9a8854SAlexander Duyck out: 90ea9a8854SAlexander Duyck pci_iov_set_numvfs(dev, 0); 91ea9a8854SAlexander Duyck return rc; 924449f079SWei Yang } 934449f079SWei Yang 94dd7cc44dSYu Zhao static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) 95dd7cc44dSYu Zhao { 96dd7cc44dSYu Zhao struct pci_bus *child; 97dd7cc44dSYu Zhao 98dd7cc44dSYu Zhao if (bus->number == busnr) 99dd7cc44dSYu Zhao return bus; 100dd7cc44dSYu Zhao 101dd7cc44dSYu Zhao child = pci_find_bus(pci_domain_nr(bus), busnr); 102dd7cc44dSYu Zhao if (child) 103dd7cc44dSYu Zhao return child; 104dd7cc44dSYu Zhao 105dd7cc44dSYu Zhao child = pci_add_new_bus(bus, NULL, busnr); 106dd7cc44dSYu Zhao if (!child) 107dd7cc44dSYu Zhao return NULL; 108dd7cc44dSYu Zhao 109b7eac055SYinghai Lu pci_bus_insert_busn_res(child, busnr, busnr); 110dd7cc44dSYu Zhao 111dd7cc44dSYu Zhao return child; 112dd7cc44dSYu Zhao } 113dd7cc44dSYu Zhao 114dc087f2fSJiang Liu static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) 115dd7cc44dSYu Zhao { 116dc087f2fSJiang Liu if (physbus != virtbus && list_empty(&virtbus->devices)) 117dc087f2fSJiang Liu pci_remove_bus(virtbus); 118dd7cc44dSYu Zhao } 119dd7cc44dSYu Zhao 1200e6c9122SWei Yang resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) 1210e6c9122SWei Yang { 1220e6c9122SWei Yang if (!dev->is_physfn) 1230e6c9122SWei Yang return 0; 1240e6c9122SWei Yang 1250e6c9122SWei Yang return dev->sriov->barsz[resno - PCI_IOV_RESOURCES]; 1260e6c9122SWei Yang } 1270e6c9122SWei Yang 128cf0921beSKarimAllah Ahmed static void pci_read_vf_config_common(struct pci_dev *virtfn) 129cf0921beSKarimAllah Ahmed { 130cf0921beSKarimAllah Ahmed struct pci_dev *physfn = virtfn->physfn; 131cf0921beSKarimAllah Ahmed 132cf0921beSKarimAllah Ahmed /* 133cf0921beSKarimAllah Ahmed * Some config registers are the same across all associated VFs. 134cf0921beSKarimAllah Ahmed * Read them once from VF0 so we can skip reading them from the 135cf0921beSKarimAllah Ahmed * other VFs. 136cf0921beSKarimAllah Ahmed * 137cf0921beSKarimAllah Ahmed * PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to 138cf0921beSKarimAllah Ahmed * have the same Revision ID and Subsystem ID, but we assume they 139cf0921beSKarimAllah Ahmed * do. 140cf0921beSKarimAllah Ahmed */ 141cf0921beSKarimAllah Ahmed pci_read_config_dword(virtfn, PCI_CLASS_REVISION, 142cf0921beSKarimAllah Ahmed &physfn->sriov->class); 143cf0921beSKarimAllah Ahmed pci_read_config_byte(virtfn, PCI_HEADER_TYPE, 144cf0921beSKarimAllah Ahmed &physfn->sriov->hdr_type); 145cf0921beSKarimAllah Ahmed pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID, 146cf0921beSKarimAllah Ahmed &physfn->sriov->subsystem_vendor); 147cf0921beSKarimAllah Ahmed pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID, 148cf0921beSKarimAllah Ahmed &physfn->sriov->subsystem_device); 149cf0921beSKarimAllah Ahmed } 150cf0921beSKarimAllah Ahmed 151a1ceea67SNiklas Schnelle int pci_iov_sysfs_link(struct pci_dev *dev, 152a1ceea67SNiklas Schnelle struct pci_dev *virtfn, int id) 153a1ceea67SNiklas Schnelle { 154a1ceea67SNiklas Schnelle char buf[VIRTFN_ID_LEN]; 155a1ceea67SNiklas Schnelle int rc; 156a1ceea67SNiklas Schnelle 157a1ceea67SNiklas Schnelle sprintf(buf, "virtfn%u", id); 158a1ceea67SNiklas Schnelle rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); 159a1ceea67SNiklas Schnelle if (rc) 160a1ceea67SNiklas Schnelle goto failed; 161a1ceea67SNiklas Schnelle rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); 162a1ceea67SNiklas Schnelle if (rc) 163a1ceea67SNiklas Schnelle goto failed1; 164a1ceea67SNiklas Schnelle 165a1ceea67SNiklas Schnelle kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); 166a1ceea67SNiklas Schnelle 167a1ceea67SNiklas Schnelle return 0; 168a1ceea67SNiklas Schnelle 169a1ceea67SNiklas Schnelle failed1: 170a1ceea67SNiklas Schnelle sysfs_remove_link(&dev->dev.kobj, buf); 171a1ceea67SNiklas Schnelle failed: 172a1ceea67SNiklas Schnelle return rc; 173a1ceea67SNiklas Schnelle } 174a1ceea67SNiklas Schnelle 175c3d5c2d9SLeon Romanovsky #ifdef CONFIG_PCI_MSI 176c3d5c2d9SLeon Romanovsky static ssize_t sriov_vf_total_msix_show(struct device *dev, 177c3d5c2d9SLeon Romanovsky struct device_attribute *attr, 178c3d5c2d9SLeon Romanovsky char *buf) 179c3d5c2d9SLeon Romanovsky { 180c3d5c2d9SLeon Romanovsky struct pci_dev *pdev = to_pci_dev(dev); 181c3d5c2d9SLeon Romanovsky u32 vf_total_msix = 0; 182c3d5c2d9SLeon Romanovsky 183c3d5c2d9SLeon Romanovsky device_lock(dev); 184e0217c5bSBjorn Helgaas if (!pdev->driver || !pdev->driver->sriov_get_vf_total_msix) 185c3d5c2d9SLeon Romanovsky goto unlock; 186c3d5c2d9SLeon Romanovsky 187e0217c5bSBjorn Helgaas vf_total_msix = pdev->driver->sriov_get_vf_total_msix(pdev); 188c3d5c2d9SLeon Romanovsky unlock: 189c3d5c2d9SLeon Romanovsky device_unlock(dev); 190c3d5c2d9SLeon Romanovsky return sysfs_emit(buf, "%u\n", vf_total_msix); 191c3d5c2d9SLeon Romanovsky } 192c3d5c2d9SLeon Romanovsky static DEVICE_ATTR_RO(sriov_vf_total_msix); 193c3d5c2d9SLeon Romanovsky 194c3d5c2d9SLeon Romanovsky static ssize_t sriov_vf_msix_count_store(struct device *dev, 195c3d5c2d9SLeon Romanovsky struct device_attribute *attr, 196c3d5c2d9SLeon Romanovsky const char *buf, size_t count) 197c3d5c2d9SLeon Romanovsky { 198c3d5c2d9SLeon Romanovsky struct pci_dev *vf_dev = to_pci_dev(dev); 199c3d5c2d9SLeon Romanovsky struct pci_dev *pdev = pci_physfn(vf_dev); 20036f354ecSKrzysztof Wilczyński int val, ret = 0; 201c3d5c2d9SLeon Romanovsky 20236f354ecSKrzysztof Wilczyński if (kstrtoint(buf, 0, &val) < 0) 20336f354ecSKrzysztof Wilczyński return -EINVAL; 204c3d5c2d9SLeon Romanovsky 205c3d5c2d9SLeon Romanovsky if (val < 0) 206c3d5c2d9SLeon Romanovsky return -EINVAL; 207c3d5c2d9SLeon Romanovsky 208c3d5c2d9SLeon Romanovsky device_lock(&pdev->dev); 209e0217c5bSBjorn Helgaas if (!pdev->driver || !pdev->driver->sriov_set_msix_vec_count) { 210c3d5c2d9SLeon Romanovsky ret = -EOPNOTSUPP; 211c3d5c2d9SLeon Romanovsky goto err_pdev; 212c3d5c2d9SLeon Romanovsky } 213c3d5c2d9SLeon Romanovsky 214c3d5c2d9SLeon Romanovsky device_lock(&vf_dev->dev); 215e0217c5bSBjorn Helgaas if (vf_dev->driver) { 216c3d5c2d9SLeon Romanovsky /* 217c3d5c2d9SLeon Romanovsky * A driver is already attached to this VF and has configured 218c3d5c2d9SLeon Romanovsky * itself based on the current MSI-X vector count. Changing 219c3d5c2d9SLeon Romanovsky * the vector size could mess up the driver, so block it. 220c3d5c2d9SLeon Romanovsky */ 221c3d5c2d9SLeon Romanovsky ret = -EBUSY; 222c3d5c2d9SLeon Romanovsky goto err_dev; 223c3d5c2d9SLeon Romanovsky } 224c3d5c2d9SLeon Romanovsky 225e0217c5bSBjorn Helgaas ret = pdev->driver->sriov_set_msix_vec_count(vf_dev, val); 226c3d5c2d9SLeon Romanovsky 227c3d5c2d9SLeon Romanovsky err_dev: 228c3d5c2d9SLeon Romanovsky device_unlock(&vf_dev->dev); 229c3d5c2d9SLeon Romanovsky err_pdev: 230c3d5c2d9SLeon Romanovsky device_unlock(&pdev->dev); 231c3d5c2d9SLeon Romanovsky return ret ? : count; 232c3d5c2d9SLeon Romanovsky } 233c3d5c2d9SLeon Romanovsky static DEVICE_ATTR_WO(sriov_vf_msix_count); 234c3d5c2d9SLeon Romanovsky #endif 235c3d5c2d9SLeon Romanovsky 236c3d5c2d9SLeon Romanovsky static struct attribute *sriov_vf_dev_attrs[] = { 237c3d5c2d9SLeon Romanovsky #ifdef CONFIG_PCI_MSI 238c3d5c2d9SLeon Romanovsky &dev_attr_sriov_vf_msix_count.attr, 239c3d5c2d9SLeon Romanovsky #endif 240c3d5c2d9SLeon Romanovsky NULL, 241c3d5c2d9SLeon Romanovsky }; 242c3d5c2d9SLeon Romanovsky 243c3d5c2d9SLeon Romanovsky static umode_t sriov_vf_attrs_are_visible(struct kobject *kobj, 244c3d5c2d9SLeon Romanovsky struct attribute *a, int n) 245c3d5c2d9SLeon Romanovsky { 246c3d5c2d9SLeon Romanovsky struct device *dev = kobj_to_dev(kobj); 247c3d5c2d9SLeon Romanovsky struct pci_dev *pdev = to_pci_dev(dev); 248c3d5c2d9SLeon Romanovsky 249c3d5c2d9SLeon Romanovsky if (!pdev->is_virtfn) 250c3d5c2d9SLeon Romanovsky return 0; 251c3d5c2d9SLeon Romanovsky 252c3d5c2d9SLeon Romanovsky return a->mode; 253c3d5c2d9SLeon Romanovsky } 254c3d5c2d9SLeon Romanovsky 255c3d5c2d9SLeon Romanovsky const struct attribute_group sriov_vf_dev_attr_group = { 256c3d5c2d9SLeon Romanovsky .attrs = sriov_vf_dev_attrs, 257c3d5c2d9SLeon Romanovsky .is_visible = sriov_vf_attrs_are_visible, 258c3d5c2d9SLeon Romanovsky }; 259c3d5c2d9SLeon Romanovsky 260753f6124SJan H. Schönherr int pci_iov_add_virtfn(struct pci_dev *dev, int id) 261dd7cc44dSYu Zhao { 262dd7cc44dSYu Zhao int i; 263dc087f2fSJiang Liu int rc = -ENOMEM; 264dd7cc44dSYu Zhao u64 size; 265dd7cc44dSYu Zhao struct pci_dev *virtfn; 266dd7cc44dSYu Zhao struct resource *res; 267dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 2688b1fce04SGu Zheng struct pci_bus *bus; 269dd7cc44dSYu Zhao 270b07579c0SWei Yang bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id)); 271dc087f2fSJiang Liu if (!bus) 272dc087f2fSJiang Liu goto failed; 273dc087f2fSJiang Liu 274dc087f2fSJiang Liu virtfn = pci_alloc_dev(bus); 275dc087f2fSJiang Liu if (!virtfn) 276dc087f2fSJiang Liu goto failed0; 277dc087f2fSJiang Liu 278b07579c0SWei Yang virtfn->devfn = pci_iov_virtfn_devfn(dev, id); 279dd7cc44dSYu Zhao virtfn->vendor = dev->vendor; 2803142d832SFilippo Sironi virtfn->device = iov->vf_device; 281cf0921beSKarimAllah Ahmed virtfn->is_virtfn = 1; 282cf0921beSKarimAllah Ahmed virtfn->physfn = pci_dev_get(dev); 28312856e7aSMatthew Rosato virtfn->no_command_memory = 1; 284cf0921beSKarimAllah Ahmed 285cf0921beSKarimAllah Ahmed if (id == 0) 286cf0921beSKarimAllah Ahmed pci_read_vf_config_common(virtfn); 287cf0921beSKarimAllah Ahmed 288156c5532SPo Liu rc = pci_setup_device(virtfn); 289156c5532SPo Liu if (rc) 290cf0921beSKarimAllah Ahmed goto failed1; 291156c5532SPo Liu 292dd7cc44dSYu Zhao virtfn->dev.parent = dev->dev.parent; 293aa931977SAlex Williamson virtfn->multifunction = 0; 294dd7cc44dSYu Zhao 295dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 296c1fe1f96SBjorn Helgaas res = &dev->resource[i + PCI_IOV_RESOURCES]; 297dd7cc44dSYu Zhao if (!res->parent) 298dd7cc44dSYu Zhao continue; 299dd7cc44dSYu Zhao virtfn->resource[i].name = pci_name(virtfn); 300dd7cc44dSYu Zhao virtfn->resource[i].flags = res->flags; 3010e6c9122SWei Yang size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES); 302dd7cc44dSYu Zhao virtfn->resource[i].start = res->start + size * id; 303dd7cc44dSYu Zhao virtfn->resource[i].end = virtfn->resource[i].start + size - 1; 304dd7cc44dSYu Zhao rc = request_resource(res, &virtfn->resource[i]); 305dd7cc44dSYu Zhao BUG_ON(rc); 306dd7cc44dSYu Zhao } 307dd7cc44dSYu Zhao 308dd7cc44dSYu Zhao pci_device_add(virtfn, virtfn->bus); 309a1ceea67SNiklas Schnelle rc = pci_iov_sysfs_link(dev, virtfn, id); 310dd7cc44dSYu Zhao if (rc) 3118c386cc8SNavid Emamdoost goto failed1; 312dd7cc44dSYu Zhao 31327d61629SStuart Hayes pci_bus_add_device(virtfn); 31427d61629SStuart Hayes 315dd7cc44dSYu Zhao return 0; 316dd7cc44dSYu Zhao 317dd7cc44dSYu Zhao failed1: 3188c386cc8SNavid Emamdoost pci_stop_and_remove_bus_device(virtfn); 319dd7cc44dSYu Zhao pci_dev_put(dev); 320dc087f2fSJiang Liu failed0: 321dc087f2fSJiang Liu virtfn_remove_bus(dev->bus, bus); 322dc087f2fSJiang Liu failed: 323dd7cc44dSYu Zhao 324dd7cc44dSYu Zhao return rc; 325dd7cc44dSYu Zhao } 326dd7cc44dSYu Zhao 327753f6124SJan H. Schönherr void pci_iov_remove_virtfn(struct pci_dev *dev, int id) 328dd7cc44dSYu Zhao { 329dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 330dd7cc44dSYu Zhao struct pci_dev *virtfn; 331dd7cc44dSYu Zhao 332dc087f2fSJiang Liu virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), 333b07579c0SWei Yang pci_iov_virtfn_bus(dev, id), 334b07579c0SWei Yang pci_iov_virtfn_devfn(dev, id)); 335dd7cc44dSYu Zhao if (!virtfn) 336dd7cc44dSYu Zhao return; 337dd7cc44dSYu Zhao 338dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 339dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 34009cedbefSYinghai Lu /* 34109cedbefSYinghai Lu * pci_stop_dev() could have been called for this virtfn already, 34209cedbefSYinghai Lu * so the directory for the virtfn may have been removed before. 34309cedbefSYinghai Lu * Double check to avoid spurious sysfs warnings. 34409cedbefSYinghai Lu */ 34509cedbefSYinghai Lu if (virtfn->dev.kobj.sd) 346dd7cc44dSYu Zhao sysfs_remove_link(&virtfn->dev.kobj, "physfn"); 347dd7cc44dSYu Zhao 348210647afSYinghai Lu pci_stop_and_remove_bus_device(virtfn); 349dc087f2fSJiang Liu virtfn_remove_bus(dev->bus, virtfn->bus); 350dd7cc44dSYu Zhao 351dc087f2fSJiang Liu /* balance pci_get_domain_bus_and_slot() */ 352dc087f2fSJiang Liu pci_dev_put(virtfn); 353dd7cc44dSYu Zhao pci_dev_put(dev); 354dd7cc44dSYu Zhao } 355dd7cc44dSYu Zhao 356aaee0c1fSKelsey Skunberg static ssize_t sriov_totalvfs_show(struct device *dev, 357aaee0c1fSKelsey Skunberg struct device_attribute *attr, 358aaee0c1fSKelsey Skunberg char *buf) 359aaee0c1fSKelsey Skunberg { 360aaee0c1fSKelsey Skunberg struct pci_dev *pdev = to_pci_dev(dev); 361aaee0c1fSKelsey Skunberg 362f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", pci_sriov_get_totalvfs(pdev)); 363aaee0c1fSKelsey Skunberg } 364aaee0c1fSKelsey Skunberg 365aaee0c1fSKelsey Skunberg static ssize_t sriov_numvfs_show(struct device *dev, 366aaee0c1fSKelsey Skunberg struct device_attribute *attr, 367aaee0c1fSKelsey Skunberg char *buf) 368aaee0c1fSKelsey Skunberg { 369aaee0c1fSKelsey Skunberg struct pci_dev *pdev = to_pci_dev(dev); 37035ff867bSPierre Crégut u16 num_vfs; 371aaee0c1fSKelsey Skunberg 37235ff867bSPierre Crégut /* Serialize vs sriov_numvfs_store() so readers see valid num_VFs */ 37335ff867bSPierre Crégut device_lock(&pdev->dev); 37435ff867bSPierre Crégut num_vfs = pdev->sriov->num_VFs; 37535ff867bSPierre Crégut device_unlock(&pdev->dev); 37635ff867bSPierre Crégut 377f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", num_vfs); 378aaee0c1fSKelsey Skunberg } 379aaee0c1fSKelsey Skunberg 380aaee0c1fSKelsey Skunberg /* 381aaee0c1fSKelsey Skunberg * num_vfs > 0; number of VFs to enable 382aaee0c1fSKelsey Skunberg * num_vfs = 0; disable all VFs 383aaee0c1fSKelsey Skunberg * 384aaee0c1fSKelsey Skunberg * Note: SRIOV spec does not allow partial VF 385aaee0c1fSKelsey Skunberg * disable, so it's all or none. 386aaee0c1fSKelsey Skunberg */ 387aaee0c1fSKelsey Skunberg static ssize_t sriov_numvfs_store(struct device *dev, 388aaee0c1fSKelsey Skunberg struct device_attribute *attr, 389aaee0c1fSKelsey Skunberg const char *buf, size_t count) 390aaee0c1fSKelsey Skunberg { 391aaee0c1fSKelsey Skunberg struct pci_dev *pdev = to_pci_dev(dev); 39236f354ecSKrzysztof Wilczyński int ret = 0; 393aaee0c1fSKelsey Skunberg u16 num_vfs; 394aaee0c1fSKelsey Skunberg 39536f354ecSKrzysztof Wilczyński if (kstrtou16(buf, 0, &num_vfs) < 0) 39636f354ecSKrzysztof Wilczyński return -EINVAL; 397aaee0c1fSKelsey Skunberg 398aaee0c1fSKelsey Skunberg if (num_vfs > pci_sriov_get_totalvfs(pdev)) 399aaee0c1fSKelsey Skunberg return -ERANGE; 400aaee0c1fSKelsey Skunberg 401aaee0c1fSKelsey Skunberg device_lock(&pdev->dev); 402aaee0c1fSKelsey Skunberg 403aaee0c1fSKelsey Skunberg if (num_vfs == pdev->sriov->num_VFs) 404aaee0c1fSKelsey Skunberg goto exit; 405aaee0c1fSKelsey Skunberg 406e9c3bbd6SMoritz Fischer /* is PF driver loaded */ 407e0217c5bSBjorn Helgaas if (!pdev->driver) { 408e9c3bbd6SMoritz Fischer pci_info(pdev, "no driver bound to device; cannot configure SR-IOV\n"); 409e9c3bbd6SMoritz Fischer ret = -ENOENT; 410e9c3bbd6SMoritz Fischer goto exit; 411e9c3bbd6SMoritz Fischer } 412e9c3bbd6SMoritz Fischer 413aaee0c1fSKelsey Skunberg /* is PF driver loaded w/callback */ 414e0217c5bSBjorn Helgaas if (!pdev->driver->sriov_configure) { 415e9c3bbd6SMoritz Fischer pci_info(pdev, "driver does not support SR-IOV configuration via sysfs\n"); 416aaee0c1fSKelsey Skunberg ret = -ENOENT; 417aaee0c1fSKelsey Skunberg goto exit; 418aaee0c1fSKelsey Skunberg } 419aaee0c1fSKelsey Skunberg 420aaee0c1fSKelsey Skunberg if (num_vfs == 0) { 421aaee0c1fSKelsey Skunberg /* disable VFs */ 422e0217c5bSBjorn Helgaas ret = pdev->driver->sriov_configure(pdev, 0); 423aaee0c1fSKelsey Skunberg goto exit; 424aaee0c1fSKelsey Skunberg } 425aaee0c1fSKelsey Skunberg 426aaee0c1fSKelsey Skunberg /* enable VFs */ 427aaee0c1fSKelsey Skunberg if (pdev->sriov->num_VFs) { 428aaee0c1fSKelsey Skunberg pci_warn(pdev, "%d VFs already enabled. Disable before enabling %d VFs\n", 429aaee0c1fSKelsey Skunberg pdev->sriov->num_VFs, num_vfs); 430aaee0c1fSKelsey Skunberg ret = -EBUSY; 431aaee0c1fSKelsey Skunberg goto exit; 432aaee0c1fSKelsey Skunberg } 433aaee0c1fSKelsey Skunberg 434e0217c5bSBjorn Helgaas ret = pdev->driver->sriov_configure(pdev, num_vfs); 435aaee0c1fSKelsey Skunberg if (ret < 0) 436aaee0c1fSKelsey Skunberg goto exit; 437aaee0c1fSKelsey Skunberg 438aaee0c1fSKelsey Skunberg if (ret != num_vfs) 439aaee0c1fSKelsey Skunberg pci_warn(pdev, "%d VFs requested; only %d enabled\n", 440aaee0c1fSKelsey Skunberg num_vfs, ret); 441aaee0c1fSKelsey Skunberg 442aaee0c1fSKelsey Skunberg exit: 443aaee0c1fSKelsey Skunberg device_unlock(&pdev->dev); 444aaee0c1fSKelsey Skunberg 445aaee0c1fSKelsey Skunberg if (ret < 0) 446aaee0c1fSKelsey Skunberg return ret; 447aaee0c1fSKelsey Skunberg 448aaee0c1fSKelsey Skunberg return count; 449aaee0c1fSKelsey Skunberg } 450aaee0c1fSKelsey Skunberg 451aaee0c1fSKelsey Skunberg static ssize_t sriov_offset_show(struct device *dev, 452aaee0c1fSKelsey Skunberg struct device_attribute *attr, 453aaee0c1fSKelsey Skunberg char *buf) 454aaee0c1fSKelsey Skunberg { 455aaee0c1fSKelsey Skunberg struct pci_dev *pdev = to_pci_dev(dev); 456aaee0c1fSKelsey Skunberg 457f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", pdev->sriov->offset); 458aaee0c1fSKelsey Skunberg } 459aaee0c1fSKelsey Skunberg 460aaee0c1fSKelsey Skunberg static ssize_t sriov_stride_show(struct device *dev, 461aaee0c1fSKelsey Skunberg struct device_attribute *attr, 462aaee0c1fSKelsey Skunberg char *buf) 463aaee0c1fSKelsey Skunberg { 464aaee0c1fSKelsey Skunberg struct pci_dev *pdev = to_pci_dev(dev); 465aaee0c1fSKelsey Skunberg 466f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", pdev->sriov->stride); 467aaee0c1fSKelsey Skunberg } 468aaee0c1fSKelsey Skunberg 469aaee0c1fSKelsey Skunberg static ssize_t sriov_vf_device_show(struct device *dev, 470aaee0c1fSKelsey Skunberg struct device_attribute *attr, 471aaee0c1fSKelsey Skunberg char *buf) 472aaee0c1fSKelsey Skunberg { 473aaee0c1fSKelsey Skunberg struct pci_dev *pdev = to_pci_dev(dev); 474aaee0c1fSKelsey Skunberg 475f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%x\n", pdev->sriov->vf_device); 476aaee0c1fSKelsey Skunberg } 477aaee0c1fSKelsey Skunberg 478aaee0c1fSKelsey Skunberg static ssize_t sriov_drivers_autoprobe_show(struct device *dev, 479aaee0c1fSKelsey Skunberg struct device_attribute *attr, 480aaee0c1fSKelsey Skunberg char *buf) 481aaee0c1fSKelsey Skunberg { 482aaee0c1fSKelsey Skunberg struct pci_dev *pdev = to_pci_dev(dev); 483aaee0c1fSKelsey Skunberg 484f8cf6e51SKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", pdev->sriov->drivers_autoprobe); 485aaee0c1fSKelsey Skunberg } 486aaee0c1fSKelsey Skunberg 487aaee0c1fSKelsey Skunberg static ssize_t sriov_drivers_autoprobe_store(struct device *dev, 488aaee0c1fSKelsey Skunberg struct device_attribute *attr, 489aaee0c1fSKelsey Skunberg const char *buf, size_t count) 490aaee0c1fSKelsey Skunberg { 491aaee0c1fSKelsey Skunberg struct pci_dev *pdev = to_pci_dev(dev); 492aaee0c1fSKelsey Skunberg bool drivers_autoprobe; 493aaee0c1fSKelsey Skunberg 494aaee0c1fSKelsey Skunberg if (kstrtobool(buf, &drivers_autoprobe) < 0) 495aaee0c1fSKelsey Skunberg return -EINVAL; 496aaee0c1fSKelsey Skunberg 497aaee0c1fSKelsey Skunberg pdev->sriov->drivers_autoprobe = drivers_autoprobe; 498aaee0c1fSKelsey Skunberg 499aaee0c1fSKelsey Skunberg return count; 500aaee0c1fSKelsey Skunberg } 501aaee0c1fSKelsey Skunberg 502aaee0c1fSKelsey Skunberg static DEVICE_ATTR_RO(sriov_totalvfs); 503244c06c3SKelsey Skunberg static DEVICE_ATTR_RW(sriov_numvfs); 504aaee0c1fSKelsey Skunberg static DEVICE_ATTR_RO(sriov_offset); 505aaee0c1fSKelsey Skunberg static DEVICE_ATTR_RO(sriov_stride); 506aaee0c1fSKelsey Skunberg static DEVICE_ATTR_RO(sriov_vf_device); 507244c06c3SKelsey Skunberg static DEVICE_ATTR_RW(sriov_drivers_autoprobe); 508aaee0c1fSKelsey Skunberg 509c3d5c2d9SLeon Romanovsky static struct attribute *sriov_pf_dev_attrs[] = { 510aaee0c1fSKelsey Skunberg &dev_attr_sriov_totalvfs.attr, 511aaee0c1fSKelsey Skunberg &dev_attr_sriov_numvfs.attr, 512aaee0c1fSKelsey Skunberg &dev_attr_sriov_offset.attr, 513aaee0c1fSKelsey Skunberg &dev_attr_sriov_stride.attr, 514aaee0c1fSKelsey Skunberg &dev_attr_sriov_vf_device.attr, 515aaee0c1fSKelsey Skunberg &dev_attr_sriov_drivers_autoprobe.attr, 516c3d5c2d9SLeon Romanovsky #ifdef CONFIG_PCI_MSI 517c3d5c2d9SLeon Romanovsky &dev_attr_sriov_vf_total_msix.attr, 518c3d5c2d9SLeon Romanovsky #endif 519aaee0c1fSKelsey Skunberg NULL, 520aaee0c1fSKelsey Skunberg }; 521aaee0c1fSKelsey Skunberg 522c3d5c2d9SLeon Romanovsky static umode_t sriov_pf_attrs_are_visible(struct kobject *kobj, 523aaee0c1fSKelsey Skunberg struct attribute *a, int n) 524aaee0c1fSKelsey Skunberg { 525aaee0c1fSKelsey Skunberg struct device *dev = kobj_to_dev(kobj); 526aaee0c1fSKelsey Skunberg 527aaee0c1fSKelsey Skunberg if (!dev_is_pf(dev)) 528aaee0c1fSKelsey Skunberg return 0; 529aaee0c1fSKelsey Skunberg 530aaee0c1fSKelsey Skunberg return a->mode; 531aaee0c1fSKelsey Skunberg } 532aaee0c1fSKelsey Skunberg 533c3d5c2d9SLeon Romanovsky const struct attribute_group sriov_pf_dev_attr_group = { 534c3d5c2d9SLeon Romanovsky .attrs = sriov_pf_dev_attrs, 535c3d5c2d9SLeon Romanovsky .is_visible = sriov_pf_attrs_are_visible, 536aaee0c1fSKelsey Skunberg }; 537aaee0c1fSKelsey Skunberg 538995df527SWei Yang int __weak pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs) 539995df527SWei Yang { 540995df527SWei Yang return 0; 541995df527SWei Yang } 542995df527SWei Yang 543a39e3fcdSAlexander Duyck int __weak pcibios_sriov_disable(struct pci_dev *pdev) 544a39e3fcdSAlexander Duyck { 545a39e3fcdSAlexander Duyck return 0; 546a39e3fcdSAlexander Duyck } 547a39e3fcdSAlexander Duyck 54818f9e9d1SSebastian Ott static int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs) 54918f9e9d1SSebastian Ott { 55018f9e9d1SSebastian Ott unsigned int i; 55118f9e9d1SSebastian Ott int rc; 55218f9e9d1SSebastian Ott 553aff68a5aSSebastian Ott if (dev->no_vf_scan) 554aff68a5aSSebastian Ott return 0; 555aff68a5aSSebastian Ott 55618f9e9d1SSebastian Ott for (i = 0; i < num_vfs; i++) { 55718f9e9d1SSebastian Ott rc = pci_iov_add_virtfn(dev, i); 55818f9e9d1SSebastian Ott if (rc) 55918f9e9d1SSebastian Ott goto failed; 56018f9e9d1SSebastian Ott } 56118f9e9d1SSebastian Ott return 0; 56218f9e9d1SSebastian Ott failed: 56318f9e9d1SSebastian Ott while (i--) 56418f9e9d1SSebastian Ott pci_iov_remove_virtfn(dev, i); 56518f9e9d1SSebastian Ott 56618f9e9d1SSebastian Ott return rc; 56718f9e9d1SSebastian Ott } 56818f9e9d1SSebastian Ott 569dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn) 570dd7cc44dSYu Zhao { 571dd7cc44dSYu Zhao int rc; 5723443c382SAlexander Duyck int i; 573dd7cc44dSYu Zhao int nres; 574ce288ec3SAlexander Duyck u16 initial; 575dd7cc44dSYu Zhao struct resource *res; 576dd7cc44dSYu Zhao struct pci_dev *pdev; 577dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 578bbef98abSRam Pai int bars = 0; 579b07579c0SWei Yang int bus; 580dd7cc44dSYu Zhao 581dd7cc44dSYu Zhao if (!nr_virtfn) 582dd7cc44dSYu Zhao return 0; 583dd7cc44dSYu Zhao 5846b136724SBjorn Helgaas if (iov->num_VFs) 585dd7cc44dSYu Zhao return -EINVAL; 586dd7cc44dSYu Zhao 587dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); 5886b136724SBjorn Helgaas if (initial > iov->total_VFs || 5896b136724SBjorn Helgaas (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs))) 590dd7cc44dSYu Zhao return -EIO; 591dd7cc44dSYu Zhao 5926b136724SBjorn Helgaas if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs || 593dd7cc44dSYu Zhao (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) 594dd7cc44dSYu Zhao return -EINVAL; 595dd7cc44dSYu Zhao 596dd7cc44dSYu Zhao nres = 0; 597dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 598bbef98abSRam Pai bars |= (1 << (i + PCI_IOV_RESOURCES)); 599c1fe1f96SBjorn Helgaas res = &dev->resource[i + PCI_IOV_RESOURCES]; 600dd7cc44dSYu Zhao if (res->parent) 601dd7cc44dSYu Zhao nres++; 602dd7cc44dSYu Zhao } 603dd7cc44dSYu Zhao if (nres != iov->nres) { 6047506dc79SFrederick Lawler pci_err(dev, "not enough MMIO resources for SR-IOV\n"); 605dd7cc44dSYu Zhao return -ENOMEM; 606dd7cc44dSYu Zhao } 607dd7cc44dSYu Zhao 608b07579c0SWei Yang bus = pci_iov_virtfn_bus(dev, nr_virtfn - 1); 60968f8e9faSBjorn Helgaas if (bus > dev->bus->busn_res.end) { 6107506dc79SFrederick Lawler pci_err(dev, "can't enable %d VFs (bus %02x out of range of %pR)\n", 61168f8e9faSBjorn Helgaas nr_virtfn, bus, &dev->bus->busn_res); 612dd7cc44dSYu Zhao return -ENOMEM; 613dd7cc44dSYu Zhao } 614dd7cc44dSYu Zhao 615bbef98abSRam Pai if (pci_enable_resources(dev, bars)) { 6167506dc79SFrederick Lawler pci_err(dev, "SR-IOV: IOV BARS not allocated\n"); 617bbef98abSRam Pai return -ENOMEM; 618bbef98abSRam Pai } 619bbef98abSRam Pai 620dd7cc44dSYu Zhao if (iov->link != dev->devfn) { 621dd7cc44dSYu Zhao pdev = pci_get_slot(dev->bus, iov->link); 622dd7cc44dSYu Zhao if (!pdev) 623dd7cc44dSYu Zhao return -ENODEV; 624dd7cc44dSYu Zhao 625dc087f2fSJiang Liu if (!pdev->is_physfn) { 626dd7cc44dSYu Zhao pci_dev_put(pdev); 627652d1100SStefan Assmann return -ENOSYS; 628dc087f2fSJiang Liu } 629dd7cc44dSYu Zhao 630dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, 631dd7cc44dSYu Zhao &pdev->dev.kobj, "dep_link"); 632dc087f2fSJiang Liu pci_dev_put(pdev); 633dd7cc44dSYu Zhao if (rc) 634dd7cc44dSYu Zhao return rc; 635dd7cc44dSYu Zhao } 636dd7cc44dSYu Zhao 6376b136724SBjorn Helgaas iov->initial_VFs = initial; 638dd7cc44dSYu Zhao if (nr_virtfn < initial) 639dd7cc44dSYu Zhao initial = nr_virtfn; 640dd7cc44dSYu Zhao 641c23b6135SAlexander Duyck rc = pcibios_sriov_enable(dev, initial); 642c23b6135SAlexander Duyck if (rc) { 6437506dc79SFrederick Lawler pci_err(dev, "failure %d from pcibios_sriov_enable()\n", rc); 644c23b6135SAlexander Duyck goto err_pcibios; 645995df527SWei Yang } 646995df527SWei Yang 647f40ec3c7SGavin Shan pci_iov_set_numvfs(dev, nr_virtfn); 648f40ec3c7SGavin Shan iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; 649f40ec3c7SGavin Shan pci_cfg_access_lock(dev); 650f40ec3c7SGavin Shan pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 651f40ec3c7SGavin Shan msleep(100); 652f40ec3c7SGavin Shan pci_cfg_access_unlock(dev); 653f40ec3c7SGavin Shan 65418f9e9d1SSebastian Ott rc = sriov_add_vfs(dev, initial); 655dd7cc44dSYu Zhao if (rc) 65618f9e9d1SSebastian Ott goto err_pcibios; 657dd7cc44dSYu Zhao 658dd7cc44dSYu Zhao kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); 6596b136724SBjorn Helgaas iov->num_VFs = nr_virtfn; 660dd7cc44dSYu Zhao 661dd7cc44dSYu Zhao return 0; 662dd7cc44dSYu Zhao 663c23b6135SAlexander Duyck err_pcibios: 664dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 665fb51ccbfSJan Kiszka pci_cfg_access_lock(dev); 666dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 667dd7cc44dSYu Zhao ssleep(1); 668fb51ccbfSJan Kiszka pci_cfg_access_unlock(dev); 669dd7cc44dSYu Zhao 6700fc690a7SGavin Shan pcibios_sriov_disable(dev); 6710fc690a7SGavin Shan 672dd7cc44dSYu Zhao if (iov->link != dev->devfn) 673dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 674dd7cc44dSYu Zhao 675b3908644SAlexander Duyck pci_iov_set_numvfs(dev, 0); 676dd7cc44dSYu Zhao return rc; 677dd7cc44dSYu Zhao } 678dd7cc44dSYu Zhao 67918f9e9d1SSebastian Ott static void sriov_del_vfs(struct pci_dev *dev) 68018f9e9d1SSebastian Ott { 68118f9e9d1SSebastian Ott struct pci_sriov *iov = dev->sriov; 68218f9e9d1SSebastian Ott int i; 68318f9e9d1SSebastian Ott 68418f9e9d1SSebastian Ott for (i = 0; i < iov->num_VFs; i++) 68518f9e9d1SSebastian Ott pci_iov_remove_virtfn(dev, i); 68618f9e9d1SSebastian Ott } 68718f9e9d1SSebastian Ott 688dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev) 689dd7cc44dSYu Zhao { 690dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 691dd7cc44dSYu Zhao 6926b136724SBjorn Helgaas if (!iov->num_VFs) 693dd7cc44dSYu Zhao return; 694dd7cc44dSYu Zhao 69518f9e9d1SSebastian Ott sriov_del_vfs(dev); 696dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 697fb51ccbfSJan Kiszka pci_cfg_access_lock(dev); 698dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 699dd7cc44dSYu Zhao ssleep(1); 700fb51ccbfSJan Kiszka pci_cfg_access_unlock(dev); 701dd7cc44dSYu Zhao 7020fc690a7SGavin Shan pcibios_sriov_disable(dev); 7030fc690a7SGavin Shan 704dd7cc44dSYu Zhao if (iov->link != dev->devfn) 705dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 706dd7cc44dSYu Zhao 7076b136724SBjorn Helgaas iov->num_VFs = 0; 708f59dca27SWei Yang pci_iov_set_numvfs(dev, 0); 709dd7cc44dSYu Zhao } 710dd7cc44dSYu Zhao 711d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos) 712d1b054daSYu Zhao { 7130e6c9122SWei Yang int i, bar64; 714d1b054daSYu Zhao int rc; 715d1b054daSYu Zhao int nres; 716d1b054daSYu Zhao u32 pgsz; 717ea9a8854SAlexander Duyck u16 ctrl, total; 718d1b054daSYu Zhao struct pci_sriov *iov; 719d1b054daSYu Zhao struct resource *res; 720d1b054daSYu Zhao struct pci_dev *pdev; 721d1b054daSYu Zhao 722d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); 723d1b054daSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) { 724d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); 725d1b054daSYu Zhao ssleep(1); 726d1b054daSYu Zhao } 727d1b054daSYu Zhao 728d1b054daSYu Zhao ctrl = 0; 729d1b054daSYu Zhao list_for_each_entry(pdev, &dev->bus->devices, bus_list) 730d1b054daSYu Zhao if (pdev->is_physfn) 731d1b054daSYu Zhao goto found; 732d1b054daSYu Zhao 733d1b054daSYu Zhao pdev = NULL; 734d1b054daSYu Zhao if (pci_ari_enabled(dev->bus)) 735d1b054daSYu Zhao ctrl |= PCI_SRIOV_CTRL_ARI; 736d1b054daSYu Zhao 737d1b054daSYu Zhao found: 738d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); 739d1b054daSYu Zhao 740ff45f9ddSBen Shelton pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); 741ff45f9ddSBen Shelton if (!total) 742ff45f9ddSBen Shelton return 0; 743d1b054daSYu Zhao 744d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); 745d1b054daSYu Zhao i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; 746d1b054daSYu Zhao pgsz &= ~((1 << i) - 1); 747d1b054daSYu Zhao if (!pgsz) 748d1b054daSYu Zhao return -EIO; 749d1b054daSYu Zhao 750d1b054daSYu Zhao pgsz &= ~(pgsz - 1); 7518161fe91SVaidyanathan Srinivasan pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); 752d1b054daSYu Zhao 7530e6c9122SWei Yang iov = kzalloc(sizeof(*iov), GFP_KERNEL); 7540e6c9122SWei Yang if (!iov) 7550e6c9122SWei Yang return -ENOMEM; 7560e6c9122SWei Yang 757d1b054daSYu Zhao nres = 0; 758d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 759c1fe1f96SBjorn Helgaas res = &dev->resource[i + PCI_IOV_RESOURCES]; 76011183991SDavid Daney /* 76111183991SDavid Daney * If it is already FIXED, don't change it, something 76211183991SDavid Daney * (perhaps EA or header fixups) wants it this way. 76311183991SDavid Daney */ 76411183991SDavid Daney if (res->flags & IORESOURCE_PCI_FIXED) 76511183991SDavid Daney bar64 = (res->flags & IORESOURCE_MEM_64) ? 1 : 0; 76611183991SDavid Daney else 7670e6c9122SWei Yang bar64 = __pci_read_base(dev, pci_bar_unknown, res, 768d1b054daSYu Zhao pos + PCI_SRIOV_BAR + i * 4); 769d1b054daSYu Zhao if (!res->flags) 770d1b054daSYu Zhao continue; 771d1b054daSYu Zhao if (resource_size(res) & (PAGE_SIZE - 1)) { 772d1b054daSYu Zhao rc = -EIO; 773d1b054daSYu Zhao goto failed; 774d1b054daSYu Zhao } 7750e6c9122SWei Yang iov->barsz[i] = resource_size(res); 776d1b054daSYu Zhao res->end = res->start + resource_size(res) * total - 1; 7777506dc79SFrederick Lawler pci_info(dev, "VF(n) BAR%d space: %pR (contains BAR%d for %d VFs)\n", 778e88ae01dSWei Yang i, res, i, total); 7790e6c9122SWei Yang i += bar64; 780d1b054daSYu Zhao nres++; 781d1b054daSYu Zhao } 782d1b054daSYu Zhao 783d1b054daSYu Zhao iov->pos = pos; 784d1b054daSYu Zhao iov->nres = nres; 785d1b054daSYu Zhao iov->ctrl = ctrl; 7866b136724SBjorn Helgaas iov->total_VFs = total; 7878d85a7a4SJakub Kicinski iov->driver_max_VFs = total; 7883142d832SFilippo Sironi pci_read_config_word(dev, pos + PCI_SRIOV_VF_DID, &iov->vf_device); 789d1b054daSYu Zhao iov->pgsz = pgsz; 790d1b054daSYu Zhao iov->self = dev; 7910e7df224SBodong Wang iov->drivers_autoprobe = true; 792d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); 793d1b054daSYu Zhao pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); 79462f87c0eSYijing Wang if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) 7954d135dbeSYu Zhao iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link); 796d1b054daSYu Zhao 797d1b054daSYu Zhao if (pdev) 798d1b054daSYu Zhao iov->dev = pci_dev_get(pdev); 799e277d2fcSYu Zhao else 800d1b054daSYu Zhao iov->dev = dev; 801e277d2fcSYu Zhao 802d1b054daSYu Zhao dev->sriov = iov; 803d1b054daSYu Zhao dev->is_physfn = 1; 804ea9a8854SAlexander Duyck rc = compute_max_vf_buses(dev); 805ea9a8854SAlexander Duyck if (rc) 806ea9a8854SAlexander Duyck goto fail_max_buses; 807d1b054daSYu Zhao 808d1b054daSYu Zhao return 0; 809d1b054daSYu Zhao 810ea9a8854SAlexander Duyck fail_max_buses: 811ea9a8854SAlexander Duyck dev->sriov = NULL; 812ea9a8854SAlexander Duyck dev->is_physfn = 0; 813d1b054daSYu Zhao failed: 814d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 815c1fe1f96SBjorn Helgaas res = &dev->resource[i + PCI_IOV_RESOURCES]; 816d1b054daSYu Zhao res->flags = 0; 817d1b054daSYu Zhao } 818d1b054daSYu Zhao 8190e6c9122SWei Yang kfree(iov); 820d1b054daSYu Zhao return rc; 821d1b054daSYu Zhao } 822d1b054daSYu Zhao 823d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev) 824d1b054daSYu Zhao { 8256b136724SBjorn Helgaas BUG_ON(dev->sriov->num_VFs); 826dd7cc44dSYu Zhao 827e277d2fcSYu Zhao if (dev != dev->sriov->dev) 828d1b054daSYu Zhao pci_dev_put(dev->sriov->dev); 829d1b054daSYu Zhao 830d1b054daSYu Zhao kfree(dev->sriov); 831d1b054daSYu Zhao dev->sriov = NULL; 832d1b054daSYu Zhao } 833d1b054daSYu Zhao 8348c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev) 8358c5cdb6aSYu Zhao { 8368c5cdb6aSYu Zhao int i; 8378c5cdb6aSYu Zhao u16 ctrl; 8388c5cdb6aSYu Zhao struct pci_sriov *iov = dev->sriov; 8398c5cdb6aSYu Zhao 8408c5cdb6aSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); 8418c5cdb6aSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) 8428c5cdb6aSYu Zhao return; 8438c5cdb6aSYu Zhao 844ff26449eSTony Nguyen /* 845ff26449eSTony Nguyen * Restore PCI_SRIOV_CTRL_ARI before pci_iov_set_numvfs() because 846ff26449eSTony Nguyen * it reads offset & stride, which depend on PCI_SRIOV_CTRL_ARI. 847ff26449eSTony Nguyen */ 848ff26449eSTony Nguyen ctrl &= ~PCI_SRIOV_CTRL_ARI; 849ff26449eSTony Nguyen ctrl |= iov->ctrl & PCI_SRIOV_CTRL_ARI; 850ff26449eSTony Nguyen pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, ctrl); 851ff26449eSTony Nguyen 85239098edbSDenis Efremov for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) 85339098edbSDenis Efremov pci_update_resource(dev, i + PCI_IOV_RESOURCES); 8548c5cdb6aSYu Zhao 8558c5cdb6aSYu Zhao pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); 856f59dca27SWei Yang pci_iov_set_numvfs(dev, iov->num_VFs); 8578c5cdb6aSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 8588c5cdb6aSYu Zhao if (iov->ctrl & PCI_SRIOV_CTRL_VFE) 8598c5cdb6aSYu Zhao msleep(100); 8608c5cdb6aSYu Zhao } 8618c5cdb6aSYu Zhao 862d1b054daSYu Zhao /** 863d1b054daSYu Zhao * pci_iov_init - initialize the IOV capability 864d1b054daSYu Zhao * @dev: the PCI device 865d1b054daSYu Zhao * 866d1b054daSYu Zhao * Returns 0 on success, or negative on failure. 867d1b054daSYu Zhao */ 868d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev) 869d1b054daSYu Zhao { 870d1b054daSYu Zhao int pos; 871d1b054daSYu Zhao 8725f4d91a1SKenji Kaneshige if (!pci_is_pcie(dev)) 873d1b054daSYu Zhao return -ENODEV; 874d1b054daSYu Zhao 875d1b054daSYu Zhao pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); 876d1b054daSYu Zhao if (pos) 877d1b054daSYu Zhao return sriov_init(dev, pos); 878d1b054daSYu Zhao 879d1b054daSYu Zhao return -ENODEV; 880d1b054daSYu Zhao } 881d1b054daSYu Zhao 882d1b054daSYu Zhao /** 883d1b054daSYu Zhao * pci_iov_release - release resources used by the IOV capability 884d1b054daSYu Zhao * @dev: the PCI device 885d1b054daSYu Zhao */ 886d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev) 887d1b054daSYu Zhao { 888d1b054daSYu Zhao if (dev->is_physfn) 889d1b054daSYu Zhao sriov_release(dev); 890d1b054daSYu Zhao } 891d1b054daSYu Zhao 892d1b054daSYu Zhao /** 89338972375SJakub Kicinski * pci_iov_remove - clean up SR-IOV state after PF driver is detached 89438972375SJakub Kicinski * @dev: the PCI device 89538972375SJakub Kicinski */ 89638972375SJakub Kicinski void pci_iov_remove(struct pci_dev *dev) 89738972375SJakub Kicinski { 89838972375SJakub Kicinski struct pci_sriov *iov = dev->sriov; 89938972375SJakub Kicinski 90038972375SJakub Kicinski if (!dev->is_physfn) 90138972375SJakub Kicinski return; 90238972375SJakub Kicinski 90338972375SJakub Kicinski iov->driver_max_VFs = iov->total_VFs; 90438972375SJakub Kicinski if (iov->num_VFs) 90538972375SJakub Kicinski pci_warn(dev, "driver left SR-IOV enabled after remove\n"); 90638972375SJakub Kicinski } 90738972375SJakub Kicinski 90838972375SJakub Kicinski /** 9096ffa2489SBjorn Helgaas * pci_iov_update_resource - update a VF BAR 9106ffa2489SBjorn Helgaas * @dev: the PCI device 9116ffa2489SBjorn Helgaas * @resno: the resource number 9126ffa2489SBjorn Helgaas * 9136ffa2489SBjorn Helgaas * Update a VF BAR in the SR-IOV capability of a PF. 9146ffa2489SBjorn Helgaas */ 9156ffa2489SBjorn Helgaas void pci_iov_update_resource(struct pci_dev *dev, int resno) 9166ffa2489SBjorn Helgaas { 9176ffa2489SBjorn Helgaas struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL; 9186ffa2489SBjorn Helgaas struct resource *res = dev->resource + resno; 9196ffa2489SBjorn Helgaas int vf_bar = resno - PCI_IOV_RESOURCES; 9206ffa2489SBjorn Helgaas struct pci_bus_region region; 921546ba9f8SBjorn Helgaas u16 cmd; 9226ffa2489SBjorn Helgaas u32 new; 9236ffa2489SBjorn Helgaas int reg; 9246ffa2489SBjorn Helgaas 9256ffa2489SBjorn Helgaas /* 9266ffa2489SBjorn Helgaas * The generic pci_restore_bars() path calls this for all devices, 9276ffa2489SBjorn Helgaas * including VFs and non-SR-IOV devices. If this is not a PF, we 9286ffa2489SBjorn Helgaas * have nothing to do. 9296ffa2489SBjorn Helgaas */ 9306ffa2489SBjorn Helgaas if (!iov) 9316ffa2489SBjorn Helgaas return; 9326ffa2489SBjorn Helgaas 933546ba9f8SBjorn Helgaas pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &cmd); 934546ba9f8SBjorn Helgaas if ((cmd & PCI_SRIOV_CTRL_VFE) && (cmd & PCI_SRIOV_CTRL_MSE)) { 935546ba9f8SBjorn Helgaas dev_WARN(&dev->dev, "can't update enabled VF BAR%d %pR\n", 936546ba9f8SBjorn Helgaas vf_bar, res); 937546ba9f8SBjorn Helgaas return; 938546ba9f8SBjorn Helgaas } 939546ba9f8SBjorn Helgaas 9406ffa2489SBjorn Helgaas /* 9416ffa2489SBjorn Helgaas * Ignore unimplemented BARs, unused resource slots for 64-bit 9426ffa2489SBjorn Helgaas * BARs, and non-movable resources, e.g., those described via 9436ffa2489SBjorn Helgaas * Enhanced Allocation. 9446ffa2489SBjorn Helgaas */ 9456ffa2489SBjorn Helgaas if (!res->flags) 9466ffa2489SBjorn Helgaas return; 9476ffa2489SBjorn Helgaas 9486ffa2489SBjorn Helgaas if (res->flags & IORESOURCE_UNSET) 9496ffa2489SBjorn Helgaas return; 9506ffa2489SBjorn Helgaas 9516ffa2489SBjorn Helgaas if (res->flags & IORESOURCE_PCI_FIXED) 9526ffa2489SBjorn Helgaas return; 9536ffa2489SBjorn Helgaas 9546ffa2489SBjorn Helgaas pcibios_resource_to_bus(dev->bus, ®ion, res); 9556ffa2489SBjorn Helgaas new = region.start; 9566ffa2489SBjorn Helgaas new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK; 9576ffa2489SBjorn Helgaas 9586ffa2489SBjorn Helgaas reg = iov->pos + PCI_SRIOV_BAR + 4 * vf_bar; 9596ffa2489SBjorn Helgaas pci_write_config_dword(dev, reg, new); 9606ffa2489SBjorn Helgaas if (res->flags & IORESOURCE_MEM_64) { 9616ffa2489SBjorn Helgaas new = region.start >> 16 >> 16; 9626ffa2489SBjorn Helgaas pci_write_config_dword(dev, reg + 4, new); 9636ffa2489SBjorn Helgaas } 9646ffa2489SBjorn Helgaas } 9656ffa2489SBjorn Helgaas 966978d2d68SWei Yang resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, 967978d2d68SWei Yang int resno) 968978d2d68SWei Yang { 969978d2d68SWei Yang return pci_iov_resource_size(dev, resno); 970978d2d68SWei Yang } 971978d2d68SWei Yang 9728c5cdb6aSYu Zhao /** 9736faf17f6SChris Wright * pci_sriov_resource_alignment - get resource alignment for VF BAR 9746faf17f6SChris Wright * @dev: the PCI device 9756faf17f6SChris Wright * @resno: the resource number 9766faf17f6SChris Wright * 9776faf17f6SChris Wright * Returns the alignment of the VF BAR found in the SR-IOV capability. 9786faf17f6SChris Wright * This is not the same as the resource size which is defined as 9796faf17f6SChris Wright * the VF BAR size multiplied by the number of VFs. The alignment 9806faf17f6SChris Wright * is just the VF BAR size. 9816faf17f6SChris Wright */ 9820e52247aSCam Macdonell resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno) 9836faf17f6SChris Wright { 984978d2d68SWei Yang return pcibios_iov_resource_alignment(dev, resno); 9856faf17f6SChris Wright } 9866faf17f6SChris Wright 9876faf17f6SChris Wright /** 9888c5cdb6aSYu Zhao * pci_restore_iov_state - restore the state of the IOV capability 9898c5cdb6aSYu Zhao * @dev: the PCI device 9908c5cdb6aSYu Zhao */ 9918c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev) 9928c5cdb6aSYu Zhao { 9938c5cdb6aSYu Zhao if (dev->is_physfn) 9948c5cdb6aSYu Zhao sriov_restore_state(dev); 9958c5cdb6aSYu Zhao } 996a28724b0SYu Zhao 997a28724b0SYu Zhao /** 998608c0d88SBryant G. Ly * pci_vf_drivers_autoprobe - set PF property drivers_autoprobe for VFs 999608c0d88SBryant G. Ly * @dev: the PCI device 1000608c0d88SBryant G. Ly * @auto_probe: set VF drivers auto probe flag 1001608c0d88SBryant G. Ly */ 1002608c0d88SBryant G. Ly void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool auto_probe) 1003608c0d88SBryant G. Ly { 1004608c0d88SBryant G. Ly if (dev->is_physfn) 1005608c0d88SBryant G. Ly dev->sriov->drivers_autoprobe = auto_probe; 1006608c0d88SBryant G. Ly } 1007608c0d88SBryant G. Ly 1008608c0d88SBryant G. Ly /** 1009a28724b0SYu Zhao * pci_iov_bus_range - find bus range used by Virtual Function 1010a28724b0SYu Zhao * @bus: the PCI bus 1011a28724b0SYu Zhao * 1012a28724b0SYu Zhao * Returns max number of buses (exclude current one) used by Virtual 1013a28724b0SYu Zhao * Functions. 1014a28724b0SYu Zhao */ 1015a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus) 1016a28724b0SYu Zhao { 1017a28724b0SYu Zhao int max = 0; 1018a28724b0SYu Zhao struct pci_dev *dev; 1019a28724b0SYu Zhao 1020a28724b0SYu Zhao list_for_each_entry(dev, &bus->devices, bus_list) { 1021a28724b0SYu Zhao if (!dev->is_physfn) 1022a28724b0SYu Zhao continue; 10234449f079SWei Yang if (dev->sriov->max_VF_buses > max) 10244449f079SWei Yang max = dev->sriov->max_VF_buses; 1025a28724b0SYu Zhao } 1026a28724b0SYu Zhao 1027a28724b0SYu Zhao return max ? max - bus->number : 0; 1028a28724b0SYu Zhao } 1029dd7cc44dSYu Zhao 1030dd7cc44dSYu Zhao /** 1031dd7cc44dSYu Zhao * pci_enable_sriov - enable the SR-IOV capability 1032dd7cc44dSYu Zhao * @dev: the PCI device 103352a8873bSRandy Dunlap * @nr_virtfn: number of virtual functions to enable 1034dd7cc44dSYu Zhao * 1035dd7cc44dSYu Zhao * Returns 0 on success, or negative on failure. 1036dd7cc44dSYu Zhao */ 1037dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) 1038dd7cc44dSYu Zhao { 1039dd7cc44dSYu Zhao might_sleep(); 1040dd7cc44dSYu Zhao 1041dd7cc44dSYu Zhao if (!dev->is_physfn) 1042652d1100SStefan Assmann return -ENOSYS; 1043dd7cc44dSYu Zhao 1044dd7cc44dSYu Zhao return sriov_enable(dev, nr_virtfn); 1045dd7cc44dSYu Zhao } 1046dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov); 1047dd7cc44dSYu Zhao 1048dd7cc44dSYu Zhao /** 1049dd7cc44dSYu Zhao * pci_disable_sriov - disable the SR-IOV capability 1050dd7cc44dSYu Zhao * @dev: the PCI device 1051dd7cc44dSYu Zhao */ 1052dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev) 1053dd7cc44dSYu Zhao { 1054dd7cc44dSYu Zhao might_sleep(); 1055dd7cc44dSYu Zhao 1056dd7cc44dSYu Zhao if (!dev->is_physfn) 1057dd7cc44dSYu Zhao return; 1058dd7cc44dSYu Zhao 1059dd7cc44dSYu Zhao sriov_disable(dev); 1060dd7cc44dSYu Zhao } 1061dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov); 106274bb1bccSYu Zhao 106374bb1bccSYu Zhao /** 1064fb8a0d9dSWilliams, Mitch A * pci_num_vf - return number of VFs associated with a PF device_release_driver 1065fb8a0d9dSWilliams, Mitch A * @dev: the PCI device 1066fb8a0d9dSWilliams, Mitch A * 1067fb8a0d9dSWilliams, Mitch A * Returns number of VFs, or 0 if SR-IOV is not enabled. 1068fb8a0d9dSWilliams, Mitch A */ 1069fb8a0d9dSWilliams, Mitch A int pci_num_vf(struct pci_dev *dev) 1070fb8a0d9dSWilliams, Mitch A { 10711452cd76SBjorn Helgaas if (!dev->is_physfn) 1072fb8a0d9dSWilliams, Mitch A return 0; 10731452cd76SBjorn Helgaas 10746b136724SBjorn Helgaas return dev->sriov->num_VFs; 1075fb8a0d9dSWilliams, Mitch A } 1076fb8a0d9dSWilliams, Mitch A EXPORT_SYMBOL_GPL(pci_num_vf); 1077bff73156SDonald Dutile 1078bff73156SDonald Dutile /** 10795a8eb242SAlexander Duyck * pci_vfs_assigned - returns number of VFs are assigned to a guest 10805a8eb242SAlexander Duyck * @dev: the PCI device 10815a8eb242SAlexander Duyck * 10825a8eb242SAlexander Duyck * Returns number of VFs belonging to this device that are assigned to a guest. 1083652d1100SStefan Assmann * If device is not a physical function returns 0. 10845a8eb242SAlexander Duyck */ 10855a8eb242SAlexander Duyck int pci_vfs_assigned(struct pci_dev *dev) 10865a8eb242SAlexander Duyck { 10875a8eb242SAlexander Duyck struct pci_dev *vfdev; 10885a8eb242SAlexander Duyck unsigned int vfs_assigned = 0; 10895a8eb242SAlexander Duyck unsigned short dev_id; 10905a8eb242SAlexander Duyck 10915a8eb242SAlexander Duyck /* only search if we are a PF */ 10925a8eb242SAlexander Duyck if (!dev->is_physfn) 10935a8eb242SAlexander Duyck return 0; 10945a8eb242SAlexander Duyck 10955a8eb242SAlexander Duyck /* 10965a8eb242SAlexander Duyck * determine the device ID for the VFs, the vendor ID will be the 10975a8eb242SAlexander Duyck * same as the PF so there is no need to check for that one 10985a8eb242SAlexander Duyck */ 10993142d832SFilippo Sironi dev_id = dev->sriov->vf_device; 11005a8eb242SAlexander Duyck 11015a8eb242SAlexander Duyck /* loop through all the VFs to see if we own any that are assigned */ 11025a8eb242SAlexander Duyck vfdev = pci_get_device(dev->vendor, dev_id, NULL); 11035a8eb242SAlexander Duyck while (vfdev) { 11045a8eb242SAlexander Duyck /* 11055a8eb242SAlexander Duyck * It is considered assigned if it is a virtual function with 11065a8eb242SAlexander Duyck * our dev as the physical function and the assigned bit is set 11075a8eb242SAlexander Duyck */ 11085a8eb242SAlexander Duyck if (vfdev->is_virtfn && (vfdev->physfn == dev) && 1109be63497cSEthan Zhao pci_is_dev_assigned(vfdev)) 11105a8eb242SAlexander Duyck vfs_assigned++; 11115a8eb242SAlexander Duyck 11125a8eb242SAlexander Duyck vfdev = pci_get_device(dev->vendor, dev_id, vfdev); 11135a8eb242SAlexander Duyck } 11145a8eb242SAlexander Duyck 11155a8eb242SAlexander Duyck return vfs_assigned; 11165a8eb242SAlexander Duyck } 11175a8eb242SAlexander Duyck EXPORT_SYMBOL_GPL(pci_vfs_assigned); 11185a8eb242SAlexander Duyck 11195a8eb242SAlexander Duyck /** 1120bff73156SDonald Dutile * pci_sriov_set_totalvfs -- reduce the TotalVFs available 1121bff73156SDonald Dutile * @dev: the PCI PF device 11222094f167SRandy Dunlap * @numvfs: number that should be used for TotalVFs supported 1123bff73156SDonald Dutile * 1124bff73156SDonald Dutile * Should be called from PF driver's probe routine with 1125bff73156SDonald Dutile * device's mutex held. 1126bff73156SDonald Dutile * 1127bff73156SDonald Dutile * Returns 0 if PF is an SRIOV-capable device and 1128652d1100SStefan Assmann * value of numvfs valid. If not a PF return -ENOSYS; 1129652d1100SStefan Assmann * if numvfs is invalid return -EINVAL; 1130bff73156SDonald Dutile * if VFs already enabled, return -EBUSY. 1131bff73156SDonald Dutile */ 1132bff73156SDonald Dutile int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) 1133bff73156SDonald Dutile { 1134652d1100SStefan Assmann if (!dev->is_physfn) 1135652d1100SStefan Assmann return -ENOSYS; 113651259d00SBjorn Helgaas 1137652d1100SStefan Assmann if (numvfs > dev->sriov->total_VFs) 1138bff73156SDonald Dutile return -EINVAL; 1139bff73156SDonald Dutile 1140bff73156SDonald Dutile /* Shouldn't change if VFs already enabled */ 1141bff73156SDonald Dutile if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE) 1142bff73156SDonald Dutile return -EBUSY; 1143bff73156SDonald Dutile 114451259d00SBjorn Helgaas dev->sriov->driver_max_VFs = numvfs; 1145bff73156SDonald Dutile return 0; 1146bff73156SDonald Dutile } 1147bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs); 1148bff73156SDonald Dutile 1149bff73156SDonald Dutile /** 1150ddc191f5SJonghwan Choi * pci_sriov_get_totalvfs -- get total VFs supported on this device 1151bff73156SDonald Dutile * @dev: the PCI PF device 1152bff73156SDonald Dutile * 1153bff73156SDonald Dutile * For a PCIe device with SRIOV support, return the PCIe 11546b136724SBjorn Helgaas * SRIOV capability value of TotalVFs or the value of driver_max_VFs 1155652d1100SStefan Assmann * if the driver reduced it. Otherwise 0. 1156bff73156SDonald Dutile */ 1157bff73156SDonald Dutile int pci_sriov_get_totalvfs(struct pci_dev *dev) 1158bff73156SDonald Dutile { 11591452cd76SBjorn Helgaas if (!dev->is_physfn) 1160652d1100SStefan Assmann return 0; 1161bff73156SDonald Dutile 11626b136724SBjorn Helgaas return dev->sriov->driver_max_VFs; 1163bff73156SDonald Dutile } 1164bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs); 11658effc395SAlexander Duyck 11668effc395SAlexander Duyck /** 11678effc395SAlexander Duyck * pci_sriov_configure_simple - helper to configure SR-IOV 11688effc395SAlexander Duyck * @dev: the PCI device 11698effc395SAlexander Duyck * @nr_virtfn: number of virtual functions to enable, 0 to disable 11708effc395SAlexander Duyck * 11718effc395SAlexander Duyck * Enable or disable SR-IOV for devices that don't require any PF setup 11728effc395SAlexander Duyck * before enabling SR-IOV. Return value is negative on error, or number of 11738effc395SAlexander Duyck * VFs allocated on success. 11748effc395SAlexander Duyck */ 11758effc395SAlexander Duyck int pci_sriov_configure_simple(struct pci_dev *dev, int nr_virtfn) 11768effc395SAlexander Duyck { 11778effc395SAlexander Duyck int rc; 11788effc395SAlexander Duyck 11798effc395SAlexander Duyck might_sleep(); 11808effc395SAlexander Duyck 11818effc395SAlexander Duyck if (!dev->is_physfn) 11828effc395SAlexander Duyck return -ENODEV; 11838effc395SAlexander Duyck 11848effc395SAlexander Duyck if (pci_vfs_assigned(dev)) { 11858effc395SAlexander Duyck pci_warn(dev, "Cannot modify SR-IOV while VFs are assigned\n"); 11868effc395SAlexander Duyck return -EPERM; 11878effc395SAlexander Duyck } 11888effc395SAlexander Duyck 11898effc395SAlexander Duyck if (nr_virtfn == 0) { 11908effc395SAlexander Duyck sriov_disable(dev); 11918effc395SAlexander Duyck return 0; 11928effc395SAlexander Duyck } 11938effc395SAlexander Duyck 11948effc395SAlexander Duyck rc = sriov_enable(dev, nr_virtfn); 11958effc395SAlexander Duyck if (rc < 0) 11968effc395SAlexander Duyck return rc; 11978effc395SAlexander Duyck 11988effc395SAlexander Duyck return nr_virtfn; 11998effc395SAlexander Duyck } 12008effc395SAlexander Duyck EXPORT_SYMBOL_GPL(pci_sriov_configure_simple); 1201