From 1e89d268e7a83d8388d4d09d3f2089ced72cc15f Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:15 +0000 Subject: PCI: When removing bus, always remove legacy files & unregister We always call device_register() and pci_create_legacy_files() for a new bus before handing out the "struct pci_bus *". Therefore, there's no possiblity of removing the bus with pci_remove_bus() before those calls have been made, so we don't need to check "bus->is_added" before calling pci_remove_legacy_files() and device_unregister(). [bhelgaas: changelog] Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani --- drivers/pci/remove.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index cc875e6ed15..a8318801c09 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -50,9 +50,6 @@ void pci_remove_bus(struct pci_bus *bus) list_del(&bus->node); pci_bus_release_busn_res(bus); up_write(&pci_bus_sem); - if (!bus->is_added) - return; - pci_remove_legacy_files(bus); device_unregister(&bus->dev); } -- cgit v1.2.3 From 981cf9ea9a6a6c422e51fc04bedad00b7792ccbc Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:16 +0000 Subject: PCI: Clean up usages of pci_bus->is_added Now pci_bus->is_added is only used to guard invoking of pcibios_fixup_bus() in pci_scan_child_bus(), so just set it directly after the fixups and remove the other test and set in pci_bus_add_devices(). [bhelgaas: changelog] Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu --- drivers/pci/bus.c | 11 ++--------- drivers/pci/probe.c | 3 +-- 2 files changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 8647dc6f52d..bdc1e8bf7e6 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -206,16 +206,9 @@ void pci_bus_add_devices(const struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { BUG_ON(!dev->is_added); - child = dev->subordinate; - - if (!child) - continue; - pci_bus_add_devices(child); - - if (child->is_added) - continue; - child->is_added = 1; + if (child) + pci_bus_add_devices(child); } } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b494066ef32..45c93b39af3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1627,8 +1627,7 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) if (!bus->is_added) { dev_dbg(&bus->dev, "fixups for bus\n"); pcibios_fixup_bus(bus); - if (pci_is_root_bus(bus)) - bus->is_added = 1; + bus->is_added = 1; } for (pass=0; pass < 2; pass++) -- cgit v1.2.3 From d65eba6a7a769bb939303969267cbf1c916358f5 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Fri, 12 Apr 2013 05:44:17 +0000 Subject: PCI: acpiphp: Use list_for_each_entry_safe() in acpiphp_sanitize_bus() Function acpiphp_sanitize_bus() may call pci_stop_and_remove_bus_device(), which in turn may remove device from bus->devices list. So walk the bus->devices list with list_for_each_entry_safe(). Signed-off-by: Yijing Wang Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani --- drivers/pci/hotplug/acpiphp_glue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 270fdbadc19..7948bc91960 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -1082,11 +1082,11 @@ static void acpiphp_set_hpp_values(struct pci_bus *bus) */ static void acpiphp_sanitize_bus(struct pci_bus *bus) { - struct pci_dev *dev; + struct pci_dev *dev, *tmp; int i; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM; - list_for_each_entry(dev, &bus->devices, bus_list) { + list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { for (i=0; iresource[i]; if ((res->flags & type_mask) && !res->start && -- cgit v1.2.3 From 3a0e40beefc20852191ed65d53e1b82d95ac11b8 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:18 +0000 Subject: PCI: acpiphp: Remove all functions even if function 0 doesn't exist Currently function disable_device() detects slot state by checking existence of PCI function 0. It's unreliable because the PCI device for function 0 may be removed through the sysfs interface. If that happens, it will cause powering off a hotplug slot without destroying all PCI devices. On the other hand, it won't hurt us except wasting some computation power if the check is removed, because all code of disable_device() is self-protected. So remove the check. Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani --- drivers/pci/hotplug/acpiphp_glue.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 7948bc91960..a1c8dd67aae 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -914,13 +914,6 @@ static int disable_device(struct acpiphp_slot *slot) struct pci_dev *pdev; struct pci_bus *bus = slot->bridge->pci_bus; - /* The slot will be enabled when func 0 is added, so check - func 0 before disable the slot. */ - pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); - if (!pdev) - goto err_exit; - pci_dev_put(pdev); - list_for_each_entry(func, &slot->funcs, sibling) { if (func->bridge) { /* cleanup p2p bridges under this P2P bridge */ -- cgit v1.2.3 From ce15d873d05ebf3bf38579c4e0252140e28f1781 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:19 +0000 Subject: PCI: acpiphp: Replace local macros with standard ACPI macros Replace local defined macros (ACPI_STA_xxx) with standard ACPI macros (ACPI_STA_DEVICE_xxx). Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani --- drivers/pci/hotplug/acpiphp.h | 4 ---- drivers/pci/hotplug/acpiphp_glue.c | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index b70ac00a117..1b311f9db82 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -146,10 +146,6 @@ struct acpiphp_attention_info #define ACPI_PCI_HOST_HID "PNP0A03" /* ACPI _STA method value (ignore bit 4; battery present) */ -#define ACPI_STA_PRESENT (0x00000001) -#define ACPI_STA_ENABLED (0x00000002) -#define ACPI_STA_SHOW_IN_UI (0x00000004) -#define ACPI_STA_FUNCTIONING (0x00000008) #define ACPI_STA_ALL (0x0000000f) /* bridge flags */ diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index a1c8dd67aae..718464f8fd4 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -487,7 +487,7 @@ static int add_bridge(struct acpi_pci_root *root) dbg("%s: _STA evaluation failure\n", __func__); return 0; } - if ((tmp & ACPI_STA_FUNCTIONING) == 0) + if ((tmp & ACPI_STA_DEVICE_FUNCTIONING) == 0) /* don't register this object */ return 0; } @@ -1389,7 +1389,7 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot) sta = get_slot_status(slot); - return (sta & ACPI_STA_SHOW_IN_UI) ? 0 : 1; + return (sta & ACPI_STA_DEVICE_UI) ? 0 : 1; } -- cgit v1.2.3 From 10a9574756201fbbdd0cac11f370f00d3d02bfa1 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:20 +0000 Subject: PCI: Add pcibios hooks for adding and removing PCI buses On ACPI-based platforms, the pci_slot driver creates PCI slot devices according to information from ACPI tables by registering an ACPI PCI subdriver. The ACPI PCI subdriver will only be called when creating/ destroying PCI root buses, and it won't be called when hot-plugging P2P bridges. It may cause stale PCI slot devices after hot-removing a P2P bridge if that bridge has associated PCI slots. And the acpiphp driver has the same issue too. This patch introduces two hook points into the PCI core, which will be invoked when creating/destroying PCI buses for PCI host and P2P bridges. They could be used to setup/destroy platform dependent stuff in a unified way, both at boot time and for PCI hotplug operations. Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani Cc: Tony Luck Cc: Fenghua Yu Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Myron Stowe --- drivers/pci/probe.c | 12 ++++++++++++ drivers/pci/remove.c | 1 + 2 files changed, 13 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 45c93b39af3..43ece5d41d3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -673,6 +673,8 @@ add_dev: ret = device_register(&child->dev); WARN_ON(ret < 0); + pcibios_add_bus(child); + /* Create legacy_io and legacy_mem files for this bus */ pci_create_legacy_files(child); @@ -1660,6 +1662,14 @@ int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) return 0; } +void __weak pcibios_add_bus(struct pci_bus *bus) +{ +} + +void __weak pcibios_remove_bus(struct pci_bus *bus) +{ +} + struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { @@ -1714,6 +1724,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, if (error) goto class_dev_reg_err; + pcibios_add_bus(b); + /* Create legacy_io and legacy_mem files for this bus */ pci_create_legacy_files(b); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index a8318801c09..8fc54b7327b 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -51,6 +51,7 @@ void pci_remove_bus(struct pci_bus *bus) pci_bus_release_busn_res(bus); up_write(&pci_bus_sem); pci_remove_legacy_files(bus); + pcibios_remove_bus(bus); device_unregister(&bus->dev); } EXPORT_SYMBOL(pci_remove_bus); -- cgit v1.2.3 From 5090d4a6a1f150ec593ac5f2a755b5a3e878b21e Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:21 +0000 Subject: PCI/ACPI: Prepare stub functions to handle ACPI PCI (hotplug) slots Prepare two stub functions to handle ACPI PCI slots and ACPI PCI hotplug slots, which will be invoked by the PCI core when creating/destroying PCI buses. It will be used to get rid of ACPI PCI subdrivers for pci_slot and acpiphp, and eventually remove the ACPI PCI subdriver mechanism. And it will also be used to handle ACPI PCI (hotplug) slots in a unified way, both at boot time and for PCI hotplug operations. Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani Cc: Tony Luck Cc: Fenghua Yu Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Myron Stowe --- drivers/pci/pci-acpi.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index dee5dddaa29..b8b8437c703 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -287,6 +287,30 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = { .run_wake = acpi_pci_run_wake, }; +void acpi_pci_add_bus(struct pci_bus *bus) +{ + acpi_handle handle = NULL; + + if (bus->bridge) + handle = ACPI_HANDLE(bus->bridge); + if (acpi_pci_disabled || handle == NULL) + return; + + /* TODO: add PCI slots and acpiphp hotplug slots */ +} + +void acpi_pci_remove_bus(struct pci_bus *bus) +{ + /* + * bus->bridge->acpi_node.handle has already been reset to NULL + * when acpi_pci_remove_bus() is called, so don't check ACPI handle. + */ + if (acpi_pci_disabled) + return; + + /* TODO: remove PCI slots and acpiphp hotplug slots */ +} + /* ACPI bus type */ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) { -- cgit v1.2.3 From 5c0b04e3d913c91aee6e48e567e20a3f67849618 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:24 +0000 Subject: PCI/ACPI: Handle PCI slot devices when creating/destroying PCI buses Currently the pci_slot driver doesn't update PCI slot devices when PCI device hotplug event happens, which may cause memory leak and returning stale information to user. Now the pci_slot driver has been changed as built-in driver, so invoke PCI slot enumeration and destroy routines directly from the PCI core. And remove ACPI PCI sub-driver related code because it isn't needed any more. [bhelgas: removed "extern" from function declarations] Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Acked-by: Rafael J. Wysocki Cc: Toshi Kani --- drivers/pci/pci-acpi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index b8b8437c703..98e582a2c90 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -296,7 +296,7 @@ void acpi_pci_add_bus(struct pci_bus *bus) if (acpi_pci_disabled || handle == NULL) return; - /* TODO: add PCI slots and acpiphp hotplug slots */ + acpi_pci_slot_enumerate(bus, handle); } void acpi_pci_remove_bus(struct pci_bus *bus) @@ -308,7 +308,7 @@ void acpi_pci_remove_bus(struct pci_bus *bus) if (acpi_pci_disabled) return; - /* TODO: remove PCI slots and acpiphp hotplug slots */ + acpi_pci_slot_remove(bus); } /* ACPI bus type */ @@ -385,7 +385,10 @@ static int __init acpi_pci_init(void) ret = register_acpi_bus_type(&acpi_pci_bus); if (ret) return 0; + pci_set_platform_pm(&acpi_pci_platform_pm); + acpi_pci_slot_init(); + return 0; } arch_initcall(acpi_pci_init); -- cgit v1.2.3 From 6037a803b05eef9943fb64982e19964007fb7478 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:25 +0000 Subject: PCI: acpiphp: Convert acpiphp to be builtin only, not modular Convert acpiphp to be builtin only, with no module option. Previously, when HOTPLUG_PCI_ACPI=m, users could disable acpiphp by removing the module or preventing it from loading. That can't be done if acpiphp is builtin statically, so this adds an "acpiphp.disable" kernel parameter. If a user needs to use this parameter, it is a bug, and we want to hear about it. [bhelgaas: fold in acpiphp.disable here, remove documentation] Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/Kconfig | 7 ++----- drivers/pci/hotplug/acpiphp.h | 1 + drivers/pci/hotplug/acpiphp_core.c | 19 +++++++------------ 3 files changed, 10 insertions(+), 17 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 13e9e63a726..9fcb87f353d 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -52,15 +52,12 @@ config HOTPLUG_PCI_IBM When in doubt, say N. config HOTPLUG_PCI_ACPI - tristate "ACPI PCI Hotplug driver" - depends on (!ACPI_DOCK && ACPI) || (ACPI_DOCK) + bool "ACPI PCI Hotplug driver" + depends on HOTPLUG_PCI=y && ((!ACPI_DOCK && ACPI) || (ACPI_DOCK)) help Say Y here if you have a system that supports PCI Hotplug using ACPI. - To compile this driver as a module, choose M here: the - module will be called acpiphp. - When in doubt, say N. config HOTPLUG_PCI_ACPI_IBM diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 1b311f9db82..b06ae681d5b 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -190,5 +190,6 @@ extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot); /* variables */ extern bool acpiphp_debug; +extern bool acpiphp_disabled; #endif /* _ACPIPHP_H */ diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index c2fd3095701..81adbfa4df1 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -48,6 +48,7 @@ #define SLOT_NAME_SIZE 21 /* {_SUN} */ bool acpiphp_debug; +bool acpiphp_disabled; /* local variables */ static struct acpiphp_attention_info *attention_info; @@ -60,7 +61,9 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); +MODULE_PARM_DESC(disable, "disable acpiphp driver"); module_param_named(debug, acpiphp_debug, bool, 0644); +module_param_named(disable, acpiphp_disabled, bool, 0444); /* export the attention callback registration methods */ EXPORT_SYMBOL_GPL(acpiphp_register_attention); @@ -353,9 +356,11 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) static int __init acpiphp_init(void) { - info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n", + acpiphp_disabled ? ", disabled by user; please report a bug" + : ""); - if (acpi_pci_disabled) + if (acpi_pci_disabled || acpiphp_disabled) return 0; /* read all the ACPI info from the system */ @@ -364,14 +369,4 @@ static int __init acpiphp_init(void) } -static void __exit acpiphp_exit(void) -{ - if (acpi_pci_disabled) - return; - - /* deallocate internal data structures etc. */ - acpiphp_glue_exit(); -} - module_init(acpiphp_init); -module_exit(acpiphp_exit); -- cgit v1.2.3 From 3b63aaa70e1ccc4b66d60acc78da09700706a703 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:26 +0000 Subject: PCI: acpiphp: Do not use ACPI PCI subdriver mechanism Previously the acpiphp driver registered itself as an ACPI PCI subdriver, so its callbacks were invoked when creating/destroying PCI root buses to manage ACPI-based PCI hotplug slots. But it doesn't handle P2P bridge hotplug events, so it will cause strange behaviour if there are hotplug slots associated with a hot-removed P2P bridge. This patch fixes this issue by: 1) Directly hooking into PCI core to update hotplug slot devices when creating/destroying PCI buses through: pci_{add|remove}_bus() -> acpi_pci_{add|remove}_bus() 2) Getting rid of unused ACPI PCI subdriver-related code It also cleans up unused code in the acpiphp driver. [bhelgaas: keep acpi_pci_add_bus() stub for CONFIG_ACPI=n] Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani --- drivers/pci/hotplug/acpiphp.h | 3 - drivers/pci/hotplug/acpiphp_core.c | 13 +- drivers/pci/hotplug/acpiphp_glue.c | 288 +++++++------------------------------ drivers/pci/pci-acpi.c | 3 + 4 files changed, 60 insertions(+), 247 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index b06ae681d5b..abd48d309ad 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -119,7 +119,6 @@ struct acpiphp_slot { */ struct acpiphp_func { struct acpiphp_slot *slot; /* parent */ - struct acpiphp_bridge *bridge; /* Ejectable PCI-to-PCI bridge */ struct list_head sibling; struct notifier_block nb; @@ -176,8 +175,6 @@ extern int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot); extern void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot); /* acpiphp_glue.c */ -extern int acpiphp_glue_init (void); -extern void acpiphp_glue_exit (void); typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data); extern int acpiphp_enable_slot (struct acpiphp_slot *slot); diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index 81adbfa4df1..ca8127950fc 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -354,19 +355,9 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot) } -static int __init acpiphp_init(void) +void __init acpiphp_init(void) { info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n", acpiphp_disabled ? ", disabled by user; please report a bug" : ""); - - if (acpi_pci_disabled || acpiphp_disabled) - return 0; - - /* read all the ACPI info from the system */ - /* initialize internal data structure etc. */ - return acpiphp_glue_init(); } - - -module_init(acpiphp_init); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 718464f8fd4..20db4d6564a 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -157,6 +157,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) int device, function, retval; struct pci_bus *pbus = bridge->pci_bus; struct pci_dev *pdev; + u32 val; if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle)) return AE_OK; @@ -249,11 +250,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) newfunc->slot = slot; list_add_tail(&newfunc->sibling, &slot->funcs); - pdev = pci_get_slot(pbus, PCI_DEVFN(device, function)); - if (pdev) { + if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function), + &val, 60*1000)) slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); - pci_dev_put(pdev); - } if (is_dock_device(handle)) { /* we don't want to call this device's _EJ0 @@ -366,148 +365,6 @@ static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle } -static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge) -{ - acpi_handle dummy_handle; - struct acpiphp_func *func; - - if (ACPI_SUCCESS(acpi_get_handle(bridge->handle, - "_EJ0", &dummy_handle))) { - bridge->flags |= BRIDGE_HAS_EJ0; - - dbg("found ejectable p2p bridge\n"); - - /* make link between PCI bridge and PCI function */ - func = acpiphp_bridge_handle_to_function(bridge->handle); - if (!func) - return; - bridge->func = func; - func->bridge = bridge; - } -} - - -/* allocate and initialize host bridge data structure */ -static void add_host_bridge(struct acpi_pci_root *root) -{ - struct acpiphp_bridge *bridge; - acpi_handle handle = root->device->handle; - - bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); - if (bridge == NULL) - return; - - bridge->handle = handle; - - bridge->pci_bus = root->bus; - - init_bridge_misc(bridge); -} - - -/* allocate and initialize PCI-to-PCI bridge data structure */ -static void add_p2p_bridge(acpi_handle *handle) -{ - struct acpiphp_bridge *bridge; - - bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); - if (bridge == NULL) { - err("out of memory\n"); - return; - } - - bridge->handle = handle; - config_p2p_bridge_flags(bridge); - - bridge->pci_dev = acpi_get_pci_dev(handle); - bridge->pci_bus = bridge->pci_dev->subordinate; - if (!bridge->pci_bus) { - err("This is not a PCI-to-PCI bridge!\n"); - goto err; - } - - /* - * Grab a ref to the subordinate PCI bus in case the bus is - * removed via PCI core logical hotplug. The ref pins the bus - * (which we access during module unload). - */ - get_device(&bridge->pci_bus->dev); - - init_bridge_misc(bridge); - return; - err: - pci_dev_put(bridge->pci_dev); - kfree(bridge); - return; -} - - -/* callback routine to find P2P bridges */ -static acpi_status -find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - acpi_status status; - struct pci_dev *dev; - - dev = acpi_get_pci_dev(handle); - if (!dev || !dev->subordinate) - goto out; - - /* check if this bridge has ejectable slots */ - if ((detect_ejectable_slots(handle) > 0)) { - dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); - add_p2p_bridge(handle); - } - - /* search P2P bridges under this p2p bridge */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, NULL, NULL, NULL); - if (ACPI_FAILURE(status)) - warn("find_p2p_bridge failed (error code = 0x%x)\n", status); - - out: - pci_dev_put(dev); - return AE_OK; -} - - -/* find hot-pluggable slots, and then find P2P bridge */ -static int add_bridge(struct acpi_pci_root *root) -{ - acpi_status status; - unsigned long long tmp; - acpi_handle dummy_handle; - acpi_handle handle = root->device->handle; - - /* if the bridge doesn't have _STA, we assume it is always there */ - status = acpi_get_handle(handle, "_STA", &dummy_handle); - if (ACPI_SUCCESS(status)) { - status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp); - if (ACPI_FAILURE(status)) { - dbg("%s: _STA evaluation failure\n", __func__); - return 0; - } - if ((tmp & ACPI_STA_DEVICE_FUNCTIONING) == 0) - /* don't register this object */ - return 0; - } - - /* check if this bridge has ejectable slots */ - if (detect_ejectable_slots(handle) > 0) { - dbg("found PCI host-bus bridge with hot-pluggable slots\n"); - add_host_bridge(root); - } - - /* search P2P bridges under this host bridge */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, NULL, NULL, NULL); - - if (ACPI_FAILURE(status)) - warn("find_p2p_bridge failed (error code = 0x%x)\n", status); - - return 0; -} - static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) { struct acpiphp_bridge *bridge; @@ -567,56 +424,12 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) slot = next; } - /* - * Only P2P bridges have a pci_dev - */ - if (bridge->pci_dev) - put_device(&bridge->pci_bus->dev); - + put_device(&bridge->pci_bus->dev); pci_dev_put(bridge->pci_dev); list_del(&bridge->list); kfree(bridge); } -static acpi_status -cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - struct acpiphp_bridge *bridge; - - /* cleanup p2p bridges under this P2P bridge - in a depth-first manner */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - cleanup_p2p_bridge, NULL, NULL, NULL); - - bridge = acpiphp_handle_to_bridge(handle); - if (bridge) - cleanup_bridge(bridge); - - return AE_OK; -} - -static void remove_bridge(struct acpi_pci_root *root) -{ - struct acpiphp_bridge *bridge; - acpi_handle handle = root->device->handle; - - /* cleanup p2p bridges under this host bridge - in a depth-first manner */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, - (u32)1, cleanup_p2p_bridge, NULL, NULL, NULL); - - /* - * On root bridges with hotplug slots directly underneath (ie, - * no p2p bridge between), we call cleanup_bridge(). - * - * The else clause cleans up root bridges that either had no - * hotplug slots at all, or had a p2p bridge underneath. - */ - bridge = acpiphp_handle_to_bridge(handle); - if (bridge) - cleanup_bridge(bridge); -} - static int power_on_slot(struct acpiphp_slot *slot) { acpi_status status; @@ -798,6 +611,7 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev) } } } + /** * enable_device - enable, configure a slot * @slot: slot to be enabled @@ -812,7 +626,6 @@ static int __ref enable_device(struct acpiphp_slot *slot) struct acpiphp_func *func; int retval = 0; int num, max, pass; - acpi_status status; if (slot->flags & SLOT_ENABLED) goto err_exit; @@ -867,18 +680,6 @@ static int __ref enable_device(struct acpiphp_slot *slot) slot->flags &= (~SLOT_ENABLED); continue; } - - if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE && - dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) { - pci_dev_put(dev); - continue; - } - - status = find_p2p_bridge(func->handle, (u32)1, bus, NULL); - if (ACPI_FAILURE(status)) - warn("find_p2p_bridge failed (error code = 0x%x)\n", - status); - pci_dev_put(dev); } @@ -912,16 +713,6 @@ static int disable_device(struct acpiphp_slot *slot) { struct acpiphp_func *func; struct pci_dev *pdev; - struct pci_bus *bus = slot->bridge->pci_bus; - - list_for_each_entry(func, &slot->funcs, sibling) { - if (func->bridge) { - /* cleanup p2p bridges under this P2P bridge */ - cleanup_p2p_bridge(func->bridge->handle, - (u32)1, NULL, NULL); - func->bridge = NULL; - } - } /* * enable_device() enumerates all functions in this device via @@ -940,7 +731,6 @@ static int disable_device(struct acpiphp_slot *slot) slot->flags &= (~SLOT_ENABLED); -err_exit: return 0; } @@ -1287,30 +1077,62 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func); } -static struct acpi_pci_driver acpi_pci_hp_driver = { - .add = add_bridge, - .remove = remove_bridge, -}; - -/** - * acpiphp_glue_init - initializes all PCI hotplug - ACPI glue data structures +/* + * Create hotplug slots for the PCI bus. + * It should always return 0 to avoid skipping following notifiers. */ -int __init acpiphp_glue_init(void) +void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle) { - acpi_pci_register_driver(&acpi_pci_hp_driver); + acpi_handle dummy_handle; + struct acpiphp_bridge *bridge; - return 0; -} + if (acpiphp_disabled) + return; + if (detect_ejectable_slots(handle) <= 0) + return; -/** - * acpiphp_glue_exit - terminates all PCI hotplug - ACPI glue data structures - * - * This function frees all data allocated in acpiphp_glue_init(). - */ -void acpiphp_glue_exit(void) + bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); + if (bridge == NULL) { + err("out of memory\n"); + return; + } + + bridge->handle = handle; + bridge->pci_dev = pci_dev_get(bus->self); + bridge->pci_bus = bus; + + /* + * Grab a ref to the subordinate PCI bus in case the bus is + * removed via PCI core logical hotplug. The ref pins the bus + * (which we access during module unload). + */ + get_device(&bus->dev); + + if (!pci_is_root_bus(bridge->pci_bus) && + ACPI_SUCCESS(acpi_get_handle(bridge->handle, + "_EJ0", &dummy_handle))) { + dbg("found ejectable p2p bridge\n"); + bridge->flags |= BRIDGE_HAS_EJ0; + bridge->func = acpiphp_bridge_handle_to_function(handle); + } + + init_bridge_misc(bridge); +} + +/* Destroy hotplug slots associated with the PCI bus */ +void acpiphp_remove_slots(struct pci_bus *bus) { - acpi_pci_unregister_driver(&acpi_pci_hp_driver); + struct acpiphp_bridge *bridge, *tmp; + + if (acpiphp_disabled) + return; + + list_for_each_entry_safe(bridge, tmp, &bridge_list, list) + if (bridge->pci_bus == bus) { + cleanup_bridge(bridge); + break; + } } /** diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 98e582a2c90..d927933dcf4 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -297,6 +297,7 @@ void acpi_pci_add_bus(struct pci_bus *bus) return; acpi_pci_slot_enumerate(bus, handle); + acpiphp_enumerate_slots(bus, handle); } void acpi_pci_remove_bus(struct pci_bus *bus) @@ -308,6 +309,7 @@ void acpi_pci_remove_bus(struct pci_bus *bus) if (acpi_pci_disabled) return; + acpiphp_remove_slots(bus); acpi_pci_slot_remove(bus); } @@ -388,6 +390,7 @@ static int __init acpi_pci_init(void) pci_set_platform_pm(&acpi_pci_platform_pm); acpi_pci_slot_init(); + acpiphp_init(); return 0; } -- cgit v1.2.3 From ad41dd9dd0c8ca1876f30b62c5c79625ffe83174 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Fri, 12 Apr 2013 05:44:27 +0000 Subject: PCI: acpiphp: Use normal list to simplify implementation Use normal list for struct acpiphp_slot to simplify implementation. Signed-off-by: Yijing Wang Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani --- drivers/pci/hotplug/acpiphp.h | 4 ++-- drivers/pci/hotplug/acpiphp_glue.c | 23 ++++++++++------------- 2 files changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index abd48d309ad..4ff716b6a6b 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -73,8 +73,8 @@ static inline const char *slot_name(struct slot *slot) */ struct acpiphp_bridge { struct list_head list; + struct list_head slots; acpi_handle handle; - struct acpiphp_slot *slots; /* Ejectable PCI-to-PCI bridge (PCI bridge and PCI function) */ struct acpiphp_func *func; @@ -97,7 +97,7 @@ struct acpiphp_bridge { * PCI slot information for each *physical* PCI slot */ struct acpiphp_slot { - struct acpiphp_slot *next; + struct list_head node; struct acpiphp_bridge *bridge; /* parent */ struct list_head funcs; /* one slot may have different objects (i.e. for each function) */ diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 20db4d6564a..453a749d959 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -154,7 +154,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_handle tmp; acpi_status status = AE_OK; unsigned long long adr, sun; - int device, function, retval; + int device, function, retval, found = 0; struct pci_bus *pbus = bridge->pci_bus; struct pci_dev *pdev; u32 val; @@ -208,14 +208,15 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) } /* search for objects that share the same slot */ - for (slot = bridge->slots; slot; slot = slot->next) + list_for_each_entry(slot, &bridge->slots, node) if (slot->device == device) { if (slot->sun != sun) warn("sibling found, but _SUN doesn't match!\n"); + found = 1; break; } - if (!slot) { + if (!found) { slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); if (!slot) { kfree(newfunc); @@ -228,9 +229,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) INIT_LIST_HEAD(&slot->funcs); mutex_init(&slot->crit_sect); - slot->next = bridge->slots; - bridge->slots = slot; - + list_add_tail(&slot->node, &bridge->slots); bridge->nr_slots++; dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", @@ -289,7 +288,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) err_exit: bridge->nr_slots--; - bridge->slots = slot->next; + list_del(&slot->node); kfree(slot); kfree(newfunc); @@ -353,7 +352,7 @@ static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle struct acpiphp_func *func; list_for_each_entry(bridge, &bridge_list, list) { - for (slot = bridge->slots; slot; slot = slot->next) { + list_for_each_entry(slot, &bridge->slots, node) { list_for_each_entry(func, &slot->funcs, sibling) { if (func->handle == handle) return func; @@ -400,9 +399,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) err("failed to install interrupt notify handler\n"); } - slot = bridge->slots; - while (slot) { - next = slot->next; + list_for_each_entry_safe(slot, next, &bridge->slots, node) { list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) { if (is_dock_device(func->handle)) { unregister_hotplug_dock_device(func->handle); @@ -421,7 +418,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) acpiphp_unregister_hotplug_slot(slot); list_del(&slot->funcs); kfree(slot); - slot = next; } put_device(&bridge->pci_bus->dev); @@ -820,7 +816,7 @@ static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) enabled = disabled = 0; - for (slot = bridge->slots; slot; slot = slot->next) { + list_for_each_entry(slot, &bridge->slots, node) { unsigned int status = get_slot_status(slot); if (slot->flags & SLOT_ENABLED) { if (status == ACPI_STA_ALL) @@ -1098,6 +1094,7 @@ void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle) return; } + INIT_LIST_HEAD(&bridge->slots); bridge->handle = handle; bridge->pci_dev = pci_dev_get(bus->self); bridge->pci_bus = bus; -- cgit v1.2.3 From 3d54a3160fb6ba877324ffffa5d301dec8038fd9 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 12 Apr 2013 05:44:28 +0000 Subject: PCI: acpiphp: Protect acpiphp data structures from concurrent updates Now acpiphp_enumerate_slots() and acpiphp_remove_slots() may be invoked concurrently by the PCI core, so add a bridge_mutex and reference count mechanism to protect acpiphp bridge/slot/function data structures. To avoid deadlock, handle_hotplug_event_bridge() will requeue the hotplug event onto the kacpi_hotplug_wq by calling alloc_acpi_hp_work(). But the workaround has introduced a minor race window because the 'bridge' passed to _handle_hotplug_event_bridge() may have already been destroyed when _handle_hotplug_event_bridge() is actually executed by the kacpi_hotplug_wq. So hold a reference count on the passed 'bridge'. Fix the same issue for handle_hotplug_event_func() too. Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Reviewed-by: Yinghai Lu Cc: "Rafael J. Wysocki" Cc: Toshi Kani --- drivers/pci/hotplug/acpiphp.h | 1 + drivers/pci/hotplug/acpiphp_glue.c | 95 ++++++++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 18 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 4ff716b6a6b..6a319f42b30 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -74,6 +74,7 @@ static inline const char *slot_name(struct slot *slot) struct acpiphp_bridge { struct list_head list; struct list_head slots; + struct kref ref; acpi_handle handle; /* Ejectable PCI-to-PCI bridge (PCI bridge and PCI function) */ diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 453a749d959..96fed19c6d9 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -54,6 +54,7 @@ #include "acpiphp.h" static LIST_HEAD(bridge_list); +static DEFINE_MUTEX(bridge_mutex); #define MY_NAME "acpiphp_glue" @@ -61,6 +62,7 @@ static void handle_hotplug_event_bridge (acpi_handle, u32, void *); static void acpiphp_sanitize_bus(struct pci_bus *bus); static void acpiphp_set_hpp_values(struct pci_bus *bus); static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context); +static void free_bridge(struct kref *kref); /* callback routine to check for the existence of a pci dock device */ static acpi_status @@ -76,6 +78,39 @@ is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv) } } +static inline void get_bridge(struct acpiphp_bridge *bridge) +{ + kref_get(&bridge->ref); +} + +static inline void put_bridge(struct acpiphp_bridge *bridge) +{ + kref_put(&bridge->ref, free_bridge); +} + +static void free_bridge(struct kref *kref) +{ + struct acpiphp_bridge *bridge; + struct acpiphp_slot *slot, *next; + struct acpiphp_func *func, *tmp; + + bridge = container_of(kref, struct acpiphp_bridge, ref); + + list_for_each_entry_safe(slot, next, &bridge->slots, node) { + list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) { + kfree(func); + } + kfree(slot); + } + + /* Release reference acquired by acpiphp_bridge_handle_to_function() */ + if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) + put_bridge(bridge->func->slot->bridge); + put_device(&bridge->pci_bus->dev); + pci_dev_put(bridge->pci_dev); + kfree(bridge); +} + /* * the _DCK method can do funny things... and sometimes not * hah-hah funny. @@ -171,7 +206,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) device = (adr >> 16) & 0xffff; function = adr & 0xffff; - pdev = pbus->self; + pdev = bridge->pci_dev; if (pdev && device_is_managed_by_native_pciehp(pdev)) return AE_OK; @@ -179,7 +214,6 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) if (!newfunc) return AE_NO_MEMORY; - INIT_LIST_HEAD(&newfunc->sibling); newfunc->handle = handle; newfunc->function = function; @@ -229,7 +263,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) INIT_LIST_HEAD(&slot->funcs); mutex_init(&slot->crit_sect); + mutex_lock(&bridge_mutex); list_add_tail(&slot->node, &bridge->slots); + mutex_unlock(&bridge_mutex); bridge->nr_slots++; dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", @@ -247,7 +283,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) } newfunc->slot = slot; + mutex_lock(&bridge_mutex); list_add_tail(&newfunc->sibling, &slot->funcs); + mutex_unlock(&bridge_mutex); if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function), &val, 60*1000)) @@ -288,7 +326,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) err_exit: bridge->nr_slots--; + mutex_lock(&bridge_mutex); list_del(&slot->node); + mutex_unlock(&bridge_mutex); kfree(slot); kfree(newfunc); @@ -313,13 +353,17 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge) acpi_status status; /* must be added to the list prior to calling register_slot */ + mutex_lock(&bridge_mutex); list_add(&bridge->list, &bridge_list); + mutex_unlock(&bridge_mutex); /* register all slot objects under this bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1, register_slot, NULL, bridge, NULL); if (ACPI_FAILURE(status)) { + mutex_lock(&bridge_mutex); list_del(&bridge->list); + mutex_unlock(&bridge_mutex); return; } @@ -349,16 +393,21 @@ static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle { struct acpiphp_bridge *bridge; struct acpiphp_slot *slot; - struct acpiphp_func *func; + struct acpiphp_func *func = NULL; + mutex_lock(&bridge_mutex); list_for_each_entry(bridge, &bridge_list, list) { list_for_each_entry(slot, &bridge->slots, node) { list_for_each_entry(func, &slot->funcs, sibling) { - if (func->handle == handle) + if (func->handle == handle) { + get_bridge(func->slot->bridge); + mutex_unlock(&bridge_mutex); return func; + } } } } + mutex_unlock(&bridge_mutex); return NULL; } @@ -368,17 +417,22 @@ static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) { struct acpiphp_bridge *bridge; + mutex_lock(&bridge_mutex); list_for_each_entry(bridge, &bridge_list, list) - if (bridge->handle == handle) + if (bridge->handle == handle) { + get_bridge(bridge); + mutex_unlock(&bridge_mutex); return bridge; + } + mutex_unlock(&bridge_mutex); return NULL; } static void cleanup_bridge(struct acpiphp_bridge *bridge) { - struct acpiphp_slot *slot, *next; - struct acpiphp_func *func, *tmp; + struct acpiphp_slot *slot; + struct acpiphp_func *func; acpi_status status; acpi_handle handle = bridge->handle; @@ -399,8 +453,8 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) err("failed to install interrupt notify handler\n"); } - list_for_each_entry_safe(slot, next, &bridge->slots, node) { - list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) { + list_for_each_entry(slot, &bridge->slots, node) { + list_for_each_entry(func, &slot->funcs, sibling) { if (is_dock_device(func->handle)) { unregister_hotplug_dock_device(func->handle); unregister_dock_notifier(&func->nb); @@ -412,18 +466,13 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) if (ACPI_FAILURE(status)) err("failed to remove notify handler\n"); } - list_del(&func->sibling); - kfree(func); } acpiphp_unregister_hotplug_slot(slot); - list_del(&slot->funcs); - kfree(slot); } - put_device(&bridge->pci_bus->dev); - pci_dev_put(bridge->pci_dev); + mutex_lock(&bridge_mutex); list_del(&bridge->list); - kfree(bridge); + mutex_unlock(&bridge_mutex); } static int power_on_slot(struct acpiphp_slot *slot) @@ -620,7 +669,6 @@ static int __ref enable_device(struct acpiphp_slot *slot) struct pci_dev *dev; struct pci_bus *bus = slot->bridge->pci_bus; struct acpiphp_func *func; - int retval = 0; int num, max, pass; if (slot->flags & SLOT_ENABLED) @@ -680,7 +728,7 @@ static int __ref enable_device(struct acpiphp_slot *slot) err_exit: - return retval; + return 0; } /* return first device in slot, acquiring a reference on it */ @@ -897,6 +945,7 @@ check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) dbg("%s: re-enumerating slots under %s\n", __func__, objname); acpiphp_check_bridge(bridge); + put_bridge(bridge); } return AE_OK ; } @@ -974,6 +1023,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ + put_bridge(bridge); } /** @@ -987,6 +1037,8 @@ static void _handle_hotplug_event_bridge(struct work_struct *work) static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *context) { + struct acpiphp_bridge *bridge = context; + /* * Currently the code adds all hotplug events to the kacpid_wq * queue when it should add hotplug events to the kacpi_hotplug_wq. @@ -995,6 +1047,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, * For now just re-add this work to the kacpi_hotplug_wq so we * don't deadlock on hotplug actions. */ + get_bridge(bridge); alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge); } @@ -1049,6 +1102,7 @@ static void _handle_hotplug_event_func(struct work_struct *work) acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_func */ + put_bridge(func->slot->bridge); } /** @@ -1062,6 +1116,8 @@ static void _handle_hotplug_event_func(struct work_struct *work) static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context) { + struct acpiphp_func *func = context; + /* * Currently the code adds all hotplug events to the kacpid_wq * queue when it should add hotplug events to the kacpi_hotplug_wq. @@ -1070,6 +1126,7 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, * For now just re-add this work to the kacpi_hotplug_wq so we * don't deadlock on hotplug actions. */ + get_bridge(func->slot->bridge); alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func); } @@ -1095,6 +1152,7 @@ void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle) } INIT_LIST_HEAD(&bridge->slots); + kref_init(&bridge->ref); bridge->handle = handle; bridge->pci_dev = pci_dev_get(bus->self); bridge->pci_bus = bus; @@ -1128,6 +1186,7 @@ void acpiphp_remove_slots(struct pci_bus *bus) list_for_each_entry_safe(bridge, tmp, &bridge_list, list) if (bridge->pci_bus == bus) { cleanup_bridge(bridge); + put_bridge(bridge); break; } } -- cgit v1.2.3