aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci/iov.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/iov.c')
-rw-r--r--drivers/pci/iov.c463
1 files changed, 146 insertions, 317 deletions
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 553d8ee55c1..cb6f24740ee 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -11,8 +11,10 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/mutex.h>
+#include <linux/export.h>
#include <linux/string.h>
#include <linux/delay.h>
+#include <linux/pci-ats.h>
#include "pci.h"
#define VIRTFN_ID_LEN 16
@@ -31,7 +33,6 @@ static inline u8 virtfn_devfn(struct pci_dev *dev, int id)
static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
{
- int rc;
struct pci_bus *child;
if (bus->number == busnr)
@@ -45,57 +46,45 @@ static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr)
if (!child)
return NULL;
- child->subordinate = busnr;
- child->dev.parent = bus->bridge;
- rc = pci_bus_add_child(child);
- if (rc) {
- pci_remove_bus(child);
- return NULL;
- }
+ pci_bus_insert_busn_res(child, busnr, busnr);
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;
+
+ mutex_lock(&iov->dev->sriov->lock);
+ bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id));
+ if (!bus)
+ goto failed;
- virtfn = alloc_pci_dev();
+ 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;
+ virtfn->multifunction = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
res = dev->resource + PCI_IOV_RESOURCES + i;
@@ -104,7 +93,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset)
virtfn->resource[i].name = pci_name(virtfn);
virtfn->resource[i].flags = res->flags;
size = resource_size(res);
- do_div(size, iov->total);
+ do_div(size, iov->total_VFs);
virtfn->resource[i].start = res->start + size * id;
virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
rc = request_resource(res, &virtfn->resource[i]);
@@ -117,12 +106,7 @@ 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);
- if (rc)
- goto failed1;
+ pci_bus_add_device(virtfn);
sprintf(buf, "virtfn%u", id);
rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
if (rc)
@@ -140,8 +124,10 @@ failed2:
failed1:
pci_dev_put(dev);
mutex_lock(&iov->dev->sriov->lock);
- pci_remove_bus_device(virtfn);
- virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
+ pci_stop_and_remove_bus_device(virtfn);
+failed0:
+ virtfn_remove_bus(dev->bus, bus);
+failed:
mutex_unlock(&iov->dev->sriov->lock);
return rc;
@@ -150,20 +136,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);
@@ -171,107 +152,24 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
sprintf(buf, "virtfn%u", id);
sysfs_remove_link(&dev->dev.kobj, buf);
- sysfs_remove_link(&virtfn->dev.kobj, "physfn");
+ /*
+ * pci_stop_dev() could have been called for this virtfn already,
+ * so the directory for the virtfn may have been removed before.
+ * Double check to avoid spurious sysfs warnings.
+ */
+ if (virtfn->dev.kobj.sd)
+ sysfs_remove_link(&virtfn->dev.kobj, "physfn");
mutex_lock(&iov->dev->sriov->lock);
- pci_remove_bus_device(virtfn);
- virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
+ pci_stop_and_remove_bus_device(virtfn);
+ 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);
}
-static int sriov_migration(struct pci_dev *dev)
-{
- u16 status;
- struct pci_sriov *iov = dev->sriov;
-
- if (!iov->nr_virtfn)
- return 0;
-
- if (!(iov->cap & PCI_SRIOV_CAP_VFM))
- return 0;
-
- pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status);
- if (!(status & PCI_SRIOV_STATUS_VFM))
- return 0;
-
- schedule_work(&iov->mtask);
-
- return 1;
-}
-
-static void sriov_migration_task(struct work_struct *work)
-{
- int i;
- u8 state;
- u16 status;
- struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask);
-
- for (i = iov->initial; i < iov->nr_virtfn; i++) {
- state = readb(iov->mstate + i);
- if (state == PCI_SRIOV_VFM_MI) {
- writeb(PCI_SRIOV_VFM_AV, iov->mstate + i);
- state = readb(iov->mstate + i);
- if (state == PCI_SRIOV_VFM_AV)
- virtfn_add(iov->self, i, 1);
- } else if (state == PCI_SRIOV_VFM_MO) {
- virtfn_remove(iov->self, i, 1);
- writeb(PCI_SRIOV_VFM_UA, iov->mstate + i);
- state = readb(iov->mstate + i);
- if (state == PCI_SRIOV_VFM_AV)
- virtfn_add(iov->self, i, 0);
- }
- }
-
- pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status);
- status &= ~PCI_SRIOV_STATUS_VFM;
- pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status);
-}
-
-static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn)
-{
- int bir;
- u32 table;
- resource_size_t pa;
- struct pci_sriov *iov = dev->sriov;
-
- if (nr_virtfn <= iov->initial)
- return 0;
-
- pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table);
- bir = PCI_SRIOV_VFM_BIR(table);
- if (bir > PCI_STD_RESOURCE_END)
- return -EIO;
-
- table = PCI_SRIOV_VFM_OFFSET(table);
- if (table + nr_virtfn > pci_resource_len(dev, bir))
- return -EIO;
-
- pa = pci_resource_start(dev, bir) + table;
- iov->mstate = ioremap(pa, nr_virtfn);
- if (!iov->mstate)
- return -ENOMEM;
-
- INIT_WORK(&iov->mtask, sriov_migration_task);
-
- iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR;
- pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
-
- return 0;
-}
-
-static void sriov_disable_migration(struct pci_dev *dev)
-{
- struct pci_sriov *iov = dev->sriov;
-
- iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR);
- pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
-
- cancel_work_sync(&iov->mtask);
- iounmap(iov->mstate);
-}
-
static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
{
int rc;
@@ -281,23 +179,23 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
struct resource *res;
struct pci_dev *pdev;
struct pci_sriov *iov = dev->sriov;
+ int bars = 0;
if (!nr_virtfn)
return 0;
- if (iov->nr_virtfn)
+ if (iov->num_VFs)
return -EINVAL;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
- if (initial > iov->total ||
- (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total)))
+ if (initial > iov->total_VFs ||
+ (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
return -EIO;
- if (nr_virtfn < 0 || nr_virtfn > iov->total ||
+ if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
return -EINVAL;
- 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))
@@ -305,6 +203,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
nres = 0;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
+ bars |= (1 << (i + PCI_IOV_RESOURCES));
res = dev->resource + PCI_IOV_RESOURCES + i;
if (res->parent)
nres++;
@@ -317,34 +216,41 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
iov->offset = offset;
iov->stride = stride;
- if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) {
+ if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->busn_res.end) {
dev_err(&dev->dev, "SR-IOV: bus number out of range\n");
return -ENOMEM;
}
+ if (pci_enable_resources(dev, bars)) {
+ dev_err(&dev->dev, "SR-IOV: IOV BARS not allocated\n");
+ return -ENOMEM;
+ }
+
if (iov->link != dev->devfn) {
pdev = pci_get_slot(dev->bus, iov->link);
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_block_user_cfg_access(dev);
+ pci_cfg_access_lock(dev);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
msleep(100);
- pci_unblock_user_cfg_access(dev);
+ pci_cfg_access_unlock(dev);
- iov->initial = initial;
+ iov->initial_VFs = initial;
if (nr_virtfn < initial)
initial = nr_virtfn;
@@ -354,14 +260,8 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
goto failed;
}
- if (iov->cap & PCI_SRIOV_CAP_VFM) {
- rc = sriov_enable_migration(dev, nr_virtfn);
- if (rc)
- goto failed;
- }
-
kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
- iov->nr_virtfn = nr_virtfn;
+ iov->num_VFs = nr_virtfn;
return 0;
@@ -370,10 +270,11 @@ failed:
virtfn_remove(dev, j, 0);
iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
- pci_block_user_cfg_access(dev);
+ 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_unblock_user_cfg_access(dev);
+ pci_cfg_access_unlock(dev);
if (iov->link != dev->devfn)
sysfs_remove_link(&dev->dev.kobj, "dep_link");
@@ -386,25 +287,23 @@ static void sriov_disable(struct pci_dev *dev)
int i;
struct pci_sriov *iov = dev->sriov;
- if (!iov->nr_virtfn)
+ if (!iov->num_VFs)
return;
- if (iov->cap & PCI_SRIOV_CAP_VFM)
- sriov_disable_migration(dev);
-
- for (i = 0; i < iov->nr_virtfn; i++)
+ for (i = 0; i < iov->num_VFs; i++)
virtfn_remove(dev, i, 0);
iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
- pci_block_user_cfg_access(dev);
+ pci_cfg_access_lock(dev);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
ssleep(1);
- pci_unblock_user_cfg_access(dev);
+ pci_cfg_access_unlock(dev);
if (iov->link != dev->devfn)
sysfs_remove_link(&dev->dev.kobj, "dep_link");
- iov->nr_virtfn = 0;
+ 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)
@@ -418,8 +317,8 @@ static int sriov_init(struct pci_dev *dev, int pos)
struct resource *res;
struct pci_dev *pdev;
- if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
- dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
+ if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END &&
+ pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT)
return -ENODEV;
pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
@@ -443,7 +342,7 @@ static int sriov_init(struct pci_dev *dev, int pos)
found:
pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl);
- pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, total);
+ pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0);
pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset);
pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride);
if (!offset || (total > 1 && !stride))
@@ -482,14 +381,14 @@ found:
iov->pos = pos;
iov->nres = nres;
iov->ctrl = ctrl;
- iov->total = total;
+ iov->total_VFs = total;
iov->offset = offset;
iov->stride = stride;
iov->pgsz = pgsz;
iov->self = dev;
pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
- if (dev->pcie_type == PCI_EXP_TYPE_RC_END)
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
if (pdev)
@@ -515,7 +414,7 @@ failed:
static void sriov_release(struct pci_dev *dev)
{
- BUG_ON(dev->sriov->nr_virtfn);
+ BUG_ON(dev->sriov->num_VFs);
if (dev != dev->sriov->dev)
pci_dev_put(dev->sriov->dev);
@@ -540,7 +439,7 @@ static void sriov_restore_state(struct pci_dev *dev)
pci_update_resource(dev, i);
pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
- pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
msleep(100);
@@ -613,7 +512,7 @@ resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
struct resource tmp;
enum pci_bar_type type;
int reg = pci_iov_resource_bar(dev, resno, &type);
-
+
if (!reg)
return 0;
@@ -647,7 +546,7 @@ int pci_iov_bus_range(struct pci_bus *bus)
list_for_each_entry(dev, &bus->devices, bus_list) {
if (!dev->is_physfn)
continue;
- busnr = virtfn_bus(dev, dev->sriov->total - 1);
+ busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1);
if (busnr > max)
max = busnr;
}
@@ -667,7 +566,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);
}
@@ -689,25 +588,6 @@ void pci_disable_sriov(struct pci_dev *dev)
EXPORT_SYMBOL_GPL(pci_disable_sriov);
/**
- * pci_sriov_migration - notify SR-IOV core of Virtual Function Migration
- * @dev: the PCI device
- *
- * Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not.
- *
- * Physical Function driver is responsible to register IRQ handler using
- * VF Migration Interrupt Message Number, and call this function when the
- * interrupt is generated by the hardware.
- */
-irqreturn_t pci_sriov_migration(struct pci_dev *dev)
-{
- if (!dev->is_physfn)
- return IRQ_NONE;
-
- return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE;
-}
-EXPORT_SYMBOL_GPL(pci_sriov_migration);
-
-/**
* pci_num_vf - return number of VFs associated with a PF device_release_driver
* @dev: the PCI device
*
@@ -715,151 +595,100 @@ EXPORT_SYMBOL_GPL(pci_sriov_migration);
*/
int pci_num_vf(struct pci_dev *dev)
{
- if (!dev || !dev->is_physfn)
+ if (!dev->is_physfn)
return 0;
- else
- return dev->sriov->nr_virtfn;
-}
-EXPORT_SYMBOL_GPL(pci_num_vf);
-
-static int ats_alloc_one(struct pci_dev *dev, int ps)
-{
- int pos;
- u16 cap;
- struct pci_ats *ats;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
- if (!pos)
- return -ENODEV;
-
- ats = kzalloc(sizeof(*ats), GFP_KERNEL);
- if (!ats)
- return -ENOMEM;
-
- ats->pos = pos;
- ats->stu = ps;
- pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap);
- ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) :
- PCI_ATS_MAX_QDEP;
- dev->ats = ats;
-
- return 0;
-}
-
-static void ats_free_one(struct pci_dev *dev)
-{
- kfree(dev->ats);
- dev->ats = NULL;
+ return dev->sriov->num_VFs;
}
+EXPORT_SYMBOL_GPL(pci_num_vf);
/**
- * pci_enable_ats - enable the ATS capability
+ * pci_vfs_assigned - returns number of VFs are assigned to a guest
* @dev: the PCI device
- * @ps: the IOMMU page shift
*
- * Returns 0 on success, or negative on failure.
+ * Returns number of VFs belonging to this device that are assigned to a guest.
+ * If device is not a physical function returns 0.
*/
-int pci_enable_ats(struct pci_dev *dev, int ps)
+int pci_vfs_assigned(struct pci_dev *dev)
{
- int rc;
- u16 ctrl;
-
- BUG_ON(dev->ats && dev->ats->is_enabled);
-
- if (ps < PCI_ATS_MIN_STU)
- return -EINVAL;
-
- if (dev->is_physfn || dev->is_virtfn) {
- struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn;
-
- mutex_lock(&pdev->sriov->lock);
- if (pdev->ats)
- rc = pdev->ats->stu == ps ? 0 : -EINVAL;
- else
- rc = ats_alloc_one(pdev, ps);
+ struct pci_dev *vfdev;
+ unsigned int vfs_assigned = 0;
+ unsigned short dev_id;
- if (!rc)
- pdev->ats->ref_cnt++;
- mutex_unlock(&pdev->sriov->lock);
- if (rc)
- return rc;
- }
+ /* only search if we are a PF */
+ if (!dev->is_physfn)
+ return 0;
- if (!dev->is_physfn) {
- rc = ats_alloc_one(dev, ps);
- if (rc)
- return rc;
+ /*
+ * determine the device ID for the VFs, the vendor ID will be the
+ * same as the PF so there is no need to check for that one
+ */
+ pci_read_config_word(dev, dev->sriov->pos + PCI_SRIOV_VF_DID, &dev_id);
+
+ /* loop through all the VFs to see if we own any that are assigned */
+ vfdev = pci_get_device(dev->vendor, dev_id, NULL);
+ while (vfdev) {
+ /*
+ * It is considered assigned if it is a virtual function with
+ * our dev as the physical function and the assigned bit is set
+ */
+ if (vfdev->is_virtfn && (vfdev->physfn == dev) &&
+ (vfdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED))
+ vfs_assigned++;
+
+ vfdev = pci_get_device(dev->vendor, dev_id, vfdev);
}
- ctrl = PCI_ATS_CTRL_ENABLE;
- if (!dev->is_virtfn)
- ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU);
- pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl);
-
- dev->ats->is_enabled = 1;
-
- return 0;
+ return vfs_assigned;
}
+EXPORT_SYMBOL_GPL(pci_vfs_assigned);
/**
- * pci_disable_ats - disable the ATS capability
- * @dev: the PCI device
+ * pci_sriov_set_totalvfs -- reduce the TotalVFs available
+ * @dev: the PCI PF device
+ * @numvfs: number that should be used for TotalVFs supported
+ *
+ * Should be called from PF driver's probe routine with
+ * device's mutex held.
+ *
+ * Returns 0 if PF is an SRIOV-capable device and
+ * value of numvfs valid. If not a PF return -ENOSYS;
+ * if numvfs is invalid return -EINVAL;
+ * if VFs already enabled, return -EBUSY.
*/
-void pci_disable_ats(struct pci_dev *dev)
+int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
{
- u16 ctrl;
-
- BUG_ON(!dev->ats || !dev->ats->is_enabled);
-
- pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl);
- ctrl &= ~PCI_ATS_CTRL_ENABLE;
- pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl);
-
- dev->ats->is_enabled = 0;
-
- if (dev->is_physfn || dev->is_virtfn) {
- struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn;
+ if (!dev->is_physfn)
+ return -ENOSYS;
+ if (numvfs > dev->sriov->total_VFs)
+ return -EINVAL;
- mutex_lock(&pdev->sriov->lock);
- pdev->ats->ref_cnt--;
- if (!pdev->ats->ref_cnt)
- ats_free_one(pdev);
- mutex_unlock(&pdev->sriov->lock);
- }
+ /* Shouldn't change if VFs already enabled */
+ if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
+ return -EBUSY;
+ else
+ dev->sriov->driver_max_VFs = numvfs;
- if (!dev->is_physfn)
- ats_free_one(dev);
+ return 0;
}
+EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
/**
- * pci_ats_queue_depth - query the ATS Invalidate Queue Depth
- * @dev: the PCI device
+ * pci_sriov_get_totalvfs -- get total VFs supported on this device
+ * @dev: the PCI PF device
*
- * Returns the queue depth on success, or negative on failure.
- *
- * The ATS spec uses 0 in the Invalidate Queue Depth field to
- * indicate that the function can accept 32 Invalidate Request.
- * But here we use the `real' values (i.e. 1~32) for the Queue
- * Depth; and 0 indicates the function shares the Queue with
- * other functions (doesn't exclusively own a Queue).
+ * 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 0.
*/
-int pci_ats_queue_depth(struct pci_dev *dev)
+int pci_sriov_get_totalvfs(struct pci_dev *dev)
{
- int pos;
- u16 cap;
-
- if (dev->is_virtfn)
+ if (!dev->is_physfn)
return 0;
- if (dev->ats)
- return dev->ats->qdep;
-
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS);
- if (!pos)
- return -ENODEV;
-
- pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap);
+ if (dev->sriov->driver_max_VFs)
+ return dev->sriov->driver_max_VFs;
- return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) :
- PCI_ATS_MAX_QDEP;
+ return dev->sriov->total_VFs;
}
+EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);