Skip to content

Commit 84c8b58

Browse files
westeribjorn-helgaas
authored andcommitted
ACPI / hotplug / PCI: Don't scan bridges managed by native hotplug
When acpiphp re-enumerates a PCI hierarchy because of an ACPI Notify() event, we should skip bridges managed by native hotplug (pciehp or shpchp). We don't want to scan below a native hotplug bridge until the hotplug controller generates a hot-add event. A typical scenario is a Root Port leading to a Thunderbolt host router that remains powered off until something is connected to it. See [1] for the lspci details. 1. Before something is connected, only the Root Port exists. It has PCI_EXP_SLTCAP_HPC set and pciehp is responsible for hotplug: 00:1b.0 Root Port (HotPlug+) 2. When a USB-C or Thunderbolt device is connected, the Switch in the Thunderbolt host router is powered up, the Root Port signals a hotplug add event and pciehp enumerates the Switch: 01:00.0 Switch Upstream Port to [bus 02-39] 02:00.0 Switch Downstream Port to [bus 03] (HotPlug-, to NHI) 02:01.0 Switch Downstream Port to [bus 04-38] (HotPlug+, to Thunderbolt connector) 02:02.0 Switch Downstream Port to [bus 39] (HotPlug-, to xHCI) The 02:00.0 and 02:02.0 Ports lead to Endpoints that are not powered up yet. The Ports have PCI_EXP_SLTCAP_HPC cleared, so pciehp doesn't handle hotplug for them and we assign minimal resources to them. The 02:01.0 Port has PCI_EXP_SLTCAP_HPC set, so pciehp handles native hotplug events for it. 3. The BIOS powers up the xHCI controller. If a Thunderbolt device was connected (not just a USB-C device), it also powers up the NHI. Then it sends an ACPI Notify() to the Root Port, and acpiphp enumerates the new device(s): 03:00.0 Thunderbolt Host Controller (NHI) Endpoint 39:00.0 xHCI Endpoint 4. If a Thunderbolt device was connected, the host router firmware uses the NHI to set up Thunderbolt tunnels and triggers a native hotplug event (via 02:01.0 in this example). Then pciehp enumerates the new Thunderbolt devices: 04:00.0 Switch Upstream Port to [bus 05-38] 05:01.0 Switch Downstream Port to [bus 06-09] (HotPlug-) 05:04.0 Switch Downstream Port to [bus 0a-38] (HotPlug+) In this example, 05:01.0 leads to another Switch and some NICs. This subtree is static, so 05:01.0 doesn't support hotplug and has PCI_EXP_SLTCAP_HPC cleared. In step 3, acpiphp previously enumerated everything below the Root Port, including things below the 02:01.0 Port. We don't want that because pciehp expects to manage hotplug below that Port, and firmware on the host router may be in the middle of configuring its Link so it may not be ready yet. To make this work better with the native PCIe (pciehp) and standard PCI (shpchp) hotplug drivers, we let them handle all slot management and resource allocation for hotplug bridges and restrict ACPI hotplug to non-hotplug bridges. [1] https://bugzilla.kernel.org/show_bug.cgi?id=199581#c5 Link: https://lkml.kernel.org/r/20180529160155.1738-1-mika.westerberg@linux.intel.com Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> [bhelgaas: changelog, use hotplug_is_native() instead of dev->is_hotplug_bridge] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
1 parent 95d969e commit 84c8b58

File tree

1 file changed

+58
-17
lines changed

1 file changed

+58
-17
lines changed

drivers/pci/hotplug/acpiphp_glue.c

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,12 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
287287
/*
288288
* Expose slots to user space for functions that have _EJ0 or _RMV or
289289
* are located in dock stations. Do not expose them for devices handled
290-
* by the native PCIe hotplug (PCIeHP), becuase that code is supposed to
291-
* expose slots to user space in those cases.
290+
* by the native PCIe hotplug (PCIeHP) or standard PCI hotplug
291+
* (SHPCHP), because that code is supposed to expose slots to user
292+
* space in those cases.
292293
*/
293294
if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev))
294-
&& !(pdev && pdev->is_hotplug_bridge && pciehp_is_native(pdev))) {
295+
&& !(pdev && hotplug_is_native(pdev))) {
295296
unsigned long long sun;
296297
int retval;
297298

@@ -430,6 +431,29 @@ static int acpiphp_rescan_slot(struct acpiphp_slot *slot)
430431
return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0));
431432
}
432433

