diff options
| -rw-r--r-- | drivers/pci/pci.c | 155 | ||||
| -rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 29 | ||||
| -rw-r--r-- | include/linux/pci.h | 3 |
3 files changed, 167 insertions, 20 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 95c97bec8018..b6d4afa8ba40 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -3250,6 +3250,18 @@ static void pci_dev_lock(struct pci_dev *dev) | |||
| 3250 | device_lock(&dev->dev); | 3250 | device_lock(&dev->dev); |
| 3251 | } | 3251 | } |
| 3252 | 3252 | ||
| 3253 | /* Return 1 on successful lock, 0 on contention */ | ||
| 3254 | static int pci_dev_trylock(struct pci_dev *dev) | ||
| 3255 | { | ||
| 3256 | if (pci_cfg_access_trylock(dev)) { | ||
| 3257 | if (device_trylock(&dev->dev)) | ||
| 3258 | return 1; | ||
| 3259 | pci_cfg_access_unlock(dev); | ||
| 3260 | } | ||
| 3261 | |||
| 3262 | return 0; | ||
| 3263 | } | ||
| 3264 | |||
| 3253 | static void pci_dev_unlock(struct pci_dev *dev) | 3265 | static void pci_dev_unlock(struct pci_dev *dev) |
| 3254 | { | 3266 | { |
| 3255 | device_unlock(&dev->dev); | 3267 | device_unlock(&dev->dev); |
| @@ -3393,6 +3405,34 @@ int pci_reset_function(struct pci_dev *dev) | |||
| 3393 | } | 3405 | } |
| 3394 | EXPORT_SYMBOL_GPL(pci_reset_function); | 3406 | EXPORT_SYMBOL_GPL(pci_reset_function); |
| 3395 | 3407 | ||
| 3408 | /** | ||
| 3409 | * pci_try_reset_function - quiesce and reset a PCI device function | ||
| 3410 | * @dev: PCI device to reset | ||
| 3411 | * | ||
| 3412 | * Same as above, except return -EAGAIN if unable to lock device. | ||
| 3413 | */ | ||
| 3414 | int pci_try_reset_function(struct pci_dev *dev) | ||
| 3415 | { | ||
| 3416 | int rc; | ||
| 3417 | |||
| 3418 | rc = pci_dev_reset(dev, 1); | ||
| 3419 | if (rc) | ||
| 3420 | return rc; | ||
| 3421 | |||
| 3422 | pci_dev_save_and_disable(dev); | ||
| 3423 | |||
| 3424 | if (pci_dev_trylock(dev)) { | ||
| 3425 | rc = __pci_dev_reset(dev, 0); | ||
| 3426 | pci_dev_unlock(dev); | ||
| 3427 | } else | ||
| 3428 | rc = -EAGAIN; | ||
| 3429 | |||
| 3430 | pci_dev_restore(dev); | ||
| 3431 | |||
| 3432 | return rc; | ||
| 3433 | } | ||
| 3434 | EXPORT_SYMBOL_GPL(pci_try_reset_function); | ||
| 3435 | |||
| 3396 | /* Lock devices from the top of the tree down */ | 3436 | /* Lock devices from the top of the tree down */ |
| 3397 | static void pci_bus_lock(struct pci_bus *bus) | 3437 | static void pci_bus_lock(struct pci_bus *bus) |
| 3398 | { | 3438 | { |
| @@ -3417,6 +3457,32 @@ static void pci_bus_unlock(struct pci_bus *bus) | |||
| 3417 | } | 3457 | } |
| 3418 | } | 3458 | } |
| 3419 | 3459 | ||
| 3460 | /* Return 1 on successful lock, 0 on contention */ | ||
| 3461 | static int pci_bus_trylock(struct pci_bus *bus) | ||
| 3462 | { | ||
| 3463 | struct pci_dev *dev; | ||
| 3464 | |||
| 3465 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
| 3466 | if (!pci_dev_trylock(dev)) | ||
| 3467 | goto unlock; | ||
| 3468 | if (dev->subordinate) { | ||
| 3469 | if (!pci_bus_trylock(dev->subordinate)) { | ||
| 3470 | pci_dev_unlock(dev); | ||
| 3471 | goto unlock; | ||
| 3472 | } | ||
| 3473 | } | ||
| 3474 | } | ||
| 3475 | return 1; | ||
| 3476 | |||
| 3477 | unlock: | ||
| 3478 | list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) { | ||
| 3479 | if (dev->subordinate) | ||
| 3480 | pci_bus_unlock(dev->subordinate); | ||
| 3481 | pci_dev_unlock(dev); | ||
| 3482 | } | ||
| 3483 | return 0; | ||
| 3484 | } | ||
| 3485 | |||
| 3420 | /* Lock devices from the top of the tree down */ | 3486 | /* Lock devices from the top of the tree down */ |
| 3421 | static void pci_slot_lock(struct pci_slot *slot) | 3487 | static void pci_slot_lock(struct pci_slot *slot) |
| 3422 | { | 3488 | { |
| @@ -3445,6 +3511,37 @@ static void pci_slot_unlock(struct pci_slot *slot) | |||
| 3445 | } | 3511 | } |
| 3446 | } | 3512 | } |
| 3447 | 3513 | ||
| 3514 | /* Return 1 on successful lock, 0 on contention */ | ||
| 3515 | static int pci_slot_trylock(struct pci_slot *slot) | ||
| 3516 | { | ||
| 3517 | struct pci_dev *dev; | ||
| 3518 | |||
| 3519 | list_for_each_entry(dev, &slot->bus->devices, bus_list) { | ||
| 3520 | if (!dev->slot || dev->slot != slot) | ||
| 3521 | continue; | ||
| 3522 | if (!pci_dev_trylock(dev)) | ||
| 3523 | goto unlock; | ||
| 3524 | if (dev->subordinate) { | ||
| 3525 | if (!pci_bus_trylock(dev->subordinate)) { | ||
| 3526 | pci_dev_unlock(dev); | ||
| 3527 | goto unlock; | ||
| 3528 | } | ||
| 3529 | } | ||
| 3530 | } | ||
| 3531 | return 1; | ||
| 3532 | |||
| 3533 | unlock: | ||
| 3534 | list_for_each_entry_continue_reverse(dev, | ||
| 3535 | &slot->bus->devices, bus_list) { | ||
| 3536 | if (!dev->slot || dev->slot != slot) | ||
| 3537 | continue; | ||
| 3538 | if (dev->subordinate) | ||
| 3539 | pci_bus_unlock(dev->subordinate); | ||
| 3540 | pci_dev_unlock(dev); | ||
| 3541 | } | ||
| 3542 | return 0; | ||
| 3543 | } | ||
| 3544 | |||
| 3448 | /* Save and disable devices from the top of the tree down */ | 3545 | /* Save and disable devices from the top of the tree down */ |
| 3449 | static void pci_bus_save_and_disable(struct pci_bus *bus) | 3546 | static void pci_bus_save_and_disable(struct pci_bus *bus) |
| 3450 | { | 3547 | { |
| @@ -3568,6 +3665,35 @@ int pci_reset_slot(struct pci_slot *slot) | |||
| 3568 | } | 3665 | } |
| 3569 | EXPORT_SYMBOL_GPL(pci_reset_slot); | 3666 | EXPORT_SYMBOL_GPL(pci_reset_slot); |
| 3570 | 3667 | ||
| 3668 | /** | ||
| 3669 | * pci_try_reset_slot - Try to reset a PCI slot | ||
| 3670 | * @slot: PCI slot to reset | ||
| 3671 | * | ||
| 3672 | * Same as above except return -EAGAIN if the slot cannot be locked | ||
| 3673 | */ | ||
| 3674 | int pci_try_reset_slot(struct pci_slot *slot) | ||
| 3675 | { | ||
| 3676 | int rc; | ||
| 3677 | |||
| 3678 | rc = pci_slot_reset(slot, 1); | ||
| 3679 | if (rc) | ||
| 3680 | return rc; | ||
| 3681 | |||
| 3682 | pci_slot_save_and_disable(slot); | ||
| 3683 | |||
| 3684 | if (pci_slot_trylock(slot)) { | ||
| 3685 | might_sleep(); | ||
| 3686 | rc = pci_reset_hotplug_slot(slot->hotplug, 0); | ||
| 3687 | pci_slot_unlock(slot); | ||
| 3688 | } else | ||
| 3689 | rc = -EAGAIN; | ||
| 3690 | |||
| 3691 | pci_slot_restore(slot); | ||
| 3692 | |||
| 3693 | return rc; | ||
| 3694 | } | ||
| 3695 | EXPORT_SYMBOL_GPL(pci_try_reset_slot); | ||
| 3696 | |||
| 3571 | static int pci_bus_reset(struct pci_bus *bus, int probe) | 3697 | static int pci_bus_reset(struct pci_bus *bus, int probe) |
| 3572 | { | 3698 | { |
| 3573 | if (!bus->self) | 3699 | if (!bus->self) |
| @@ -3627,6 +3753,35 @@ int pci_reset_bus(struct pci_bus *bus) | |||
| 3627 | EXPORT_SYMBOL_GPL(pci_reset_bus); | 3753 | EXPORT_SYMBOL_GPL(pci_reset_bus); |
| 3628 | 3754 | ||
| 3629 | /** | 3755 | /** |
| 3756 | * pci_try_reset_bus - Try to reset a PCI bus | ||
| 3757 | * @bus: top level PCI bus to reset | ||
| 3758 | * | ||
| 3759 | * Same as above except return -EAGAIN if the bus cannot be locked | ||
| 3760 | */ | ||
| 3761 | int pci_try_reset_bus(struct pci_bus *bus) | ||
| 3762 | { | ||
| 3763 | int rc; | ||
| 3764 | |||
| 3765 | rc = pci_bus_reset(bus, 1); | ||
| 3766 | if (rc) | ||
| 3767 | return rc; | ||
| 3768 | |||
| 3769 | pci_bus_save_and_disable(bus); | ||
| 3770 | |||
| 3771 | if (pci_bus_trylock(bus)) { | ||
| 3772 | might_sleep(); | ||
| 3773 | pci_reset_bridge_secondary_bus(bus->self); | ||
| 3774 | pci_bus_unlock(bus); | ||
| 3775 | } else | ||
| 3776 | rc = -EAGAIN; | ||
| 3777 | |||
| 3778 | pci_bus_restore(bus); | ||
| 3779 | |||
| 3780 | return rc; | ||
| 3781 | } | ||
| 3782 | EXPORT_SYMBOL_GPL(pci_try_reset_bus); | ||
| 3783 | |||
| 3784 | /** | ||
| 3630 | * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count | 3785 | * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count |
| 3631 | * @dev: PCI device to query | 3786 | * @dev: PCI device to query |
| 3632 | * | 3787 | * |
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 6ab71b9fcf8d..2319d206f630 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c | |||
| @@ -139,25 +139,14 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) | |||
| 139 | pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); | 139 | pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); |
| 140 | 140 | ||
| 141 | /* | 141 | /* |
| 142 | * Careful, device_lock may already be held. This is the case if | 142 | * Try to reset the device. The success of this is dependent on |
| 143 | * a driver unbind is blocked. Try to get the locks ourselves to | 143 | * being able to lock the device, which is not always possible. |
| 144 | * prevent a deadlock. | ||
| 145 | */ | 144 | */ |
| 146 | if (vdev->reset_works) { | 145 | if (vdev->reset_works) { |
| 147 | bool reset_done = false; | 146 | int ret = pci_try_reset_function(pdev); |
| 148 | 147 | if (ret) | |
| 149 | if (pci_cfg_access_trylock(pdev)) { | 148 | pr_warn("%s: Failed to reset device %s (%d)\n", |
| 150 | if (device_trylock(&pdev->dev)) { | 149 | __func__, dev_name(&pdev->dev), ret); |
| 151 | __pci_reset_function_locked(pdev); | ||
| 152 | reset_done = true; | ||
| 153 | device_unlock(&pdev->dev); | ||
| 154 | } | ||
| 155 | pci_cfg_access_unlock(pdev); | ||
| 156 | } | ||
| 157 | |||
| 158 | if (!reset_done) | ||
| 159 | pr_warn("%s: Unable to acquire locks for reset of %s\n", | ||
| 160 | __func__, dev_name(&pdev->dev)); | ||
| 161 | } | 150 | } |
| 162 | 151 | ||
| 163 | pci_restore_state(pdev); | 152 | pci_restore_state(pdev); |
| @@ -514,7 +503,7 @@ static long vfio_pci_ioctl(void *device_data, | |||
| 514 | 503 | ||
| 515 | } else if (cmd == VFIO_DEVICE_RESET) { | 504 | } else if (cmd == VFIO_DEVICE_RESET) { |
| 516 | return vdev->reset_works ? | 505 | return vdev->reset_works ? |
| 517 | pci_reset_function(vdev->pdev) : -EINVAL; | 506 | pci_try_reset_function(vdev->pdev) : -EINVAL; |
| 518 | 507 | ||
| 519 | } else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) { | 508 | } else if (cmd == VFIO_DEVICE_GET_PCI_HOT_RESET_INFO) { |
| 520 | struct vfio_pci_hot_reset_info hdr; | 509 | struct vfio_pci_hot_reset_info hdr; |
| @@ -684,8 +673,8 @@ reset_info_exit: | |||
| 684 | &info, slot); | 673 | &info, slot); |
| 685 | if (!ret) | 674 | if (!ret) |
| 686 | /* User has access, do the reset */ | 675 | /* User has access, do the reset */ |
| 687 | ret = slot ? pci_reset_slot(vdev->pdev->slot) : | 676 | ret = slot ? pci_try_reset_slot(vdev->pdev->slot) : |
| 688 | pci_reset_bus(vdev->pdev->bus); | 677 | pci_try_reset_bus(vdev->pdev->bus); |
| 689 | 678 | ||
| 690 | hot_reset_release: | 679 | hot_reset_release: |
| 691 | for (i--; i >= 0; i--) | 680 | for (i--; i >= 0; i--) |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 2087e6b35545..fa959aac6169 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
| @@ -949,10 +949,13 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, | |||
| 949 | int __pci_reset_function(struct pci_dev *dev); | 949 | int __pci_reset_function(struct pci_dev *dev); |
| 950 | int __pci_reset_function_locked(struct pci_dev *dev); | 950 | int __pci_reset_function_locked(struct pci_dev *dev); |
| 951 | int pci_reset_function(struct pci_dev *dev); | 951 | int pci_reset_function(struct pci_dev *dev); |
| 952 | int pci_try_reset_function(struct pci_dev *dev); | ||
| 952 | int pci_probe_reset_slot(struct pci_slot *slot); | 953 | int pci_probe_reset_slot(struct pci_slot *slot); |
| 953 | int pci_reset_slot(struct pci_slot *slot); | 954 | int pci_reset_slot(struct pci_slot *slot); |
| 955 | int pci_try_reset_slot(struct pci_slot *slot); | ||
| 954 | int pci_probe_reset_bus(struct pci_bus *bus); | 956 | int pci_probe_reset_bus(struct pci_bus *bus); |
| 955 | int pci_reset_bus(struct pci_bus *bus); | 957 | int pci_reset_bus(struct pci_bus *bus); |
| 958 | int pci_try_reset_bus(struct pci_bus *bus); | ||
| 956 | void pci_reset_bridge_secondary_bus(struct pci_dev *dev); | 959 | void pci_reset_bridge_secondary_bus(struct pci_dev *dev); |
| 957 | void pci_update_resource(struct pci_dev *dev, int resno); | 960 | void pci_update_resource(struct pci_dev *dev, int resno); |
| 958 | int __must_check pci_assign_resource(struct pci_dev *dev, int i); | 961 | int __must_check pci_assign_resource(struct pci_dev *dev, int i); |
