vfio-pci: Use pci "try" reset interface
PCI resets will attempt to take the device_lock for any device to be reset. This is a problem if that lock is already held, for instance in the device remove path. It's not sufficient to simply kill the user process or skip the reset if called after .remove as a race could result in the same deadlock. Instead, we handle all resets as "best effort" using the PCI "try" reset interfaces. This prevents the user from being able to induce a deadlock by triggering a reset. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
parent
61cf16d8bd
commit
890ed578df
@ -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--)
|
||||||
|
Loading…
Reference in New Issue
Block a user