1d1b054daSYu Zhao /* 2d1b054daSYu Zhao * drivers/pci/iov.c 3d1b054daSYu Zhao * 4d1b054daSYu Zhao * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com> 5d1b054daSYu Zhao * 6d1b054daSYu Zhao * PCI Express I/O Virtualization (IOV) support. 7d1b054daSYu Zhao * Single Root IOV 1.0 8302b4215SYu Zhao * Address Translation Service 1.0 9d1b054daSYu Zhao */ 10d1b054daSYu Zhao 11d1b054daSYu Zhao #include <linux/pci.h> 125a0e3ad6STejun Heo #include <linux/slab.h> 13d1b054daSYu Zhao #include <linux/mutex.h> 14363c75dbSPaul Gortmaker #include <linux/export.h> 15d1b054daSYu Zhao #include <linux/string.h> 16d1b054daSYu Zhao #include <linux/delay.h> 175cdede24SJoerg Roedel #include <linux/pci-ats.h> 18d1b054daSYu Zhao #include "pci.h" 19d1b054daSYu Zhao 20dd7cc44dSYu Zhao #define VIRTFN_ID_LEN 16 21d1b054daSYu Zhao 22a28724b0SYu Zhao static inline u8 virtfn_bus(struct pci_dev *dev, int id) 23a28724b0SYu Zhao { 24a28724b0SYu Zhao return dev->bus->number + ((dev->devfn + dev->sriov->offset + 25a28724b0SYu Zhao dev->sriov->stride * id) >> 8); 26a28724b0SYu Zhao } 27a28724b0SYu Zhao 28a28724b0SYu Zhao static inline u8 virtfn_devfn(struct pci_dev *dev, int id) 29a28724b0SYu Zhao { 30a28724b0SYu Zhao return (dev->devfn + dev->sriov->offset + 31a28724b0SYu Zhao dev->sriov->stride * id) & 0xff; 32a28724b0SYu Zhao } 33a28724b0SYu Zhao 34dd7cc44dSYu Zhao static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) 35dd7cc44dSYu Zhao { 36dd7cc44dSYu Zhao struct pci_bus *child; 37dd7cc44dSYu Zhao 38dd7cc44dSYu Zhao if (bus->number == busnr) 39dd7cc44dSYu Zhao return bus; 40dd7cc44dSYu Zhao 41dd7cc44dSYu Zhao child = pci_find_bus(pci_domain_nr(bus), busnr); 42dd7cc44dSYu Zhao if (child) 43dd7cc44dSYu Zhao return child; 44dd7cc44dSYu Zhao 45dd7cc44dSYu Zhao child = pci_add_new_bus(bus, NULL, busnr); 46dd7cc44dSYu Zhao if (!child) 47dd7cc44dSYu Zhao return NULL; 48dd7cc44dSYu Zhao 49b7eac055SYinghai Lu pci_bus_insert_busn_res(child, busnr, busnr); 50dd7cc44dSYu Zhao 51dd7cc44dSYu Zhao return child; 52dd7cc44dSYu Zhao } 53dd7cc44dSYu Zhao 54dc087f2fSJiang Liu static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) 55dd7cc44dSYu Zhao { 56dc087f2fSJiang Liu if (physbus != virtbus && list_empty(&virtbus->devices)) 57dc087f2fSJiang Liu pci_remove_bus(virtbus); 58dd7cc44dSYu Zhao } 59dd7cc44dSYu Zhao 60dd7cc44dSYu Zhao static int virtfn_add(struct pci_dev *dev, int id, int reset) 61dd7cc44dSYu Zhao { 62dd7cc44dSYu Zhao int i; 63dc087f2fSJiang Liu int rc = -ENOMEM; 64dd7cc44dSYu Zhao u64 size; 65dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 66dd7cc44dSYu Zhao struct pci_dev *virtfn; 67dd7cc44dSYu Zhao struct resource *res; 68dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 698b1fce04SGu Zheng struct pci_bus *bus; 70dd7cc44dSYu Zhao 71dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 728b1fce04SGu Zheng bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); 73dc087f2fSJiang Liu if (!bus) 74dc087f2fSJiang Liu goto failed; 75dc087f2fSJiang Liu 76dc087f2fSJiang Liu virtfn = pci_alloc_dev(bus); 77dc087f2fSJiang Liu if (!virtfn) 78dc087f2fSJiang Liu goto failed0; 79dc087f2fSJiang Liu 80dd7cc44dSYu Zhao virtfn->devfn = virtfn_devfn(dev, id); 81dd7cc44dSYu Zhao virtfn->vendor = dev->vendor; 82dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); 83dd7cc44dSYu Zhao pci_setup_device(virtfn); 84dd7cc44dSYu Zhao virtfn->dev.parent = dev->dev.parent; 85fbf33f51SXudong Hao virtfn->physfn = pci_dev_get(dev); 86fbf33f51SXudong Hao virtfn->is_virtfn = 1; 87dd7cc44dSYu Zhao 88dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 89dd7cc44dSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 90dd7cc44dSYu Zhao if (!res->parent) 91dd7cc44dSYu Zhao continue; 92dd7cc44dSYu Zhao virtfn->resource[i].name = pci_name(virtfn); 93dd7cc44dSYu Zhao virtfn->resource[i].flags = res->flags; 94dd7cc44dSYu Zhao size = resource_size(res); 956b136724SBjorn Helgaas do_div(size, iov->total_VFs); 96dd7cc44dSYu Zhao virtfn->resource[i].start = res->start + size * id; 97dd7cc44dSYu Zhao virtfn->resource[i].end = virtfn->resource[i].start + size - 1; 98dd7cc44dSYu Zhao rc = request_resource(res, &virtfn->resource[i]); 99dd7cc44dSYu Zhao BUG_ON(rc); 100dd7cc44dSYu Zhao } 101dd7cc44dSYu Zhao 102dd7cc44dSYu Zhao if (reset) 1038c1c699fSYu Zhao __pci_reset_function(virtfn); 104dd7cc44dSYu Zhao 105dd7cc44dSYu Zhao pci_device_add(virtfn, virtfn->bus); 106dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 107dd7cc44dSYu Zhao 108dd7cc44dSYu Zhao rc = pci_bus_add_device(virtfn); 109dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 110dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); 111dd7cc44dSYu Zhao if (rc) 112dd7cc44dSYu Zhao goto failed1; 113dd7cc44dSYu Zhao rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn"); 114dd7cc44dSYu Zhao if (rc) 115dd7cc44dSYu Zhao goto failed2; 116dd7cc44dSYu Zhao 117dd7cc44dSYu Zhao kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE); 118dd7cc44dSYu Zhao 119dd7cc44dSYu Zhao return 0; 120dd7cc44dSYu Zhao 121dd7cc44dSYu Zhao failed2: 122dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 123dd7cc44dSYu Zhao failed1: 124dd7cc44dSYu Zhao pci_dev_put(dev); 125dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 126210647afSYinghai Lu pci_stop_and_remove_bus_device(virtfn); 127dc087f2fSJiang Liu failed0: 128dc087f2fSJiang Liu virtfn_remove_bus(dev->bus, bus); 129dc087f2fSJiang Liu failed: 130dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 131dd7cc44dSYu Zhao 132dd7cc44dSYu Zhao return rc; 133dd7cc44dSYu Zhao } 134dd7cc44dSYu Zhao 135dd7cc44dSYu Zhao static void virtfn_remove(struct pci_dev *dev, int id, int reset) 136dd7cc44dSYu Zhao { 137dd7cc44dSYu Zhao char buf[VIRTFN_ID_LEN]; 138dd7cc44dSYu Zhao struct pci_dev *virtfn; 139dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 140dd7cc44dSYu Zhao 141dc087f2fSJiang Liu virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), 142dc087f2fSJiang Liu virtfn_bus(dev, id), 143dc087f2fSJiang Liu virtfn_devfn(dev, id)); 144dd7cc44dSYu Zhao if (!virtfn) 145dd7cc44dSYu Zhao return; 146dd7cc44dSYu Zhao 147dd7cc44dSYu Zhao if (reset) { 148dd7cc44dSYu Zhao device_release_driver(&virtfn->dev); 1498c1c699fSYu Zhao __pci_reset_function(virtfn); 150dd7cc44dSYu Zhao } 151dd7cc44dSYu Zhao 152dd7cc44dSYu Zhao sprintf(buf, "virtfn%u", id); 153dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, buf); 15409cedbefSYinghai Lu /* 15509cedbefSYinghai Lu * pci_stop_dev() could have been called for this virtfn already, 15609cedbefSYinghai Lu * so the directory for the virtfn may have been removed before. 15709cedbefSYinghai Lu * Double check to avoid spurious sysfs warnings. 15809cedbefSYinghai Lu */ 15909cedbefSYinghai Lu if (virtfn->dev.kobj.sd) 160dd7cc44dSYu Zhao sysfs_remove_link(&virtfn->dev.kobj, "physfn"); 161dd7cc44dSYu Zhao 162dd7cc44dSYu Zhao mutex_lock(&iov->dev->sriov->lock); 163210647afSYinghai Lu pci_stop_and_remove_bus_device(virtfn); 164dc087f2fSJiang Liu virtfn_remove_bus(dev->bus, virtfn->bus); 165dd7cc44dSYu Zhao mutex_unlock(&iov->dev->sriov->lock); 166dd7cc44dSYu Zhao 167dc087f2fSJiang Liu /* balance pci_get_domain_bus_and_slot() */ 168dc087f2fSJiang Liu pci_dev_put(virtfn); 169dd7cc44dSYu Zhao pci_dev_put(dev); 170dd7cc44dSYu Zhao } 171dd7cc44dSYu Zhao 17274bb1bccSYu Zhao static int sriov_migration(struct pci_dev *dev) 17374bb1bccSYu Zhao { 17474bb1bccSYu Zhao u16 status; 17574bb1bccSYu Zhao struct pci_sriov *iov = dev->sriov; 17674bb1bccSYu Zhao 1776b136724SBjorn Helgaas if (!iov->num_VFs) 17874bb1bccSYu Zhao return 0; 17974bb1bccSYu Zhao 18074bb1bccSYu Zhao if (!(iov->cap & PCI_SRIOV_CAP_VFM)) 18174bb1bccSYu Zhao return 0; 18274bb1bccSYu Zhao 18374bb1bccSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status); 18474bb1bccSYu Zhao if (!(status & PCI_SRIOV_STATUS_VFM)) 18574bb1bccSYu Zhao return 0; 18674bb1bccSYu Zhao 18774bb1bccSYu Zhao schedule_work(&iov->mtask); 18874bb1bccSYu Zhao 18974bb1bccSYu Zhao return 1; 19074bb1bccSYu Zhao } 19174bb1bccSYu Zhao 19274bb1bccSYu Zhao static void sriov_migration_task(struct work_struct *work) 19374bb1bccSYu Zhao { 19474bb1bccSYu Zhao int i; 19574bb1bccSYu Zhao u8 state; 19674bb1bccSYu Zhao u16 status; 19774bb1bccSYu Zhao struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); 19874bb1bccSYu Zhao 1996b136724SBjorn Helgaas for (i = iov->initial_VFs; i < iov->num_VFs; i++) { 20074bb1bccSYu Zhao state = readb(iov->mstate + i); 20174bb1bccSYu Zhao if (state == PCI_SRIOV_VFM_MI) { 20274bb1bccSYu Zhao writeb(PCI_SRIOV_VFM_AV, iov->mstate + i); 20374bb1bccSYu Zhao state = readb(iov->mstate + i); 20474bb1bccSYu Zhao if (state == PCI_SRIOV_VFM_AV) 20574bb1bccSYu Zhao virtfn_add(iov->self, i, 1); 20674bb1bccSYu Zhao } else if (state == PCI_SRIOV_VFM_MO) { 20774bb1bccSYu Zhao virtfn_remove(iov->self, i, 1); 20874bb1bccSYu Zhao writeb(PCI_SRIOV_VFM_UA, iov->mstate + i); 20974bb1bccSYu Zhao state = readb(iov->mstate + i); 21074bb1bccSYu Zhao if (state == PCI_SRIOV_VFM_AV) 21174bb1bccSYu Zhao virtfn_add(iov->self, i, 0); 21274bb1bccSYu Zhao } 21374bb1bccSYu Zhao } 21474bb1bccSYu Zhao 21574bb1bccSYu Zhao pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status); 21674bb1bccSYu Zhao status &= ~PCI_SRIOV_STATUS_VFM; 21774bb1bccSYu Zhao pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status); 21874bb1bccSYu Zhao } 21974bb1bccSYu Zhao 22074bb1bccSYu Zhao static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn) 22174bb1bccSYu Zhao { 22274bb1bccSYu Zhao int bir; 22374bb1bccSYu Zhao u32 table; 22474bb1bccSYu Zhao resource_size_t pa; 22574bb1bccSYu Zhao struct pci_sriov *iov = dev->sriov; 22674bb1bccSYu Zhao 2276b136724SBjorn Helgaas if (nr_virtfn <= iov->initial_VFs) 22874bb1bccSYu Zhao return 0; 22974bb1bccSYu Zhao 23074bb1bccSYu Zhao pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table); 23174bb1bccSYu Zhao bir = PCI_SRIOV_VFM_BIR(table); 23274bb1bccSYu Zhao if (bir > PCI_STD_RESOURCE_END) 23374bb1bccSYu Zhao return -EIO; 23474bb1bccSYu Zhao 23574bb1bccSYu Zhao table = PCI_SRIOV_VFM_OFFSET(table); 23674bb1bccSYu Zhao if (table + nr_virtfn > pci_resource_len(dev, bir)) 23774bb1bccSYu Zhao return -EIO; 23874bb1bccSYu Zhao 23974bb1bccSYu Zhao pa = pci_resource_start(dev, bir) + table; 24074bb1bccSYu Zhao iov->mstate = ioremap(pa, nr_virtfn); 24174bb1bccSYu Zhao if (!iov->mstate) 24274bb1bccSYu Zhao return -ENOMEM; 24374bb1bccSYu Zhao 24474bb1bccSYu Zhao INIT_WORK(&iov->mtask, sriov_migration_task); 24574bb1bccSYu Zhao 24674bb1bccSYu Zhao iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR; 24774bb1bccSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 24874bb1bccSYu Zhao 24974bb1bccSYu Zhao return 0; 25074bb1bccSYu Zhao } 25174bb1bccSYu Zhao 25274bb1bccSYu Zhao static void sriov_disable_migration(struct pci_dev *dev) 25374bb1bccSYu Zhao { 25474bb1bccSYu Zhao struct pci_sriov *iov = dev->sriov; 25574bb1bccSYu Zhao 25674bb1bccSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR); 25774bb1bccSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 25874bb1bccSYu Zhao 25974bb1bccSYu Zhao cancel_work_sync(&iov->mtask); 26074bb1bccSYu Zhao iounmap(iov->mstate); 26174bb1bccSYu Zhao } 26274bb1bccSYu Zhao 263dd7cc44dSYu Zhao static int sriov_enable(struct pci_dev *dev, int nr_virtfn) 264dd7cc44dSYu Zhao { 265dd7cc44dSYu Zhao int rc; 266dd7cc44dSYu Zhao int i, j; 267dd7cc44dSYu Zhao int nres; 268dd7cc44dSYu Zhao u16 offset, stride, initial; 269dd7cc44dSYu Zhao struct resource *res; 270dd7cc44dSYu Zhao struct pci_dev *pdev; 271dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 272bbef98abSRam Pai int bars = 0; 273dd7cc44dSYu Zhao 274dd7cc44dSYu Zhao if (!nr_virtfn) 275dd7cc44dSYu Zhao return 0; 276dd7cc44dSYu Zhao 2776b136724SBjorn Helgaas if (iov->num_VFs) 278dd7cc44dSYu Zhao return -EINVAL; 279dd7cc44dSYu Zhao 280dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial); 2816b136724SBjorn Helgaas if (initial > iov->total_VFs || 2826b136724SBjorn Helgaas (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs))) 283dd7cc44dSYu Zhao return -EIO; 284dd7cc44dSYu Zhao 2856b136724SBjorn Helgaas if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs || 286dd7cc44dSYu Zhao (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial))) 287dd7cc44dSYu Zhao return -EINVAL; 288dd7cc44dSYu Zhao 289dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn); 290dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset); 291dd7cc44dSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride); 292dd7cc44dSYu Zhao if (!offset || (nr_virtfn > 1 && !stride)) 293dd7cc44dSYu Zhao return -EIO; 294dd7cc44dSYu Zhao 295dd7cc44dSYu Zhao nres = 0; 296dd7cc44dSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 297bbef98abSRam Pai bars |= (1 << (i + PCI_IOV_RESOURCES)); 298dd7cc44dSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 299dd7cc44dSYu Zhao if (res->parent) 300dd7cc44dSYu Zhao nres++; 301dd7cc44dSYu Zhao } 302dd7cc44dSYu Zhao if (nres != iov->nres) { 303dd7cc44dSYu Zhao dev_err(&dev->dev, "not enough MMIO resources for SR-IOV\n"); 304dd7cc44dSYu Zhao return -ENOMEM; 305dd7cc44dSYu Zhao } 306dd7cc44dSYu Zhao 307dd7cc44dSYu Zhao iov->offset = offset; 308dd7cc44dSYu Zhao iov->stride = stride; 309dd7cc44dSYu Zhao 310b918c62eSYinghai Lu if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->busn_res.end) { 311dd7cc44dSYu Zhao dev_err(&dev->dev, "SR-IOV: bus number out of range\n"); 312dd7cc44dSYu Zhao return -ENOMEM; 313dd7cc44dSYu Zhao } 314dd7cc44dSYu Zhao 315bbef98abSRam Pai if (pci_enable_resources(dev, bars)) { 316bbef98abSRam Pai dev_err(&dev->dev, "SR-IOV: IOV BARS not allocated\n"); 317bbef98abSRam Pai return -ENOMEM; 318bbef98abSRam Pai } 319bbef98abSRam Pai 320dd7cc44dSYu Zhao if (iov->link != dev->devfn) { 321dd7cc44dSYu Zhao pdev = pci_get_slot(dev->bus, iov->link); 322dd7cc44dSYu Zhao if (!pdev) 323dd7cc44dSYu Zhao return -ENODEV; 324dd7cc44dSYu Zhao 325dc087f2fSJiang Liu if (!pdev->is_physfn) { 326dd7cc44dSYu Zhao pci_dev_put(pdev); 327dd7cc44dSYu Zhao return -ENODEV; 328dc087f2fSJiang Liu } 329dd7cc44dSYu Zhao 330dd7cc44dSYu Zhao rc = sysfs_create_link(&dev->dev.kobj, 331dd7cc44dSYu Zhao &pdev->dev.kobj, "dep_link"); 332dc087f2fSJiang Liu pci_dev_put(pdev); 333dd7cc44dSYu Zhao if (rc) 334dd7cc44dSYu Zhao return rc; 335dd7cc44dSYu Zhao } 336dd7cc44dSYu Zhao 337dd7cc44dSYu Zhao iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE; 338fb51ccbfSJan Kiszka pci_cfg_access_lock(dev); 339dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 340dd7cc44dSYu Zhao msleep(100); 341fb51ccbfSJan Kiszka pci_cfg_access_unlock(dev); 342dd7cc44dSYu Zhao 3436b136724SBjorn Helgaas iov->initial_VFs = initial; 344dd7cc44dSYu Zhao if (nr_virtfn < initial) 345dd7cc44dSYu Zhao initial = nr_virtfn; 346dd7cc44dSYu Zhao 347dd7cc44dSYu Zhao for (i = 0; i < initial; i++) { 348dd7cc44dSYu Zhao rc = virtfn_add(dev, i, 0); 349dd7cc44dSYu Zhao if (rc) 350dd7cc44dSYu Zhao goto failed; 351dd7cc44dSYu Zhao } 352dd7cc44dSYu Zhao 35374bb1bccSYu Zhao if (iov->cap & PCI_SRIOV_CAP_VFM) { 35474bb1bccSYu Zhao rc = sriov_enable_migration(dev, nr_virtfn); 35574bb1bccSYu Zhao if (rc) 35674bb1bccSYu Zhao goto failed; 35774bb1bccSYu Zhao } 35874bb1bccSYu Zhao 359dd7cc44dSYu Zhao kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); 3606b136724SBjorn Helgaas iov->num_VFs = nr_virtfn; 361dd7cc44dSYu Zhao 362dd7cc44dSYu Zhao return 0; 363dd7cc44dSYu Zhao 364dd7cc44dSYu Zhao failed: 365dd7cc44dSYu Zhao for (j = 0; j < i; j++) 366dd7cc44dSYu Zhao virtfn_remove(dev, j, 0); 367dd7cc44dSYu Zhao 368dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 369fb51ccbfSJan Kiszka pci_cfg_access_lock(dev); 370dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 371dd7cc44dSYu Zhao ssleep(1); 372fb51ccbfSJan Kiszka pci_cfg_access_unlock(dev); 373dd7cc44dSYu Zhao 374dd7cc44dSYu Zhao if (iov->link != dev->devfn) 375dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 376dd7cc44dSYu Zhao 377dd7cc44dSYu Zhao return rc; 378dd7cc44dSYu Zhao } 379dd7cc44dSYu Zhao 380dd7cc44dSYu Zhao static void sriov_disable(struct pci_dev *dev) 381dd7cc44dSYu Zhao { 382dd7cc44dSYu Zhao int i; 383dd7cc44dSYu Zhao struct pci_sriov *iov = dev->sriov; 384dd7cc44dSYu Zhao 3856b136724SBjorn Helgaas if (!iov->num_VFs) 386dd7cc44dSYu Zhao return; 387dd7cc44dSYu Zhao 38874bb1bccSYu Zhao if (iov->cap & PCI_SRIOV_CAP_VFM) 38974bb1bccSYu Zhao sriov_disable_migration(dev); 39074bb1bccSYu Zhao 3916b136724SBjorn Helgaas for (i = 0; i < iov->num_VFs; i++) 392dd7cc44dSYu Zhao virtfn_remove(dev, i, 0); 393dd7cc44dSYu Zhao 394dd7cc44dSYu Zhao iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); 395fb51ccbfSJan Kiszka pci_cfg_access_lock(dev); 396dd7cc44dSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 397dd7cc44dSYu Zhao ssleep(1); 398fb51ccbfSJan Kiszka pci_cfg_access_unlock(dev); 399dd7cc44dSYu Zhao 400dd7cc44dSYu Zhao if (iov->link != dev->devfn) 401dd7cc44dSYu Zhao sysfs_remove_link(&dev->dev.kobj, "dep_link"); 402dd7cc44dSYu Zhao 4036b136724SBjorn Helgaas iov->num_VFs = 0; 404dd7cc44dSYu Zhao } 405dd7cc44dSYu Zhao 406d1b054daSYu Zhao static int sriov_init(struct pci_dev *dev, int pos) 407d1b054daSYu Zhao { 408d1b054daSYu Zhao int i; 409d1b054daSYu Zhao int rc; 410d1b054daSYu Zhao int nres; 411d1b054daSYu Zhao u32 pgsz; 412d1b054daSYu Zhao u16 ctrl, total, offset, stride; 413d1b054daSYu Zhao struct pci_sriov *iov; 414d1b054daSYu Zhao struct resource *res; 415d1b054daSYu Zhao struct pci_dev *pdev; 416d1b054daSYu Zhao 41762f87c0eSYijing Wang if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END && 41862f87c0eSYijing Wang pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT) 419d1b054daSYu Zhao return -ENODEV; 420d1b054daSYu Zhao 421d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); 422d1b054daSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) { 423d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); 424d1b054daSYu Zhao ssleep(1); 425d1b054daSYu Zhao } 426d1b054daSYu Zhao 427d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_TOTAL_VF, &total); 428d1b054daSYu Zhao if (!total) 429d1b054daSYu Zhao return 0; 430d1b054daSYu Zhao 431d1b054daSYu Zhao ctrl = 0; 432d1b054daSYu Zhao list_for_each_entry(pdev, &dev->bus->devices, bus_list) 433d1b054daSYu Zhao if (pdev->is_physfn) 434d1b054daSYu Zhao goto found; 435d1b054daSYu Zhao 436d1b054daSYu Zhao pdev = NULL; 437d1b054daSYu Zhao if (pci_ari_enabled(dev->bus)) 438d1b054daSYu Zhao ctrl |= PCI_SRIOV_CTRL_ARI; 439d1b054daSYu Zhao 440d1b054daSYu Zhao found: 441d1b054daSYu Zhao pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); 442d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); 443d1b054daSYu Zhao pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); 444d1b054daSYu Zhao if (!offset || (total > 1 && !stride)) 445d1b054daSYu Zhao return -EIO; 446d1b054daSYu Zhao 447d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz); 448d1b054daSYu Zhao i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; 449d1b054daSYu Zhao pgsz &= ~((1 << i) - 1); 450d1b054daSYu Zhao if (!pgsz) 451d1b054daSYu Zhao return -EIO; 452d1b054daSYu Zhao 453d1b054daSYu Zhao pgsz &= ~(pgsz - 1); 4548161fe91SVaidyanathan Srinivasan pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); 455d1b054daSYu Zhao 456d1b054daSYu Zhao nres = 0; 457d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 458d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 459d1b054daSYu Zhao i += __pci_read_base(dev, pci_bar_unknown, res, 460d1b054daSYu Zhao pos + PCI_SRIOV_BAR + i * 4); 461d1b054daSYu Zhao if (!res->flags) 462d1b054daSYu Zhao continue; 463d1b054daSYu Zhao if (resource_size(res) & (PAGE_SIZE - 1)) { 464d1b054daSYu Zhao rc = -EIO; 465d1b054daSYu Zhao goto failed; 466d1b054daSYu Zhao } 467d1b054daSYu Zhao res->end = res->start + resource_size(res) * total - 1; 468d1b054daSYu Zhao nres++; 469d1b054daSYu Zhao } 470d1b054daSYu Zhao 471d1b054daSYu Zhao iov = kzalloc(sizeof(*iov), GFP_KERNEL); 472d1b054daSYu Zhao if (!iov) { 473d1b054daSYu Zhao rc = -ENOMEM; 474d1b054daSYu Zhao goto failed; 475d1b054daSYu Zhao } 476d1b054daSYu Zhao 477d1b054daSYu Zhao iov->pos = pos; 478d1b054daSYu Zhao iov->nres = nres; 479d1b054daSYu Zhao iov->ctrl = ctrl; 4806b136724SBjorn Helgaas iov->total_VFs = total; 481d1b054daSYu Zhao iov->offset = offset; 482d1b054daSYu Zhao iov->stride = stride; 483d1b054daSYu Zhao iov->pgsz = pgsz; 484d1b054daSYu Zhao iov->self = dev; 485d1b054daSYu Zhao pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); 486d1b054daSYu Zhao pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); 48762f87c0eSYijing Wang if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) 4884d135dbeSYu Zhao iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link); 489d1b054daSYu Zhao 490d1b054daSYu Zhao if (pdev) 491d1b054daSYu Zhao iov->dev = pci_dev_get(pdev); 492e277d2fcSYu Zhao else 493d1b054daSYu Zhao iov->dev = dev; 494e277d2fcSYu Zhao 495d1b054daSYu Zhao mutex_init(&iov->lock); 496d1b054daSYu Zhao 497d1b054daSYu Zhao dev->sriov = iov; 498d1b054daSYu Zhao dev->is_physfn = 1; 499d1b054daSYu Zhao 500d1b054daSYu Zhao return 0; 501d1b054daSYu Zhao 502d1b054daSYu Zhao failed: 503d1b054daSYu Zhao for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 504d1b054daSYu Zhao res = dev->resource + PCI_IOV_RESOURCES + i; 505d1b054daSYu Zhao res->flags = 0; 506d1b054daSYu Zhao } 507d1b054daSYu Zhao 508d1b054daSYu Zhao return rc; 509d1b054daSYu Zhao } 510d1b054daSYu Zhao 511d1b054daSYu Zhao static void sriov_release(struct pci_dev *dev) 512d1b054daSYu Zhao { 5136b136724SBjorn Helgaas BUG_ON(dev->sriov->num_VFs); 514dd7cc44dSYu Zhao 515e277d2fcSYu Zhao if (dev != dev->sriov->dev) 516d1b054daSYu Zhao pci_dev_put(dev->sriov->dev); 517d1b054daSYu Zhao 518e277d2fcSYu Zhao mutex_destroy(&dev->sriov->lock); 519e277d2fcSYu Zhao 520d1b054daSYu Zhao kfree(dev->sriov); 521d1b054daSYu Zhao dev->sriov = NULL; 522d1b054daSYu Zhao } 523d1b054daSYu Zhao 5248c5cdb6aSYu Zhao static void sriov_restore_state(struct pci_dev *dev) 5258c5cdb6aSYu Zhao { 5268c5cdb6aSYu Zhao int i; 5278c5cdb6aSYu Zhao u16 ctrl; 5288c5cdb6aSYu Zhao struct pci_sriov *iov = dev->sriov; 5298c5cdb6aSYu Zhao 5308c5cdb6aSYu Zhao pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &ctrl); 5318c5cdb6aSYu Zhao if (ctrl & PCI_SRIOV_CTRL_VFE) 5328c5cdb6aSYu Zhao return; 5338c5cdb6aSYu Zhao 5348c5cdb6aSYu Zhao for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) 5358c5cdb6aSYu Zhao pci_update_resource(dev, i); 5368c5cdb6aSYu Zhao 5378c5cdb6aSYu Zhao pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz); 5386b136724SBjorn Helgaas pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs); 5398c5cdb6aSYu Zhao pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); 5408c5cdb6aSYu Zhao if (iov->ctrl & PCI_SRIOV_CTRL_VFE) 5418c5cdb6aSYu Zhao msleep(100); 5428c5cdb6aSYu Zhao } 5438c5cdb6aSYu Zhao 544d1b054daSYu Zhao /** 545d1b054daSYu Zhao * pci_iov_init - initialize the IOV capability 546d1b054daSYu Zhao * @dev: the PCI device 547d1b054daSYu Zhao * 548d1b054daSYu Zhao * Returns 0 on success, or negative on failure. 549d1b054daSYu Zhao */ 550d1b054daSYu Zhao int pci_iov_init(struct pci_dev *dev) 551d1b054daSYu Zhao { 552d1b054daSYu Zhao int pos; 553d1b054daSYu Zhao 5545f4d91a1SKenji Kaneshige if (!pci_is_pcie(dev)) 555d1b054daSYu Zhao return -ENODEV; 556d1b054daSYu Zhao 557d1b054daSYu Zhao pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); 558d1b054daSYu Zhao if (pos) 559d1b054daSYu Zhao return sriov_init(dev, pos); 560d1b054daSYu Zhao 561d1b054daSYu Zhao return -ENODEV; 562d1b054daSYu Zhao } 563d1b054daSYu Zhao 564d1b054daSYu Zhao /** 565d1b054daSYu Zhao * pci_iov_release - release resources used by the IOV capability 566d1b054daSYu Zhao * @dev: the PCI device 567d1b054daSYu Zhao */ 568d1b054daSYu Zhao void pci_iov_release(struct pci_dev *dev) 569d1b054daSYu Zhao { 570d1b054daSYu Zhao if (dev->is_physfn) 571d1b054daSYu Zhao sriov_release(dev); 572d1b054daSYu Zhao } 573d1b054daSYu Zhao 574d1b054daSYu Zhao /** 575d1b054daSYu Zhao * pci_iov_resource_bar - get position of the SR-IOV BAR 576d1b054daSYu Zhao * @dev: the PCI device 577d1b054daSYu Zhao * @resno: the resource number 578d1b054daSYu Zhao * @type: the BAR type to be filled in 579d1b054daSYu Zhao * 580d1b054daSYu Zhao * Returns position of the BAR encapsulated in the SR-IOV capability. 581d1b054daSYu Zhao */ 582d1b054daSYu Zhao int pci_iov_resource_bar(struct pci_dev *dev, int resno, 583d1b054daSYu Zhao enum pci_bar_type *type) 584d1b054daSYu Zhao { 585d1b054daSYu Zhao if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END) 586d1b054daSYu Zhao return 0; 587d1b054daSYu Zhao 588d1b054daSYu Zhao BUG_ON(!dev->is_physfn); 589d1b054daSYu Zhao 590d1b054daSYu Zhao *type = pci_bar_unknown; 591d1b054daSYu Zhao 592d1b054daSYu Zhao return dev->sriov->pos + PCI_SRIOV_BAR + 593d1b054daSYu Zhao 4 * (resno - PCI_IOV_RESOURCES); 594d1b054daSYu Zhao } 5958c5cdb6aSYu Zhao 5968c5cdb6aSYu Zhao /** 5976faf17f6SChris Wright * pci_sriov_resource_alignment - get resource alignment for VF BAR 5986faf17f6SChris Wright * @dev: the PCI device 5996faf17f6SChris Wright * @resno: the resource number 6006faf17f6SChris Wright * 6016faf17f6SChris Wright * Returns the alignment of the VF BAR found in the SR-IOV capability. 6026faf17f6SChris Wright * This is not the same as the resource size which is defined as 6036faf17f6SChris Wright * the VF BAR size multiplied by the number of VFs. The alignment 6046faf17f6SChris Wright * is just the VF BAR size. 6056faf17f6SChris Wright */ 6060e52247aSCam Macdonell resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno) 6076faf17f6SChris Wright { 6086faf17f6SChris Wright struct resource tmp; 6096faf17f6SChris Wright enum pci_bar_type type; 6106faf17f6SChris Wright int reg = pci_iov_resource_bar(dev, resno, &type); 6116faf17f6SChris Wright 6126faf17f6SChris Wright if (!reg) 6136faf17f6SChris Wright return 0; 6146faf17f6SChris Wright 6156faf17f6SChris Wright __pci_read_base(dev, type, &tmp, reg); 6166faf17f6SChris Wright return resource_alignment(&tmp); 6176faf17f6SChris Wright } 6186faf17f6SChris Wright 6196faf17f6SChris Wright /** 6208c5cdb6aSYu Zhao * pci_restore_iov_state - restore the state of the IOV capability 6218c5cdb6aSYu Zhao * @dev: the PCI device 6228c5cdb6aSYu Zhao */ 6238c5cdb6aSYu Zhao void pci_restore_iov_state(struct pci_dev *dev) 6248c5cdb6aSYu Zhao { 6258c5cdb6aSYu Zhao if (dev->is_physfn) 6268c5cdb6aSYu Zhao sriov_restore_state(dev); 6278c5cdb6aSYu Zhao } 628a28724b0SYu Zhao 629a28724b0SYu Zhao /** 630a28724b0SYu Zhao * pci_iov_bus_range - find bus range used by Virtual Function 631a28724b0SYu Zhao * @bus: the PCI bus 632a28724b0SYu Zhao * 633a28724b0SYu Zhao * Returns max number of buses (exclude current one) used by Virtual 634a28724b0SYu Zhao * Functions. 635a28724b0SYu Zhao */ 636a28724b0SYu Zhao int pci_iov_bus_range(struct pci_bus *bus) 637a28724b0SYu Zhao { 638a28724b0SYu Zhao int max = 0; 639a28724b0SYu Zhao u8 busnr; 640a28724b0SYu Zhao struct pci_dev *dev; 641a28724b0SYu Zhao 642a28724b0SYu Zhao list_for_each_entry(dev, &bus->devices, bus_list) { 643a28724b0SYu Zhao if (!dev->is_physfn) 644a28724b0SYu Zhao continue; 6456b136724SBjorn Helgaas busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1); 646a28724b0SYu Zhao if (busnr > max) 647a28724b0SYu Zhao max = busnr; 648a28724b0SYu Zhao } 649a28724b0SYu Zhao 650a28724b0SYu Zhao return max ? max - bus->number : 0; 651a28724b0SYu Zhao } 652dd7cc44dSYu Zhao 653dd7cc44dSYu Zhao /** 654dd7cc44dSYu Zhao * pci_enable_sriov - enable the SR-IOV capability 655dd7cc44dSYu Zhao * @dev: the PCI device 65652a8873bSRandy Dunlap * @nr_virtfn: number of virtual functions to enable 657dd7cc44dSYu Zhao * 658dd7cc44dSYu Zhao * Returns 0 on success, or negative on failure. 659dd7cc44dSYu Zhao */ 660dd7cc44dSYu Zhao int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) 661dd7cc44dSYu Zhao { 662dd7cc44dSYu Zhao might_sleep(); 663dd7cc44dSYu Zhao 664dd7cc44dSYu Zhao if (!dev->is_physfn) 665dd7cc44dSYu Zhao return -ENODEV; 666dd7cc44dSYu Zhao 667dd7cc44dSYu Zhao return sriov_enable(dev, nr_virtfn); 668dd7cc44dSYu Zhao } 669dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_enable_sriov); 670dd7cc44dSYu Zhao 671dd7cc44dSYu Zhao /** 672dd7cc44dSYu Zhao * pci_disable_sriov - disable the SR-IOV capability 673dd7cc44dSYu Zhao * @dev: the PCI device 674dd7cc44dSYu Zhao */ 675dd7cc44dSYu Zhao void pci_disable_sriov(struct pci_dev *dev) 676dd7cc44dSYu Zhao { 677dd7cc44dSYu Zhao might_sleep(); 678dd7cc44dSYu Zhao 679dd7cc44dSYu Zhao if (!dev->is_physfn) 680dd7cc44dSYu Zhao return; 681dd7cc44dSYu Zhao 682dd7cc44dSYu Zhao sriov_disable(dev); 683dd7cc44dSYu Zhao } 684dd7cc44dSYu Zhao EXPORT_SYMBOL_GPL(pci_disable_sriov); 68574bb1bccSYu Zhao 68674bb1bccSYu Zhao /** 68774bb1bccSYu Zhao * pci_sriov_migration - notify SR-IOV core of Virtual Function Migration 68874bb1bccSYu Zhao * @dev: the PCI device 68974bb1bccSYu Zhao * 69074bb1bccSYu Zhao * Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not. 69174bb1bccSYu Zhao * 69274bb1bccSYu Zhao * Physical Function driver is responsible to register IRQ handler using 69374bb1bccSYu Zhao * VF Migration Interrupt Message Number, and call this function when the 69474bb1bccSYu Zhao * interrupt is generated by the hardware. 69574bb1bccSYu Zhao */ 69674bb1bccSYu Zhao irqreturn_t pci_sriov_migration(struct pci_dev *dev) 69774bb1bccSYu Zhao { 69874bb1bccSYu Zhao if (!dev->is_physfn) 69974bb1bccSYu Zhao return IRQ_NONE; 70074bb1bccSYu Zhao 70174bb1bccSYu Zhao return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; 70274bb1bccSYu Zhao } 70374bb1bccSYu Zhao EXPORT_SYMBOL_GPL(pci_sriov_migration); 704302b4215SYu Zhao 705fb8a0d9dSWilliams, Mitch A /** 706fb8a0d9dSWilliams, Mitch A * pci_num_vf - return number of VFs associated with a PF device_release_driver 707fb8a0d9dSWilliams, Mitch A * @dev: the PCI device 708fb8a0d9dSWilliams, Mitch A * 709fb8a0d9dSWilliams, Mitch A * Returns number of VFs, or 0 if SR-IOV is not enabled. 710fb8a0d9dSWilliams, Mitch A */ 711fb8a0d9dSWilliams, Mitch A int pci_num_vf(struct pci_dev *dev) 712fb8a0d9dSWilliams, Mitch A { 7131452cd76SBjorn Helgaas if (!dev->is_physfn) 714fb8a0d9dSWilliams, Mitch A return 0; 7151452cd76SBjorn Helgaas 7166b136724SBjorn Helgaas return dev->sriov->num_VFs; 717fb8a0d9dSWilliams, Mitch A } 718fb8a0d9dSWilliams, Mitch A EXPORT_SYMBOL_GPL(pci_num_vf); 719bff73156SDonald Dutile 720bff73156SDonald Dutile /** 7215a8eb242SAlexander Duyck * pci_vfs_assigned - returns number of VFs are assigned to a guest 7225a8eb242SAlexander Duyck * @dev: the PCI device 7235a8eb242SAlexander Duyck * 7245a8eb242SAlexander Duyck * Returns number of VFs belonging to this device that are assigned to a guest. 7255a8eb242SAlexander Duyck * If device is not a physical function returns -ENODEV. 7265a8eb242SAlexander Duyck */ 7275a8eb242SAlexander Duyck int pci_vfs_assigned(struct pci_dev *dev) 7285a8eb242SAlexander Duyck { 7295a8eb242SAlexander Duyck struct pci_dev *vfdev; 7305a8eb242SAlexander Duyck unsigned int vfs_assigned = 0; 7315a8eb242SAlexander Duyck unsigned short dev_id; 7325a8eb242SAlexander Duyck 7335a8eb242SAlexander Duyck /* only search if we are a PF */ 7345a8eb242SAlexander Duyck if (!dev->is_physfn) 7355a8eb242SAlexander Duyck return 0; 7365a8eb242SAlexander Duyck 7375a8eb242SAlexander Duyck /* 7385a8eb242SAlexander Duyck * determine the device ID for the VFs, the vendor ID will be the 7395a8eb242SAlexander Duyck * same as the PF so there is no need to check for that one 7405a8eb242SAlexander Duyck */ 7415a8eb242SAlexander Duyck pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id); 7425a8eb242SAlexander Duyck 7435a8eb242SAlexander Duyck /* loop through all the VFs to see if we own any that are assigned */ 7445a8eb242SAlexander Duyck vfdev = pci_get_device(dev->vendor, dev_id, NULL); 7455a8eb242SAlexander Duyck while (vfdev) { 7465a8eb242SAlexander Duyck /* 7475a8eb242SAlexander Duyck * It is considered assigned if it is a virtual function with 7485a8eb242SAlexander Duyck * our dev as the physical function and the assigned bit is set 7495a8eb242SAlexander Duyck */ 7505a8eb242SAlexander Duyck if (vfdev->is_virtfn && (vfdev->physfn == dev) && 7515a8eb242SAlexander Duyck (vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED)) 7525a8eb242SAlexander Duyck vfs_assigned++; 7535a8eb242SAlexander Duyck 7545a8eb242SAlexander Duyck vfdev = pci_get_device(dev->vendor, dev_id, vfdev); 7555a8eb242SAlexander Duyck } 7565a8eb242SAlexander Duyck 7575a8eb242SAlexander Duyck return vfs_assigned; 7585a8eb242SAlexander Duyck } 7595a8eb242SAlexander Duyck EXPORT_SYMBOL_GPL(pci_vfs_assigned); 7605a8eb242SAlexander Duyck 7615a8eb242SAlexander Duyck /** 762bff73156SDonald Dutile * pci_sriov_set_totalvfs -- reduce the TotalVFs available 763bff73156SDonald Dutile * @dev: the PCI PF device 7642094f167SRandy Dunlap * @numvfs: number that should be used for TotalVFs supported 765bff73156SDonald Dutile * 766bff73156SDonald Dutile * Should be called from PF driver's probe routine with 767bff73156SDonald Dutile * device's mutex held. 768bff73156SDonald Dutile * 769bff73156SDonald Dutile * Returns 0 if PF is an SRIOV-capable device and 770bff73156SDonald Dutile * value of numvfs valid. If not a PF with VFS, return -EINVAL; 771bff73156SDonald Dutile * if VFs already enabled, return -EBUSY. 772bff73156SDonald Dutile */ 773bff73156SDonald Dutile int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) 774bff73156SDonald Dutile { 7751452cd76SBjorn Helgaas if (!dev->is_physfn || (numvfs > dev->sriov->total_VFs)) 776bff73156SDonald Dutile return -EINVAL; 777bff73156SDonald Dutile 778bff73156SDonald Dutile /* Shouldn't change if VFs already enabled */ 779bff73156SDonald Dutile if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE) 780bff73156SDonald Dutile return -EBUSY; 781bff73156SDonald Dutile else 7826b136724SBjorn Helgaas dev->sriov->driver_max_VFs = numvfs; 783bff73156SDonald Dutile 784bff73156SDonald Dutile return 0; 785bff73156SDonald Dutile } 786bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs); 787bff73156SDonald Dutile 788bff73156SDonald Dutile /** 789*ddc191f5SJonghwan Choi * pci_sriov_get_totalvfs -- get total VFs supported on this device 790bff73156SDonald Dutile * @dev: the PCI PF device 791bff73156SDonald Dutile * 792bff73156SDonald Dutile * For a PCIe device with SRIOV support, return the PCIe 7936b136724SBjorn Helgaas * SRIOV capability value of TotalVFs or the value of driver_max_VFs 794bff73156SDonald Dutile * if the driver reduced it. Otherwise, -EINVAL. 795bff73156SDonald Dutile */ 796bff73156SDonald Dutile int pci_sriov_get_totalvfs(struct pci_dev *dev) 797bff73156SDonald Dutile { 7981452cd76SBjorn Helgaas if (!dev->is_physfn) 799bff73156SDonald Dutile return -EINVAL; 800bff73156SDonald Dutile 8016b136724SBjorn Helgaas if (dev->sriov->driver_max_VFs) 8026b136724SBjorn Helgaas return dev->sriov->driver_max_VFs; 8031452cd76SBjorn Helgaas 8046b136724SBjorn Helgaas return dev->sriov->total_VFs; 805bff73156SDonald Dutile } 806bff73156SDonald Dutile EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs); 807