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
