lkml.org 
[lkml]   [2020]   [Jan]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 252/639] PCI: Fix "try" semantics of bus and slot reset
    Date
    From: Alex Williamson <alex.williamson@redhat.com>

    [ Upstream commit ddefc033eecf23f1e8b81d0663c5db965adf5516 ]

    The commit referenced below introduced device locking around save and
    restore of state for each device during a PCI bus "try" reset, making it
    decidely non-"try" and prone to deadlock in the event that a device is
    already locked. Restore __pci_reset_bus() and __pci_reset_slot() to their
    advertised locking semantics by pushing the save and restore functions into
    the branch where the entire tree is already locked. Extend the helper
    function names with "_locked" and update the comment to reflect this
    calling requirement.

    Fixes: b014e96d1abb ("PCI: Protect pci_error_handlers->reset_notify() usage with device_lock()")
    Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
    Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
    Reviewed-by: Sinan Kaya <okaya@kernel.org>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    drivers/pci/pci.c | 54 +++++++++++++++++++++++------------------------
    1 file changed, 26 insertions(+), 28 deletions(-)

    diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
    index c9f51fc24563c..57a87a001b4f4 100644
    --- a/drivers/pci/pci.c
    +++ b/drivers/pci/pci.c
    @@ -5039,39 +5039,42 @@ unlock:
    return 0;
    }

    -/* Save and disable devices from the top of the tree down */
    -static void pci_bus_save_and_disable(struct pci_bus *bus)
    +/*
    + * Save and disable devices from the top of the tree down while holding
    + * the @dev mutex lock for the entire tree.
    + */
    +static void pci_bus_save_and_disable_locked(struct pci_bus *bus)
    {
    struct pci_dev *dev;

    list_for_each_entry(dev, &bus->devices, bus_list) {
    - pci_dev_lock(dev);
    pci_dev_save_and_disable(dev);
    - pci_dev_unlock(dev);
    if (dev->subordinate)
    - pci_bus_save_and_disable(dev->subordinate);
    + pci_bus_save_and_disable_locked(dev->subordinate);
    }
    }

    /*
    - * Restore devices from top of the tree down - parent bridges need to be
    - * restored before we can get to subordinate devices.
    + * Restore devices from top of the tree down while holding @dev mutex lock
    + * for the entire tree. Parent bridges need to be restored before we can
    + * get to subordinate devices.
    */
    -static void pci_bus_restore(struct pci_bus *bus)
    +static void pci_bus_restore_locked(struct pci_bus *bus)
    {
    struct pci_dev *dev;

    list_for_each_entry(dev, &bus->devices, bus_list) {
    - pci_dev_lock(dev);
    pci_dev_restore(dev);
    - pci_dev_unlock(dev);
    if (dev->subordinate)
    - pci_bus_restore(dev->subordinate);
    + pci_bus_restore_locked(dev->subordinate);
    }
    }

    -/* Save and disable devices from the top of the tree down */
    -static void pci_slot_save_and_disable(struct pci_slot *slot)
    +/*
    + * Save and disable devices from the top of the tree down while holding
    + * the @dev mutex lock for the entire tree.
    + */
    +static void pci_slot_save_and_disable_locked(struct pci_slot *slot)
    {
    struct pci_dev *dev;

    @@ -5080,26 +5083,25 @@ static void pci_slot_save_and_disable(struct pci_slot *slot)
    continue;
    pci_dev_save_and_disable(dev);
    if (dev->subordinate)
    - pci_bus_save_and_disable(dev->subordinate);
    + pci_bus_save_and_disable_locked(dev->subordinate);
    }
    }

    /*
    - * Restore devices from top of the tree down - parent bridges need to be
    - * restored before we can get to subordinate devices.
    + * Restore devices from top of the tree down while holding @dev mutex lock
    + * for the entire tree. Parent bridges need to be restored before we can
    + * get to subordinate devices.
    */
    -static void pci_slot_restore(struct pci_slot *slot)
    +static void pci_slot_restore_locked(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;
    - pci_dev_lock(dev);
    pci_dev_restore(dev);
    - pci_dev_unlock(dev);
    if (dev->subordinate)
    - pci_bus_restore(dev->subordinate);
    + pci_bus_restore_locked(dev->subordinate);
    }
    }

    @@ -5158,17 +5160,15 @@ static int __pci_reset_slot(struct pci_slot *slot)
    if (rc)
    return rc;

    - pci_slot_save_and_disable(slot);
    -
    if (pci_slot_trylock(slot)) {
    + pci_slot_save_and_disable_locked(slot);
    might_sleep();
    rc = pci_reset_hotplug_slot(slot->hotplug, 0);
    + pci_slot_restore_locked(slot);
    pci_slot_unlock(slot);
    } else
    rc = -EAGAIN;

    - pci_slot_restore(slot);
    -
    return rc;
    }

    @@ -5254,17 +5254,15 @@ static int __pci_reset_bus(struct pci_bus *bus)
    if (rc)
    return rc;

    - pci_bus_save_and_disable(bus);
    -
    if (pci_bus_trylock(bus)) {
    + pci_bus_save_and_disable_locked(bus);
    might_sleep();
    rc = pci_bridge_secondary_bus_reset(bus->self);
    + pci_bus_restore_locked(bus);
    pci_bus_unlock(bus);
    } else
    rc = -EAGAIN;

    - pci_bus_restore(bus);
    -
    return rc;
    }

    --
    2.20.1


    \
     
     \ /
      Last update: 2020-01-24 12:14    [W:2.131 / U:0.216 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site