diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/access.c | 74 | ||||
-rw-r--r-- | drivers/pci/iov.c | 12 | ||||
-rw-r--r-- | drivers/pci/pci.c | 4 |
3 files changed, 56 insertions, 34 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c index fdaa42aac7c6..0c4c71712dfc 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -127,20 +127,20 @@ EXPORT_SYMBOL(pci_write_vpd); * We have a bit per device to indicate it's blocked and a global wait queue * for callers to sleep on until devices are unblocked. */ -static DECLARE_WAIT_QUEUE_HEAD(pci_ucfg_wait); +static DECLARE_WAIT_QUEUE_HEAD(pci_cfg_wait); -static noinline void pci_wait_ucfg(struct pci_dev *dev) +static noinline void pci_wait_cfg(struct pci_dev *dev) { DECLARE_WAITQUEUE(wait, current); - __add_wait_queue(&pci_ucfg_wait, &wait); + __add_wait_queue(&pci_cfg_wait, &wait); do { set_current_state(TASK_UNINTERRUPTIBLE); raw_spin_unlock_irq(&pci_lock); schedule(); raw_spin_lock_irq(&pci_lock); - } while (dev->block_ucfg_access); - __remove_wait_queue(&pci_ucfg_wait, &wait); + } while (dev->block_cfg_access); + __remove_wait_queue(&pci_cfg_wait, &wait); } /* Returns 0 on success, negative values indicate error. */ @@ -153,7 +153,8 @@ int pci_user_read_config_##size \ if (PCI_##size##_BAD) \ return -EINVAL; \ raw_spin_lock_irq(&pci_lock); \ - if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \ + if (unlikely(dev->block_cfg_access)) \ + pci_wait_cfg(dev); \ ret = dev->bus->ops->read(dev->bus, dev->devfn, \ pos, sizeof(type), &data); \ raw_spin_unlock_irq(&pci_lock); \ @@ -172,7 +173,8 @@ int pci_user_write_config_##size \ if (PCI_##size##_BAD) \ return -EINVAL; \ raw_spin_lock_irq(&pci_lock); \ - if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \ + if (unlikely(dev->block_cfg_access)) \ + pci_wait_cfg(dev); \ ret = dev->bus->ops->write(dev->bus, dev->devfn, \ pos, sizeof(type), val); \ raw_spin_unlock_irq(&pci_lock); \ @@ -401,36 +403,56 @@ int pci_vpd_truncate(struct pci_dev *dev, size_t size) EXPORT_SYMBOL(pci_vpd_truncate); /** - * pci_block_user_cfg_access - Block userspace PCI config reads/writes + * pci_cfg_access_lock - Lock PCI config reads/writes * @dev: pci device struct * - * When user access is blocked, any reads or writes to config space will - * sleep until access is unblocked again. We don't allow nesting of - * block/unblock calls. + * When access is locked, any userspace reads or writes to config + * space and concurrent lock requests will sleep until access is + * allowed via pci_cfg_access_unlocked again. */ -void pci_block_user_cfg_access(struct pci_dev *dev) +void pci_cfg_access_lock(struct pci_dev *dev) +{ + might_sleep(); + + raw_spin_lock_irq(&pci_lock); + if (dev->block_cfg_access) + pci_wait_cfg(dev); + dev->block_cfg_access = 1; + raw_spin_unlock_irq(&pci_lock); +} +EXPORT_SYMBOL_GPL(pci_cfg_access_lock); + +/** + * pci_cfg_access_trylock - try to lock PCI config reads/writes + * @dev: pci device struct + * + * Same as pci_cfg_access_lock, but will return 0 if access is + * already locked, 1 otherwise. This function can be used from + * atomic contexts. + */ +bool pci_cfg_access_trylock(struct pci_dev *dev) { unsigned long flags; - int was_blocked; + bool locked = true; raw_spin_lock_irqsave(&pci_lock, flags); - was_blocked = dev->block_ucfg_access; - dev->block_ucfg_access = 1; + if (dev->block_cfg_access) + locked = false; + else + dev->block_cfg_access = 1; raw_spin_unlock_irqrestore(&pci_lock, flags); - /* If we BUG() inside the pci_lock, we're guaranteed to hose - * the machine */ - BUG_ON(was_blocked); + return locked; } -EXPORT_SYMBOL_GPL(pci_block_user_cfg_access); +EXPORT_SYMBOL_GPL(pci_cfg_access_trylock); /** - * pci_unblock_user_cfg_access - Unblock userspace PCI config reads/writes + * pci_cfg_access_unlock - Unlock PCI config reads/writes * @dev: pci device struct * - * This function allows userspace PCI config accesses to resume. + * This function allows PCI config accesses to resume. */ -void pci_unblock_user_cfg_access(struct pci_dev *dev) +void pci_cfg_access_unlock(struct pci_dev *dev) { unsigned long flags; @@ -438,10 +460,10 @@ void pci_unblock_user_cfg_access(struct pci_dev *dev) /* This indicates a problem in the caller, but we don't need * to kill them, unlike a double-block above. */ - WARN_ON(!dev->block_ucfg_access); + WARN_ON(!dev->block_cfg_access); - dev->block_ucfg_access = 0; - wake_up_all(&pci_ucfg_wait); + dev->block_cfg_access = 0; + wake_up_all(&pci_cfg_wait); raw_spin_unlock_irqrestore(&pci_lock, flags); } -EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access); +EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 1969a3ee3058..6a4d70386a3d 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -348,10 +348,10 @@ static int sriov_enable(struct pci_dev *dev, int 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; if (nr_virtfn < initial) @@ -379,10 +379,10 @@ 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); 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"); @@ -405,10 +405,10 @@ static void sriov_disable(struct pci_dev *dev) 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"); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6d4a5319148d..c3cca7cdc6e5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2965,7 +2965,7 @@ static int pci_dev_reset(struct pci_dev *dev, int probe) might_sleep(); if (!probe) { - pci_block_user_cfg_access(dev); + pci_cfg_access_lock(dev); /* block PM suspend, driver probe, etc. */ device_lock(&dev->dev); } @@ -2990,7 +2990,7 @@ static int pci_dev_reset(struct pci_dev *dev, int probe) done: if (!probe) { device_unlock(&dev->dev); - pci_unblock_user_cfg_access(dev); + pci_cfg_access_unlock(dev); } return rc; |