The "bus" reset method resets a device by asserting Secondary Bus Reset
on its upstream bridge, so it is not available for a device on a root
bus, which has no upstream bridge.

Some platforms have no Root Ports and expose PCI devices directly on a
root bus, e.g., Hyper-V vPCI places each assigned PCI function directly
on the root bus of a virtual host bridge. Such a device has no reset
method at all if it supports neither FLR nor PM reset, even if the
platform is able to reset it.

pci_host_bridge::reset_root_port() already allows a host bridge to reset
a device on a root bus in a platform-specific way, so far only in place
of a Secondary Bus Reset by a Root Port. Use it for the "bus" reset
method of a device that is the only device on a root bus, and wait for
the device to become ready afterwards, as is done for other reset
methods.

Signed-off-by: Hamza Mahfooz <[email protected]>
---
 drivers/pci/pci.c | 32 ++++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 8cc6a89130b5..fd4e958d6b99 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -4924,18 +4924,46 @@ int pci_bridge_secondary_bus_reset(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_bridge_secondary_bus_reset);
 
+static int pci_root_bus_reset(struct pci_dev *dev, bool probe)
+{
+       struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
+       int rc;
+
+       /*
+        * Bridges, e.g., Root Ports, are not eligible.  Check the header type
+        * because dev->subordinate is not set up yet when the reset methods
+        * are probed during enumeration.
+        */
+       if (pci_is_bridge(dev) || !host->reset_root_port)
+               return -ENOTTY;
+
+       if (probe)
+               return 0;
+
+       rc = host->reset_root_port(host, dev);
+       if (rc)
+               return rc;
+
+       return pci_dev_wait(dev, "bus reset", PCIE_RESET_READY_POLL_MS);
+}
+
 static int pci_parent_bus_reset(struct pci_dev *dev, bool probe)
 {
        struct pci_dev *pdev;
 
-       if (pci_is_root_bus(dev->bus) || dev->subordinate ||
-           !dev->bus->self || dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)
+       if (dev->subordinate || dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)
                return -ENOTTY;
 
        list_for_each_entry(pdev, &dev->bus->devices, bus_list)
                if (pdev != dev)
                        return -ENOTTY;
 
+       if (pci_is_root_bus(dev->bus))
+               return pci_root_bus_reset(dev, probe);
+
+       if (!dev->bus->self)
+               return -ENOTTY;
+
        if (probe)
                return 0;
 
-- 
2.56.0


Reply via email to