summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2017-07-03 08:00:29 -0500
committerBjorn Helgaas <bhelgaas@google.com>2017-07-03 08:00:29 -0500
commitbb02ce95a59ee5b96579fab2e2e7635ee26d535b (patch)
tree5a4b426761d6ccded785b88eb70e91f746a62fa1 /drivers/pci
parentc781f85bc1958391e2d305fe6f52c90de0adb92e (diff)
parent52354b9d1f46aae7386db7bb8ec8484b5488087f (diff)
downloadlinux-bb02ce95a59ee5b96579fab2e2e7635ee26d535b.tar.bz2
Merge branch 'pci/virtualization' into next
* pci/virtualization: PCI: Remove __pci_dev_reset() and pci_dev_reset() PCI: Split ->reset_notify() method into ->reset_prepare() and ->reset_done() PCI: Protect pci_error_handlers->reset_notify() usage with device_lock() PCI: Protect pci_driver->sriov_configure() usage with device_lock() PCI: Mark Intel XXV710 NIC INTx masking as broken PCI: Restore PRI and PASID state after Function-Level Reset PCI: Cache PRI and PASID bits in pci_dev
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/ats.c87
-rw-r--r--drivers/pci/iov.c4
-rw-r--r--drivers/pci/pci-sysfs.c5
-rw-r--r--drivers/pci/pci.c161
-rw-r--r--drivers/pci/pci.h1
-rw-r--r--drivers/pci/quirks.c4
6 files changed, 160 insertions, 102 deletions
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c
index eeb9fb2b47aa..ad8ddbbbf245 100644
--- a/drivers/pci/ats.c
+++ b/drivers/pci/ats.c
@@ -153,23 +153,27 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs)
u32 max_requests;
int pos;
+ if (WARN_ON(pdev->pri_enabled))
+ return -EBUSY;
+
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
if (!pos)
return -EINVAL;
- pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status);
- if ((control & PCI_PRI_CTRL_ENABLE) ||
- !(status & PCI_PRI_STATUS_STOPPED))
+ if (!(status & PCI_PRI_STATUS_STOPPED))
return -EBUSY;
pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests);
reqs = min(max_requests, reqs);
+ pdev->pri_reqs_alloc = reqs;
pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs);
- control |= PCI_PRI_CTRL_ENABLE;
+ control = PCI_PRI_CTRL_ENABLE;
pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
+ pdev->pri_enabled = 1;
+
return 0;
}
EXPORT_SYMBOL_GPL(pci_enable_pri);
@@ -185,6 +189,9 @@ void pci_disable_pri(struct pci_dev *pdev)
u16 control;
int pos;
+ if (WARN_ON(!pdev->pri_enabled))
+ return;
+
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
if (!pos)
return;
@@ -192,10 +199,34 @@ void pci_disable_pri(struct pci_dev *pdev)
pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
control &= ~PCI_PRI_CTRL_ENABLE;
pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
+
+ pdev->pri_enabled = 0;
}
EXPORT_SYMBOL_GPL(pci_disable_pri);
/**
+ * pci_restore_pri_state - Restore PRI
+ * @pdev: PCI device structure
+ */
+void pci_restore_pri_state(struct pci_dev *pdev)
+{
+ u16 control = PCI_PRI_CTRL_ENABLE;
+ u32 reqs = pdev->pri_reqs_alloc;
+ int pos;
+
+ if (!pdev->pri_enabled)
+ return;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
+ if (!pos)
+ return;
+
+ pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs);
+ pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
+}
+EXPORT_SYMBOL_GPL(pci_restore_pri_state);
+
+/**
* pci_reset_pri - Resets device's PRI state
* @pdev: PCI device structure
*
@@ -207,16 +238,14 @@ int pci_reset_pri(struct pci_dev *pdev)
u16 control;
int pos;
+ if (WARN_ON(pdev->pri_enabled))
+ return -EBUSY;
+
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI);
if (!pos)
return -EINVAL;
- pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control);
- if (control & PCI_PRI_CTRL_ENABLE)
- return -EBUSY;
-
- control |= PCI_PRI_CTRL_RESET;
-
+ control = PCI_PRI_CTRL_RESET;
pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control);
return 0;
@@ -239,16 +268,14 @@ int pci_enable_pasid(struct pci_dev *pdev, int features)
u16 control, supported;
int pos;
+ if (WARN_ON(pdev->pasid_enabled))
+ return -EBUSY;
+
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
if (!pos)
return -EINVAL;
- pci_read_config_word(pdev, pos + PCI_PASID_CTRL, &control);
pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported);
-
- if (control & PCI_PASID_CTRL_ENABLE)
- return -EINVAL;
-
supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
/* User wants to enable anything unsupported? */
@@ -256,9 +283,12 @@ int pci_enable_pasid(struct pci_dev *pdev, int features)
return -EINVAL;
control = PCI_PASID_CTRL_ENABLE | features;
+ pdev->pasid_features = features;
pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
+ pdev->pasid_enabled = 1;
+
return 0;
}
EXPORT_SYMBOL_GPL(pci_enable_pasid);
@@ -266,22 +296,47 @@ EXPORT_SYMBOL_GPL(pci_enable_pasid);
/**
* pci_disable_pasid - Disable the PASID capability
* @pdev: PCI device structure
- *
*/
void pci_disable_pasid(struct pci_dev *pdev)
{
u16 control = 0;
int pos;
+ if (WARN_ON(!pdev->pasid_enabled))
+ return;
+
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
if (!pos)
return;
pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
+
+ pdev->pasid_enabled = 0;
}
EXPORT_SYMBOL_GPL(pci_disable_pasid);
/**
+ * pci_restore_pasid_state - Restore PASID capabilities
+ * @pdev: PCI device structure
+ */
+void pci_restore_pasid_state(struct pci_dev *pdev)
+{
+ u16 control;
+ int pos;
+
+ if (!pdev->pasid_enabled)
+ return;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
+ if (!pos)
+ return;
+
+ control = PCI_PASID_CTRL_ENABLE | pdev->pasid_features;
+ pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control);
+}
+EXPORT_SYMBOL_GPL(pci_restore_pasid_state);
+
+/**
* pci_pasid_features - Check which PASID features are supported
* @pdev: PCI device structure
*
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index d9dc7363ac77..120485d6f352 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -461,8 +461,6 @@ found:
else
iov->dev = dev;
- mutex_init(&iov->lock);
-
dev->sriov = iov;
dev->is_physfn = 1;
rc = compute_max_vf_buses(dev);
@@ -491,8 +489,6 @@ static void sriov_release(struct pci_dev *dev)
if (dev != dev->sriov->dev)
pci_dev_put(dev->sriov->dev);
- mutex_destroy(&dev->sriov->lock);
-
kfree(dev->sriov);
dev->sriov = NULL;
}
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index a3537cf58a20..2f3780b50723 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -595,7 +595,6 @@ static ssize_t sriov_numvfs_store(struct device *dev,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
- struct pci_sriov *iov = pdev->sriov;
int ret;
u16 num_vfs;
@@ -606,7 +605,7 @@ static ssize_t sriov_numvfs_store(struct device *dev,
if (num_vfs > pci_sriov_get_totalvfs(pdev))
return -ERANGE;
- mutex_lock(&iov->dev->sriov->lock);
+ device_lock(&pdev->dev);
if (num_vfs == pdev->sriov->num_VFs)
goto exit;
@@ -641,7 +640,7 @@ static ssize_t sriov_numvfs_store(struct device *dev,
num_vfs, ret);
exit:
- mutex_unlock(&iov->dev->sriov->lock);
+ device_unlock(&pdev->dev);
if (ret < 0)
return ret;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 703741242375..34b5fe025dd4 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -28,6 +28,7 @@
#include <linux/pm_runtime.h>
#include <linux/pci_hotplug.h>
#include <linux/vmalloc.h>
+#include <linux/pci-ats.h>
#include <asm/setup.h>
#include <asm/dma.h>
#include <linux/aer.h>
@@ -1173,6 +1174,8 @@ void pci_restore_state(struct pci_dev *dev)
/* PCI Express register must be restored first */
pci_restore_pcie_state(dev);
+ pci_restore_pasid_state(dev);
+ pci_restore_pri_state(dev);
pci_restore_ats_state(dev);
pci_restore_vc_state(dev);
@@ -4033,40 +4036,6 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
}
-static int __pci_dev_reset(struct pci_dev *dev, int probe)
-{
- int rc;
-
- might_sleep();
-
- rc = pci_dev_specific_reset(dev, probe);
- if (rc != -ENOTTY)
- goto done;
-
- if (pcie_has_flr(dev)) {
- if (!probe)
- pcie_flr(dev);
- rc = 0;
- goto done;
- }
-
- rc = pci_af_flr(dev, probe);
- if (rc != -ENOTTY)
- goto done;
-
- rc = pci_pm_reset(dev, probe);
- if (rc != -ENOTTY)
- goto done;
-
- rc = pci_dev_reset_slot_function(dev, probe);
- if (rc != -ENOTTY)
- goto done;
-
- rc = pci_parent_bus_reset(dev, probe);
-done:
- return rc;
-}
-
static void pci_dev_lock(struct pci_dev *dev)
{
pci_cfg_access_lock(dev);
@@ -4092,26 +4061,18 @@ static void pci_dev_unlock(struct pci_dev *dev)
pci_cfg_access_unlock(dev);
}
-/**
- * pci_reset_notify - notify device driver of reset
- * @dev: device to be notified of reset
- * @prepare: 'true' if device is about to be reset; 'false' if reset attempt
- * completed
- *
- * Must be called prior to device access being disabled and after device
- * access is restored.
- */
-static void pci_reset_notify(struct pci_dev *dev, bool prepare)
+static void pci_dev_save_and_disable(struct pci_dev *dev)
{
const struct pci_error_handlers *err_handler =
dev->driver ? dev->driver->err_handler : NULL;
- if (err_handler && err_handler->reset_notify)
- err_handler->reset_notify(dev, prepare);
-}
-static void pci_dev_save_and_disable(struct pci_dev *dev)
-{
- pci_reset_notify(dev, true);
+ /*
+ * dev->driver->err_handler->reset_prepare() is protected against
+ * races with ->remove() by the device lock, which must be held by
+ * the caller.
+ */
+ if (err_handler && err_handler->reset_prepare)
+ err_handler->reset_prepare(dev);
/*
* Wake-up device prior to save. PM registers default to D0 after
@@ -4133,23 +4094,18 @@ static void pci_dev_save_and_disable(struct pci_dev *dev)
static void pci_dev_restore(struct pci_dev *dev)
{
- pci_restore_state(dev);
- pci_reset_notify(dev, false);
-}
-
-static int pci_dev_reset(struct pci_dev *dev, int probe)
-{
- int rc;
-
- if (!probe)
- pci_dev_lock(dev);
-
- rc = __pci_dev_reset(dev, probe);
+ const struct pci_error_handlers *err_handler =
+ dev->driver ? dev->driver->err_handler : NULL;
- if (!probe)
- pci_dev_unlock(dev);
+ pci_restore_state(dev);
- return rc;
+ /*
+ * dev->driver->err_handler->reset_done() is protected against
+ * races with ->remove() by the device lock, which must be held by
+ * the caller.
+ */
+ if (err_handler && err_handler->reset_done)
+ err_handler->reset_done(dev);
}
/**
@@ -4171,7 +4127,13 @@ static int pci_dev_reset(struct pci_dev *dev, int probe)
*/
int __pci_reset_function(struct pci_dev *dev)
{
- return pci_dev_reset(dev, 0);
+ int ret;
+
+ pci_dev_lock(dev);
+ ret = __pci_reset_function_locked(dev);
+ pci_dev_unlock(dev);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(__pci_reset_function);
@@ -4196,7 +4158,27 @@ EXPORT_SYMBOL_GPL(__pci_reset_function);
*/
int __pci_reset_function_locked(struct pci_dev *dev)
{
- return __pci_dev_reset(dev, 0);
+ int rc;
+
+ might_sleep();
+
+ rc = pci_dev_specific_reset(dev, 0);
+ if (rc != -ENOTTY)
+ return rc;
+ if (pcie_has_flr(dev)) {
+ pcie_flr(dev);
+ return 0;
+ }
+ rc = pci_af_flr(dev, 0);
+ if (rc != -ENOTTY)
+ return rc;
+ rc = pci_pm_reset(dev, 0);
+ if (rc != -ENOTTY)
+ return rc;
+ rc = pci_dev_reset_slot_function(dev, 0);
+ if (rc != -ENOTTY)
+ return rc;
+ return pci_parent_bus_reset(dev, 0);
}
EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
@@ -4213,7 +4195,26 @@ EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
*/
int pci_probe_reset_function(struct pci_dev *dev)
{
- return pci_dev_reset(dev, 1);
+ int rc;
+
+ might_sleep();
+
+ rc = pci_dev_specific_reset(dev, 1);
+ if (rc != -ENOTTY)
+ return rc;
+ if (pcie_has_flr(dev))
+ return 0;
+ rc = pci_af_flr(dev, 1);
+ if (rc != -ENOTTY)
+ return rc;
+ rc = pci_pm_reset(dev, 1);
+ if (rc != -ENOTTY)
+ return rc;
+ rc = pci_dev_reset_slot_function(dev, 1);
+ if (rc != -ENOTTY)
+ return rc;
+
+ return pci_parent_bus_reset(dev, 1);
}
/**
@@ -4236,15 +4237,17 @@ int pci_reset_function(struct pci_dev *dev)
{
int rc;
- rc = pci_dev_reset(dev, 1);
+ rc = pci_probe_reset_function(dev);
if (rc)
return rc;
+ pci_dev_lock(dev);
pci_dev_save_and_disable(dev);
- rc = pci_dev_reset(dev, 0);
+ rc = __pci_reset_function_locked(dev);
pci_dev_restore(dev);
+ pci_dev_unlock(dev);
return rc;
}
@@ -4260,20 +4263,18 @@ int pci_try_reset_function(struct pci_dev *dev)
{
int rc;
- rc = pci_dev_reset(dev, 1);
+ rc = pci_probe_reset_function(dev);
if (rc)
return rc;
- pci_dev_save_and_disable(dev);
+ if (!pci_dev_trylock(dev))
+ return -EAGAIN;
- if (pci_dev_trylock(dev)) {
- rc = __pci_dev_reset(dev, 0);
- pci_dev_unlock(dev);
- } else
- rc = -EAGAIN;
+ pci_dev_save_and_disable(dev);
+ rc = __pci_reset_function_locked(dev);
+ pci_dev_unlock(dev);
pci_dev_restore(dev);
-
return rc;
}
EXPORT_SYMBOL_GPL(pci_try_reset_function);
@@ -4423,7 +4424,9 @@ static void pci_bus_save_and_disable(struct pci_bus *bus)
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_dev_lock(dev);
pci_dev_save_and_disable(dev);
+ pci_dev_unlock(dev);
if (dev->subordinate)
pci_bus_save_and_disable(dev->subordinate);
}
@@ -4438,7 +4441,9 @@ static void pci_bus_restore(struct pci_bus *bus)
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_dev_lock(dev);
pci_dev_restore(dev);
+ pci_dev_unlock(dev);
if (dev->subordinate)
pci_bus_restore(dev->subordinate);
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index f8113e5b9812..93f4044b8f4b 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -272,7 +272,6 @@ struct pci_sriov {
u16 driver_max_VFs; /* max num VFs driver supports */
struct pci_dev *dev; /* lowest numbered PF */
struct pci_dev *self; /* this PF */
- struct mutex lock; /* lock for setting sriov_numvfs in sysfs */
resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */
bool drivers_autoprobe; /* auto probing of VFs by driver */
};
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index e1deb796976a..6967c6b4cf6b 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3249,6 +3249,10 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1588,
quirk_broken_intx_masking);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1589,
quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x158a,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x158b,
+ quirk_broken_intx_masking);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d0,
quirk_broken_intx_masking);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d1,