434+
static void acpiphp_native_scan_bridge(struct pci_dev *bridge)
435+
{
436+
struct pci_bus *bus = bridge->subordinate;
437+
struct pci_dev *dev;
438+
int max;
439+
440+
if (!bus)
441+
return;
442+
443+
max = bus->busn_res.start;
444+
/* Scan already configured non-hotplug bridges */
445+
for_each_pci_bridge(dev, bus) {
446+
if (!hotplug_is_native(dev))
447+
max = pci_scan_bridge(bus, dev, max, 0);
448+
}
449+
450+
/* Scan non-hotplug bridges that need to be reconfigured */
451+
for_each_pci_bridge(dev, bus) {
452+
if (!hotplug_is_native(dev))
453+
max = pci_scan_bridge(bus, dev, max, 1);
454+
}
455+
}
456+
433457
/**
434458
* enable_slot - enable, configure a slot
435459
* @slot: slot to be enabled
@@ -442,25 +466,42 @@ static void enable_slot(struct acpiphp_slot *slot)
442466
struct pci_dev *dev;
443467
struct pci_bus *bus = slot->bus;
444468
struct acpiphp_func *func;
445-
int max, pass;
446-
LIST_HEAD(add_list);
447469

448-
acpiphp_rescan_slot(slot);
449-
max = acpiphp_max_busnr(bus);
450-
for (pass = 0; pass < 2; pass++) {
470+
if (bus->self && hotplug_is_native(bus->self)) {
471+
/*
472+
* If native hotplug is used, it will take care of hotplug
473+
* slot management and resource allocation for hotplug
474+
* bridges. However, ACPI hotplug may still be used for
475+
* non-hotplug bridges to bring in additional devices such
476+
* as a Thunderbolt host controller.
477+
*/
451478
for_each_pci_bridge(dev, bus) {
452-
if (PCI_SLOT(dev->devfn) != slot->device)
453-
continue;
454-
455-
max = pci_scan_bridge(bus, dev, max, pass);
456-
if (pass && dev->subordinate) {
457-
check_hotplug_bridge(slot, dev);
458-
pcibios_resource_survey_bus(dev->subordinate);
459-
__pci_bus_size_bridges(dev->subordinate, &add_list);
479+
if (PCI_SLOT(dev->devfn) == slot->device)
480+
acpiphp_native_scan_bridge(dev);
481+
}
482+
pci_assign_unassigned_bridge_resources(bus->self);
483+
} else {
484+
LIST_HEAD(add_list);
485+
int max, pass;
486+
487+
acpiphp_rescan_slot(slot);
488+
max = acpiphp_max_busnr(bus);
489+
for (pass = 0; pass < 2; pass++) {
490+
for_each_pci_bridge(dev, bus) {
491+
if (PCI_SLOT(dev->devfn) != slot->device)
492+
continue;
493+
494+
max = pci_scan_bridge(bus, dev, max, pass);
495+
if (pass && dev->subordinate) {
496+
check_hotplug_bridge(slot, dev);
497+
pcibios_resource_survey_bus(dev->subordinate);
498+
__pci_bus_size_bridges(dev->subordinate,
499+
&add_list);
500+
}
460501
}
461502
}
503+
__pci_bus_assign_resources(bus, &add_list, NULL);
462504
}
463-
__pci_bus_assign_resources(bus, &add_list, NULL);
464505

465506
acpiphp_sanitize_bus(bus);
466507
pcie_bus_configure_settings(bus);

0 commit comments

Comments
 (0)