Merge branch 'pci/reset' into next
* pci/reset: vfio-pci: Use pci "try" reset interface PCI: Add pci_try_reset_function(), pci_try_reset_slot(), pci_try_reset_bus()
This commit is contained in:
commit
6e2d98dc1a
|
@ -3250,6 +3250,18 @@ static void pci_dev_lock(struct pci_dev *dev)
|
||||||
device_lock(&dev->dev);
|
device_lock(&dev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return 1 on successful lock, 0 on contention */
|
||||||
|
static int pci_dev_trylock(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
if (pci_cfg_access_trylock(dev)) {
|
||||||
|
if (device_trylock(&dev->dev))
|
||||||
|
return 1;
|
||||||
|
pci_cfg_access_unlock(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void pci_dev_unlock(struct pci_dev *dev)
|
static void pci_dev_unlock(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
device_unlock(&dev->dev);
|
device_unlock(&dev->dev);
|
||||||
|
@ -3393,6 +3405,34 @@ int pci_reset_function(struct pci_dev *dev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pci_reset_function);
|
EXPORT_SYMBOL_GPL(pci_reset_function);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_try_reset_function - quiesce and reset a PCI device function
|
||||||
|
* @dev: PCI device to reset
|
||||||
|
*
|
||||||
|
* Same as above, except return -EAGAIN if unable to lock device.
|
||||||
|
*/
|
||||||
|
int pci_try_reset_function(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = pci_dev_reset(dev, 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
pci_dev_save_and_disable(dev);
|
||||||
|
|
||||||
|
if (pci_dev_trylock(dev)) {
|
||||||
|
rc = __pci_dev_reset(dev, 0);
|
||||||
|
pci_dev_unlock(dev);
|
||||||
|
} else
|
||||||
|
rc = -EAGAIN;
|
||||||
|
|
||||||
|
pci_dev_restore(dev);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_try_reset_function);
|
||||||
|
|
||||||
/* Lock devices from the top of the tree down */
|
/* Lock devices from the top of the tree down */
|
||||||
static void pci_bus_lock(struct pci_bus *bus)
|
static void pci_bus_lock(struct pci_bus *bus)
|
||||||
{
|
{
|
||||||
|
@ -3417,6 +3457,32 @@ static void pci_bus_unlock(struct pci_bus *bus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return 1 on successful lock, 0 on contention */
|
||||||
|
static int pci_bus_trylock(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||||
|
if (!pci_dev_trylock(dev))
|
||||||
|
goto unlock;
|
||||||
|
if (dev->subordinate) {
|
||||||
|
if (!pci_bus_trylock(dev->subordinate)) {
|
||||||
|
pci_dev_unlock(dev);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) {
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_unlock(dev->subordinate);
|
||||||
|
pci_dev_unlock(dev);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Lock devices from the top of the tree down */
|
/* Lock devices from the top of the tree down */
|
||||||
static void pci_slot_lock(struct pci_slot *slot)
|
static void pci_slot_lock(struct pci_slot *slot)
|
||||||
{
|
{
|
||||||
|
@ -3445,6 +3511,37 @@ static void pci_slot_unlock(struct pci_slot *slot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return 1 on successful lock, 0 on contention */
|
||||||
|
static int pci_slot_trylock(struct pci_slot *slot)
|
||||||
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
|
||||||
|
if (!dev->slot || dev->slot != slot)
|
||||||
|
continue;
|
||||||
|
if (!pci_dev_trylock(dev))
|
||||||
|
goto unlock;
|
||||||
|
if (dev->subordinate) {
|
||||||
|
if (!pci_bus_trylock(dev->subordinate)) {
|
||||||
|
pci_dev_unlock(dev);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
list_for_each_entry_continue_reverse(dev,
|
||||||
|
&slot->bus->devices, bus_list) {
|
||||||
|
if (!dev->slot || dev->slot != slot)
|
||||||
|
continue;
|
||||||
|
if (dev->subordinate)
|
||||||
|
pci_bus_unlock(dev->subordinate);
|
||||||
|
pci_dev_unlock(dev);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Save and disable devices from the top of the tree down */
|
/* Save and disable devices from the top of the tree down */
|
||||||
static void pci_bus_save_and_disable(struct pci_bus *bus)
|
static void pci_bus_save_and_disable(struct pci_bus *bus)
|
||||||
{
|
{
|
||||||
|
@ -3568,6 +3665,35 @@ int pci_reset_slot(struct pci_slot *slot)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pci_reset_slot);
|
EXPORT_SYMBOL_GPL(pci_reset_slot);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_try_reset_slot - Try to reset a PCI slot
|
||||||
|
* @slot: PCI slot to reset
|
||||||
|
*
|
||||||
|
* Same as above except return -EAGAIN if the slot cannot be locked
|
||||||
|
*/
|
||||||
|
int pci_try_reset_slot(struct pci_slot *slot)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = pci_slot_reset(slot, 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
pci_slot_save_and_disable(slot);
|
||||||
|
|
||||||
|
if (pci_slot_trylock(slot)) {
|
||||||
|
might_sleep();
|
||||||
|
rc = pci_reset_hotplug_slot(slot->hotplug, 0);
|
||||||
|
pci_slot_unlock(slot);
|
||||||
|
} else
|
||||||
|
rc = -EAGAIN;
|
||||||
|
|
||||||
|
pci_slot_restore(slot);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_try_reset_slot);
|
||||||
|
|
||||||
static int pci_bus_reset(struct pci_bus *bus, int probe)
|
static int pci_bus_reset(struct pci_bus *bus, int probe)
|
||||||
{
|
{
|
||||||
if (!bus->self)
|
if (!bus->self)
|
||||||
|
@ -3626,6 +3752,35 @@ int pci_reset_bus(struct pci_bus *bus)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pci_reset_bus);
|
EXPORT_SYMBOL_GPL(pci_reset_bus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_try_reset_bus - Try to reset a PCI bus
|
||||||
|
* @bus: top level PCI bus to reset
|
||||||
|
*
|
||||||
|
* Same as above except return -EAGAIN if the bus cannot be locked
|
||||||
|
*/
|
||||||
|
int pci_try_reset_bus(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = pci_bus_reset(bus, 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
pci_bus_save_and_disable(bus);
|
||||||
|
|
||||||
|
if (pci_bus_trylock(bus)) {
|
||||||
|
might_sleep();
|
||||||
|
pci_reset_bridge_secondary_bus(bus->self);
|
||||||
|
pci_bus_unlock(bus);
|
||||||
|
} else
|
||||||
|
rc = -EAGAIN;
|
||||||
|
|
||||||
|
pci_bus_restore(bus);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pci_try_reset_bus);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
|
* pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
|
||||||
* @dev: PCI device to query
|
* @dev: PCI device to query
|
||||||
|
|
|
@ -139,25 +139,14 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
|
||||||
pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
|
pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Careful, device_lock may already be held. This is the case if
|
* Try to reset the device. The success of this is dependent on
|
||||||
* a driver unbind is blocked. Try to get the locks ourselves to
|
* being able to lock the device, which is not always possible.
|
||||||
* prevent a deadlock.
|
|
||||||
*/
|
*/
|
||||||
if (vdev->reset_works) {
|
if (vdev->reset_works) {
|
||||||
bool reset_done = false;
|
int ret = pci_try_reset_function(pdev);
|
||||||
|
if (ret)
|
||||||
if (pci_cfg_access_trylock(pdev)) {
|
pr_warn("%s: Failed to reset device %s (%d)\n",
|
||||||
if (device_trylock(&pdev->dev)) {
|
__func__, dev_name(&pdev->dev), ret);
|
||||||
__pci_reset_function_locked(pdev);
|
|
||||||
reset_done = true;
|
|
||||||
device_unlock(&pdev->dev);
|
|
||||||
}
|
|
||||||
pci_cfg_access_unlock(pdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!reset_done)
|
|
||||||
pr_warn("%s: Unable to acquire locks for reset of %s\n",
|
|
||||||
__func__, dev_name(&pdev->dev));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_restore_state(pdev);
|
pci_restore_state(pdev);
|
||||||
|
@ -514,7 +503,7 @@ static long vfio_pci_ioctl(void *device_data,
|
||||||
|
|
||||||
} else if (cmd == VFIO_DEVICE_RESET) {
|
} else if (cmd == VFIO_DEVICE_RESET) {
|
||||||
return vdev->reset_works ?
|
return vdev->reset_works ?
|
||||||
pci_reset_function(vdev->pdev) : -EINVAL;
|
pci_try_reset_function(vdev->pdev) : -EINVAL;
|
||||||
|
|
||||||
} else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) {
|
} else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) {
|
||||||
struct vfio_pci_hot_reset_info hdr;
|
struct vfio_pci_hot_reset_info hdr;
|
||||||
|
@ -684,8 +673,8 @@ reset_info_exit:
|
||||||
&info, slot);
|
&info, slot);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
/* User has access, do the reset */
|
/* User has access, do the reset */
|
||||||
ret = slot ? pci_reset_slot(vdev->pdev->slot) :
|
ret = slot ? pci_try_reset_slot(vdev->pdev->slot) :
|
||||||
pci_reset_bus(vdev->pdev->bus);
|
pci_try_reset_bus(vdev->pdev->bus);
|
||||||
|
|
||||||
hot_reset_release:
|
hot_reset_release:
|
||||||
for (i--; i >= 0; i--)
|
for (i--; i >= 0; i--)
|
||||||
|
|
|
@ -949,10 +949,13 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
|
||||||
int __pci_reset_function(struct pci_dev *dev);
|
int __pci_reset_function(struct pci_dev *dev);
|
||||||
int __pci_reset_function_locked(struct pci_dev *dev);
|
int __pci_reset_function_locked(struct pci_dev *dev);
|
||||||
int pci_reset_function(struct pci_dev *dev);
|
int pci_reset_function(struct pci_dev *dev);
|
||||||
|
int pci_try_reset_function(struct pci_dev *dev);
|
||||||
int pci_probe_reset_slot(struct pci_slot *slot);
|
int pci_probe_reset_slot(struct pci_slot *slot);
|
||||||
int pci_reset_slot(struct pci_slot *slot);
|
int pci_reset_slot(struct pci_slot *slot);
|
||||||
|
int pci_try_reset_slot(struct pci_slot *slot);
|
||||||
int pci_probe_reset_bus(struct pci_bus *bus);
|
int pci_probe_reset_bus(struct pci_bus *bus);
|
||||||
int pci_reset_bus(struct pci_bus *bus);
|
int pci_reset_bus(struct pci_bus *bus);
|
||||||
|
int pci_try_reset_bus(struct pci_bus *bus);
|
||||||
void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
|
void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
|
||||||
void pci_update_resource(struct pci_dev *dev, int resno);
|
void pci_update_resource(struct pci_dev *dev, int resno);
|
||||||
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
||||||
|
|
Loading…
Reference in New Issue