diff options
| -rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 3 | ||||
| -rw-r--r-- | drivers/pci/pci-sysfs.c | 3 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 218 | ||||
| -rw-r--r-- | drivers/pci/pci.h | 2 | ||||
| -rw-r--r-- | drivers/pci/slot.c | 31 | ||||
| -rw-r--r-- | include/linux/pci.h | 10 |
6 files changed, 153 insertions, 114 deletions
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 1e9158d7bac7..2cafd3b26f34 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -79,7 +79,8 @@ static int init_slot(struct controller *ctrl) snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); retval = pci_hp_initialize(&ctrl->hotplug_slot, - ctrl->pcie->port->subordinate, 0, name); + ctrl->pcie->port->subordinate, + PCI_SLOT_ALL_DEVICES, name); if (retval) { ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval); kfree(ops); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 16eaaf749ba9..a2f8a5d6190f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -553,7 +553,6 @@ static ssize_t reset_subordinate_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - struct pci_bus *bus = pdev->subordinate; unsigned long val; if (!capable(CAP_SYS_ADMIN)) @@ -563,7 +562,7 @@ static ssize_t reset_subordinate_store(struct device *dev, return -EINVAL; if (val) { - int ret = __pci_reset_bus(bus); + int ret = pci_try_reset_bridge(pdev); if (ret) return ret; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b53488987f2c..09807a65ef87 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4911,12 +4911,8 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe) * If "dev" is below a CXL port that has SBR control masked, SBR * won't do anything, so return error. */ - if (bridge && cxl_sbr_masked(bridge)) { - if (probe) - return 0; - + if (bridge && pcie_is_cxl(bridge) && cxl_sbr_masked(bridge)) return -ENOTTY; - } rc = pci_dev_reset_iommu_prepare(dev); if (rc) { @@ -5289,13 +5285,21 @@ static bool pci_bus_resettable(struct pci_bus *bus) return true; } +static void pci_bus_lock(struct pci_bus *bus); +static void pci_bus_unlock(struct pci_bus *bus); +static int pci_bus_trylock(struct pci_bus *bus); + /* Lock devices from the top of the tree down */ -static void pci_bus_lock(struct pci_bus *bus) +static void __pci_bus_lock(struct pci_bus *bus, struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = bus->self; + + if (bridge) + pci_dev_lock(bridge); - pci_dev_lock(bus->self); list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_lock(dev->subordinate); else @@ -5304,28 +5308,34 @@ static void pci_bus_lock(struct pci_bus *bus) } /* Unlock devices from the bottom of the tree up */ -static void pci_bus_unlock(struct pci_bus *bus) +static void __pci_bus_unlock(struct pci_bus *bus, struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = bus->self; list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_unlock(dev->subordinate); else pci_dev_unlock(dev); } - pci_dev_unlock(bus->self); + + if (bridge) + pci_dev_unlock(bridge); } /* Return 1 on successful lock, 0 on contention */ -static int pci_bus_trylock(struct pci_bus *bus) +static int __pci_bus_trylock(struct pci_bus *bus, struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = bus->self; - if (!pci_dev_trylock(bus->self)) + if (bridge && !pci_dev_trylock(bridge)) return 0; list_for_each_entry(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) { if (!pci_bus_trylock(dev->subordinate)) goto unlock; @@ -5336,15 +5346,37 @@ static int pci_bus_trylock(struct pci_bus *bus) unlock: list_for_each_entry_continue_reverse(dev, &bus->devices, bus_list) { + if (slot && (!dev->slot || dev->slot != slot)) + continue; if (dev->subordinate) pci_bus_unlock(dev->subordinate); else pci_dev_unlock(dev); } - pci_dev_unlock(bus->self); + + if (bridge) + pci_dev_unlock(bridge); return 0; } +/* Lock devices from the top of the tree down */ +static void pci_bus_lock(struct pci_bus *bus) +{ + __pci_bus_lock(bus, NULL); +} + +/* Unlock devices from the bottom of the tree up */ +static void pci_bus_unlock(struct pci_bus *bus) +{ + __pci_bus_unlock(bus, NULL); +} + +/* Return 1 on successful lock, 0 on contention */ +static int pci_bus_trylock(struct pci_bus *bus) +{ + return __pci_bus_trylock(bus, NULL); +} + /* Do any devices on or below this slot prevent a bus reset? */ static bool pci_slot_resettable(struct pci_slot *slot) { @@ -5367,72 +5399,19 @@ static bool pci_slot_resettable(struct pci_slot *slot) /* Lock devices from the top of the tree down */ static void pci_slot_lock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - if (bridge) - pci_dev_lock(bridge); - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_lock(dev->subordinate); - else - pci_dev_lock(dev); - } + __pci_bus_lock(slot->bus, slot); } /* Unlock devices from the bottom of the tree up */ static void pci_slot_unlock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_unlock(dev->subordinate); - else - pci_dev_unlock(dev); - } - - if (bridge) - pci_dev_unlock(bridge); + __pci_bus_unlock(slot->bus, slot); } /* Return 1 on successful lock, 0 on contention */ static int pci_slot_trylock(struct pci_slot *slot) { - struct pci_dev *dev, *bridge = slot->bus->self; - - if (bridge && !pci_dev_trylock(bridge)) - return 0; - - list_for_each_entry(dev, &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) { - if (!pci_bus_trylock(dev->subordinate)) - goto unlock; - } else if (!pci_dev_trylock(dev)) - goto unlock; - } - return 1; - -unlock: - list_for_each_entry_continue_reverse(dev, - &slot->bus->devices, bus_list) { - if (!dev->slot || dev->slot != slot) - continue; - if (dev->subordinate) - pci_bus_unlock(dev->subordinate); - else - pci_dev_unlock(dev); - } - - if (bridge) - pci_dev_unlock(bridge); - return 0; + return __pci_bus_trylock(slot->bus, slot); } /* @@ -5538,7 +5517,7 @@ int pci_probe_reset_slot(struct pci_slot *slot) EXPORT_SYMBOL_GPL(pci_probe_reset_slot); /** - * __pci_reset_slot - Try to reset a PCI slot + * pci_try_reset_slot - Try to reset a PCI slot * @slot: PCI slot to reset * * A PCI bus may host multiple slots, each slot may support a reset mechanism @@ -5552,7 +5531,7 @@ EXPORT_SYMBOL_GPL(pci_probe_reset_slot); * * Same as above except return -EAGAIN if the slot cannot be locked */ -static int __pci_reset_slot(struct pci_slot *slot) +static int pci_try_reset_slot(struct pci_slot *slot) { int rc; @@ -5594,14 +5573,44 @@ static int pci_bus_reset(struct pci_bus *bus, bool probe) } /** - * pci_bus_error_reset - reset the bridge's subordinate bus - * @bridge: The parent device that connects to the bus to reset + * pci_try_reset_bus - Try to reset a PCI bus + * @bus: top level PCI bus to reset + * + * Same as above except return -EAGAIN if the bus cannot be locked + */ +static int pci_try_reset_bus(struct pci_bus *bus) +{ + int rc; + + rc = pci_bus_reset(bus, PCI_RESET_PROBE); + if (rc) + return rc; + + 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; + + return rc; +} + +#define PCI_RESET_RESTORE true +#define PCI_RESET_NO_RESTORE false +/** + * pci_reset_bridge - reset a bridge's subordinate bus + * @bridge: bridge that connects to the bus to reset + * @restore: when true use a reset method that invokes pci_dev_restore() post + * reset for affected devices * * This function will first try to reset the slots on this bus if the method is * available. If slot reset fails or is not available, this will fall back to a * secondary bus reset. */ -int pci_bus_error_reset(struct pci_dev *bridge) +static int pci_reset_bridge(struct pci_dev *bridge, bool restore) { struct pci_bus *bus = bridge->subordinate; struct pci_slot *slot; @@ -5617,18 +5626,43 @@ int pci_bus_error_reset(struct pci_dev *bridge) if (pci_probe_reset_slot(slot)) goto bus_reset; - list_for_each_entry(slot, &bus->slots, list) - if (pci_slot_reset(slot, PCI_RESET_DO_RESET)) + list_for_each_entry(slot, &bus->slots, list) { + int ret; + + if (restore) + ret = pci_try_reset_slot(slot); + else + ret = pci_slot_reset(slot, PCI_RESET_DO_RESET); + + if (ret) goto bus_reset; + } mutex_unlock(&pci_slot_mutex); return 0; bus_reset: mutex_unlock(&pci_slot_mutex); + + if (restore) + return pci_try_reset_bus(bus); return pci_bus_reset(bridge->subordinate, PCI_RESET_DO_RESET); } /** + * pci_bus_error_reset - reset the bridge's subordinate bus + * @bridge: The parent device that connects to the bus to reset + */ +int pci_bus_error_reset(struct pci_dev *bridge) +{ + return pci_reset_bridge(bridge, PCI_RESET_NO_RESTORE); +} + +int pci_try_reset_bridge(struct pci_dev *bridge) +{ + return pci_reset_bridge(bridge, PCI_RESET_RESTORE); +} + +/** * pci_probe_reset_bus - probe whether a PCI bus can be reset * @bus: PCI bus to probe * @@ -5641,32 +5675,6 @@ int pci_probe_reset_bus(struct pci_bus *bus) EXPORT_SYMBOL_GPL(pci_probe_reset_bus); /** - * __pci_reset_bus - Try to reset a PCI bus - * @bus: top level PCI bus to reset - * - * Same as above except return -EAGAIN if the bus cannot be locked - */ -int __pci_reset_bus(struct pci_bus *bus) -{ - int rc; - - rc = pci_bus_reset(bus, PCI_RESET_PROBE); - if (rc) - return rc; - - 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; - - return rc; -} - -/** * pci_reset_bus - Try to reset a PCI bus * @pdev: top level PCI device to reset via slot/bus * @@ -5675,7 +5683,7 @@ int __pci_reset_bus(struct pci_bus *bus) int pci_reset_bus(struct pci_dev *pdev) { return (!pci_probe_reset_slot(pdev->slot)) ? - __pci_reset_slot(pdev->slot) : __pci_reset_bus(pdev->bus); + pci_try_reset_slot(pdev->slot) : pci_try_reset_bus(pdev->bus); } EXPORT_SYMBOL_GPL(pci_reset_bus); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 13d998fbacce..a1d2ecb56207 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -231,7 +231,7 @@ bool pci_reset_supported(struct pci_dev *dev); void pci_init_reset_methods(struct pci_dev *dev); int pci_bridge_secondary_bus_reset(struct pci_dev *dev); int pci_bus_error_reset(struct pci_dev *dev); -int __pci_reset_bus(struct pci_bus *bus); +int pci_try_reset_bridge(struct pci_dev *bridge); struct pci_cap_saved_data { u16 cap_nr; diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 787311614e5b..e0b7fb43423c 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -42,6 +42,15 @@ static ssize_t address_read_file(struct pci_slot *slot, char *buf) pci_domain_nr(slot->bus), slot->bus->number); + /* + * Preserve legacy ABI expectations that hotplug drivers that manage + * multiple devices per slot emit 0 for the device number. + */ + if (slot->number == PCI_SLOT_ALL_DEVICES) + return sysfs_emit(buf, "%04x:%02x:00\n", + pci_domain_nr(slot->bus), + slot->bus->number); + return sysfs_emit(buf, "%04x:%02x:%02x\n", pci_domain_nr(slot->bus), slot->bus->number, @@ -73,7 +82,8 @@ static void pci_slot_release(struct kobject *kobj) down_read(&pci_bus_sem); list_for_each_entry(dev, &slot->bus->devices, bus_list) - if (PCI_SLOT(dev->devfn) == slot->number) + if (slot->number == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot->number) dev->slot = NULL; up_read(&pci_bus_sem); @@ -166,7 +176,8 @@ void pci_dev_assign_slot(struct pci_dev *dev) mutex_lock(&pci_slot_mutex); list_for_each_entry(slot, &dev->bus->slots, list) - if (PCI_SLOT(dev->devfn) == slot->number) + if (slot->number == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot->number) dev->slot = slot; mutex_unlock(&pci_slot_mutex); } @@ -188,7 +199,8 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) /** * pci_create_slot - create or increment refcount for physical PCI slot * @parent: struct pci_bus of parent bridge - * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder + * @slot_nr: PCI_SLOT(pci_dev->devfn), -1 for placeholder, or + * PCI_SLOT_ALL_DEVICES * @name: user visible string presented in /sys/bus/pci/slots/<name> * @hotplug: set if caller is hotplug driver, NULL otherwise * @@ -222,6 +234,16 @@ static struct pci_slot *get_slot(struct pci_bus *parent, int slot_nr) * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the * %struct pci_bus and bb is the bus number. In other words, the devfn of * the 'placeholder' slot will not be displayed. + * + * Bus-wide slots: + * For PCIe hotplug, the physical slot encompasses the entire secondary + * bus, not just a single device number. If the device supports ARI and ARI + * Forwarding is enabled in the upstream bridge, a multi-function device + * may include functions that appear to have several different device + * numbers, i.e., PCI_SLOT() values. Pass @slot_nr == PCI_SLOT_ALL_DEVICES + * to create a slot that matches all devices on the bus. Unlike placeholder + * slots, bus-wide slots go through normal slot lookup and reuse existing + * slots if present. */ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, const char *name, @@ -285,7 +307,8 @@ placeholder: down_read(&pci_bus_sem); list_for_each_entry(dev, &parent->devices, bus_list) - if (PCI_SLOT(dev->devfn) == slot_nr) + if (slot_nr == PCI_SLOT_ALL_DEVICES || + PCI_SLOT(dev->devfn) == slot_nr) dev->slot = slot; up_read(&pci_bus_sem); diff --git a/include/linux/pci.h b/include/linux/pci.h index 47c5b0c09ffb..cc98713e3ce8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -72,12 +72,20 @@ /* return bus from PCI devid = ((u16)bus_number) << 8) | devfn */ #define PCI_BUS_NUM(x) (((x) >> 8) & 0xff) +/* + * PCI_SLOT_ALL_DEVICES indicates a slot that covers all devices on the bus. + * Used for PCIe hotplug where the physical slot is the entire secondary bus, + * and, if ARI Forwarding is enabled, functions may appear to be on multiple + * devices. + */ +#define PCI_SLOT_ALL_DEVICES 0xfe + /* pci_slot represents a physical slot */ struct pci_slot { struct pci_bus *bus; /* Bus this slot is on */ struct list_head list; /* Node in list of slots */ struct hotplug_slot *hotplug; /* Hotplug info (move here) */ - unsigned char number; /* PCI_SLOT(pci_dev->devfn) */ + unsigned char number; /* Device nr, or PCI_SLOT_ALL_DEVICES */ struct kobject kobj; }; |
