diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2014-01-15 12:53:35 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2014-01-15 12:53:35 -0500 |
commit | 6e2d98dc1af49a6736cd9c54e2e44b5d5234d242 (patch) | |
tree | f17178aad8cc3e721c8da14ad439379a03a488ef | |
parent | 40304618468cae5259f77c15511e4feb7bfb624b (diff) | |
parent | 890ed578df82f5b7b5a874f9f2fa4f117305df5f (diff) |
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()
-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); |