aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-09-07 18:07:28 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-09-09 15:41:07 -0400
commita47d8c8e72a5fa2e69117674c4b0b6cc79c5bc53 (patch)
tree8c715da8750ff4479082270c7d4578e2027af972 /drivers/pci
parente532e84ea11399a6066f31641425a76dd012ce77 (diff)
ACPI / hotplug / PCI: Avoid parent bus rescans on spurious device checks
In the current ACPIPHP notify handler we always go directly for a rescan of the parent bus if we get a device check notification for a device that is not a bridge. However, this obviously is overzealous if nothing really changes, because this way we may rescan the whole PCI hierarchy pretty much in vain. That happens on Alex Williamson's machine whose ACPI tables contain device objects that are supposed to coresspond to PCIe root ports, but those ports aren't physically present (or at least they aren't visible in the PCI config space to us). The BIOS generates multiple device check notifies for those objects during boot and for each of them we go straight for the parent bus rescan, but the parent bus is the root bus in this particular case. In consequence, we rescan the whole PCI bus from the top several times in a row, which is completely unnecessary, increases boot time by 50% (after previous fixes) and generates excess dmesg output from the PCI subsystem. Fix the problem by checking if we can find anything new in the slot corresponding to the device we've got a device check notify for and doing nothig if that's not the case. The spec (ACPI 5.0, Section 5.6.6) appears to mandate this behavior, as it says: Device Check. Used to notify OSPM that the device either appeared or disappeared. If the device has appeared, OSPM will re-enumerate from the parent. If the device has disappeared, OSPM will invalidate the state of the device. OSPM may optimize out re-enumeration. Therefore, according to the spec, we are free to do nothing if nothing changes. References: https://bugzilla.kernel.org/show_bug.cgi?id=60865 Reported-and-tested-by: Alex Williamson <alex.williamson@redhat.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c32
1 files changed, 25 insertions, 7 deletions
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 1971d2943de4..9d6e535e74a1 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -528,6 +528,16 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
528 } 528 }
529} 529}
530 530
531static int acpiphp_rescan_slot(struct acpiphp_slot *slot)
532{
533 struct acpiphp_func *func;
534
535 list_for_each_entry(func, &slot->funcs, sibling)
536 acpiphp_bus_add(func_to_handle(func));
537
538 return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0));
539}
540
531/** 541/**
532 * enable_slot - enable, configure a slot 542 * enable_slot - enable, configure a slot
533 * @slot: slot to be enabled 543 * @slot: slot to be enabled
@@ -544,10 +554,7 @@ static void __ref enable_slot(struct acpiphp_slot *slot)
544 LIST_HEAD(add_list); 554 LIST_HEAD(add_list);
545 int nr_found; 555 int nr_found;
546 556
547 list_for_each_entry(func, &slot->funcs, sibling) 557 nr_found = acpiphp_rescan_slot(slot);
548 acpiphp_bus_add(func_to_handle(func));
549
550 nr_found = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
551 max = acpiphp_max_busnr(bus); 558 max = acpiphp_max_busnr(bus);
552 for (pass = 0; pass < 2; pass++) { 559 for (pass = 0; pass < 2; pass++) {
553 list_for_each_entry(dev, &bus->devices, bus_list) { 560 list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -840,11 +847,22 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data)
840 case ACPI_NOTIFY_DEVICE_CHECK: 847 case ACPI_NOTIFY_DEVICE_CHECK:
841 /* device check */ 848 /* device check */
842 dbg("%s: Device check notify on %s\n", __func__, objname); 849 dbg("%s: Device check notify on %s\n", __func__, objname);
843 if (bridge) 850 if (bridge) {
844 acpiphp_check_bridge(bridge); 851 acpiphp_check_bridge(bridge);
845 else 852 } else {
846 acpiphp_check_bridge(func->parent); 853 struct acpiphp_slot *slot = func->slot;
854 int ret;
847 855
856 /*
857 * Check if anything has changed in the slot and rescan
858 * from the parent if that's the case.
859 */
860 mutex_lock(&slot->crit_sect);
861 ret = acpiphp_rescan_slot(slot);
862 mutex_unlock(&slot->crit_sect);
863 if (ret)
864 acpiphp_check_bridge(func->parent);
865 }
848 break; 866 break;
849 867
850 case ACPI_NOTIFY_EJECT_REQUEST: 868 case ACPI_NOTIFY_EJECT_REQUEST: