summaryrefslogtreecommitdiff
path: root/drivers/pci/iov.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/iov.c')
-rw-r--r--drivers/pci/iov.c88
1 files changed, 41 insertions, 47 deletions
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index c93071d428f5..21a7182dccd4 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -47,51 +47,43 @@ static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
return NULL;
pci_bus_insert_busn_res(child, busnr, busnr);
- bus->is_added = 1;
return child;
}
-static void virtfn_remove_bus(struct pci_bus *bus, int busnr)
+static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus)
{
- struct pci_bus *child;
-
- if (bus->number == busnr)
- return;
-
- child = pci_find_bus(pci_domain_nr(bus), busnr);
- BUG_ON(!child);
-
- if (list_empty(&child->devices))
- pci_remove_bus(child);
+ if (physbus != virtbus && list_empty(&virtbus->devices))
+ pci_remove_bus(virtbus);
}
static int virtfn_add(struct pci_dev *dev, int id, int reset)
{
int i;
- int rc;
+ int rc = -ENOMEM;
u64 size;
char buf[VIRTFN_ID_LEN];
struct pci_dev *virtfn;
struct resource *res;
struct pci_sriov *iov = dev->sriov;
+ struct pci_bus *bus;
- virtfn = alloc_pci_dev();
+ mutex_lock(&iov->dev->sriov->lock);
+ bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
+ if (!bus)
+ goto failed;
+
+ virtfn = pci_alloc_dev(bus);
if (!virtfn)
- return -ENOMEM;
+ goto failed0;
- mutex_lock(&iov->dev->sriov->lock);
- virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
- if (!virtfn->bus) {
- kfree(virtfn);
- mutex_unlock(&iov->dev->sriov->lock);
- return -ENOMEM;
- }
virtfn->devfn = virtfn_devfn(dev, id);
virtfn->vendor = dev->vendor;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device);
pci_setup_device(virtfn);
virtfn->dev.parent = dev->dev.parent;
+ virtfn->physfn = pci_dev_get(dev);
+ virtfn->is_virtfn = 1;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
res = dev->resource + PCI_IOV_RESOURCES + i;
@@ -113,9 +105,6 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset)
pci_device_add(virtfn, virtfn->bus);
mutex_unlock(&iov->dev->sriov->lock);
- virtfn->physfn = pci_dev_get(dev);
- virtfn->is_virtfn = 1;
-
rc = pci_bus_add_device(virtfn);
sprintf(buf, "virtfn%u", id);
rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
@@ -135,7 +124,9 @@ failed1:
pci_dev_put(dev);
mutex_lock(&iov->dev->sriov->lock);
pci_stop_and_remove_bus_device(virtfn);
- virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
+failed0:
+ virtfn_remove_bus(dev->bus, bus);
+failed:
mutex_unlock(&iov->dev->sriov->lock);
return rc;
@@ -144,20 +135,15 @@ failed1:
static void virtfn_remove(struct pci_dev *dev, int id, int reset)
{
char buf[VIRTFN_ID_LEN];
- struct pci_bus *bus;
struct pci_dev *virtfn;
struct pci_sriov *iov = dev->sriov;
- bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id));
- if (!bus)
- return;
-
- virtfn = pci_get_slot(bus, virtfn_devfn(dev, id));
+ virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
+ virtfn_bus(dev, id),
+ virtfn_devfn(dev, id));
if (!virtfn)
return;
- pci_dev_put(virtfn);
-
if (reset) {
device_release_driver(&virtfn->dev);
__pci_reset_function(virtfn);
@@ -175,9 +161,11 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
mutex_lock(&iov->dev->sriov->lock);
pci_stop_and_remove_bus_device(virtfn);
- virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
+ virtfn_remove_bus(dev->bus, virtfn->bus);
mutex_unlock(&iov->dev->sriov->lock);
+ /* balance pci_get_domain_bus_and_slot() */
+ pci_dev_put(virtfn);
pci_dev_put(dev);
}
@@ -298,7 +286,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
return -EINVAL;
- pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
if (!offset || (nr_virtfn > 1 && !stride))
@@ -334,17 +321,19 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
if (!pdev)
return -ENODEV;
- pci_dev_put(pdev);
-
- if (!pdev->is_physfn)
- return -ENODEV;
+ if (!pdev->is_physfn) {
+ pci_dev_put(pdev);
+ return -ENOSYS;
+ }
rc = sysfs_create_link(&dev->dev.kobj,
&pdev->dev.kobj, "dep_link");
+ pci_dev_put(pdev);
if (rc)
return rc;
}
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
pci_cfg_access_lock(dev);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
@@ -379,6 +368,7 @@ failed:
iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
pci_cfg_access_lock(dev);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
ssleep(1);
pci_cfg_access_unlock(dev);
@@ -412,6 +402,7 @@ static void sriov_disable(struct pci_dev *dev)
sysfs_remove_link(&dev->dev.kobj, "dep_link");
iov->num_VFs = 0;
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
}
static int sriov_init(struct pci_dev *dev, int pos)
@@ -673,7 +664,7 @@ int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
might_sleep();
if (!dev->is_physfn)
- return -ENODEV;
+ return -ENOSYS;
return sriov_enable(dev, nr_virtfn);
}
@@ -733,7 +724,7 @@ EXPORT_SYMBOL_GPL(pci_num_vf);
* @dev: the PCI device
*
* Returns number of VFs belonging to this device that are assigned to a guest.
- * If device is not a physical function returns -ENODEV.
+ * If device is not a physical function returns 0.
*/
int pci_vfs_assigned(struct pci_dev *dev)
{
@@ -778,12 +769,15 @@ EXPORT_SYMBOL_GPL(pci_vfs_assigned);
* device's mutex held.
*
* Returns 0 if PF is an SRIOV-capable device and
- * value of numvfs valid. If not a PF with VFS, return -EINVAL;
+ * value of numvfs valid. If not a PF return -ENOSYS;
+ * if numvfs is invalid return -EINVAL;
* if VFs already enabled, return -EBUSY.
*/
int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
{
- if (!dev->is_physfn || (numvfs > dev->sriov->total_VFs))
+ if (!dev->is_physfn)
+ return -ENOSYS;
+ if (numvfs > dev->sriov->total_VFs)
return -EINVAL;
/* Shouldn't change if VFs already enabled */
@@ -797,17 +791,17 @@ int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
/**
- * pci_sriov_get_totalvfs -- get total VFs supported on this devic3
+ * pci_sriov_get_totalvfs -- get total VFs supported on this device
* @dev: the PCI PF device
*
* For a PCIe device with SRIOV support, return the PCIe
* SRIOV capability value of TotalVFs or the value of driver_max_VFs
- * if the driver reduced it. Otherwise, -EINVAL.
+ * if the driver reduced it. Otherwise 0.
*/
int pci_sriov_get_totalvfs(struct pci_dev *dev)
{
if (!dev->is_physfn)
- return -EINVAL;
+ return 0;
if (dev->sriov->driver_max_VFs)
return dev->sriov->driver_max_VFs;