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 /drivers/pci | |
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()
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pci.c | 155 |
1 files changed, 155 insertions, 0 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 | * |