|
@@ -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);
|