PCI: acpiphp: Do not use ACPI PCI subdriver mechanism
authorJiang Liu <liuj97@gmail.com>
Fri, 12 Apr 2013 05:44:26 +0000 (05:44 +0000)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 12 Apr 2013 22:52:01 +0000 (16:52 -0600)
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 <jiang.liu@huawei.com>
Signed-off-by: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Yinghai Lu <yinghai@kernel.org>
Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
Cc: Toshi Kani <toshi.kani@hp.com>
drivers/pci/hotplug/acpiphp.h
drivers/pci/hotplug/acpiphp_core.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/pci-acpi.c
include/linux/pci-acpi.h

index b06ae681d5b7f61e9cd8fd43c2b5f74afcbd01e0..abd48d309ad0e09c10a4a23f6fc79e395a06342e 100644 (file)
@@ -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);
index 81adbfa4df1bae20683e2d8994ae4996df2da336..ca8127950fcd49397027370d82937246204a0e9b 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
+#include <linux/pci-acpi.h>
 #include <linux/pci_hotplug.h>
 #include <linux/slab.h>
 #include <linux/smp.h>
@@ -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);
index 718464f8fd40885b1bc06c56eec168c31f71e0cf..20db4d6564a82615d51cf802c80bac886a79fa49 100644 (file)
@@ -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;
+               }
 }
 
 /**
index 98e582a2c90945d138514a3a51df928188706c87..d927933dcf44afebd52b4a3d94eef1f90b788563 100644 (file)
@@ -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;
 }
index 23cd40abba4fae9a0f8daadd3a07c049267a78a9..81b31613eb252b3a46052d2375e333affc91c77f 100644 (file)
@@ -56,6 +56,17 @@ static inline void acpi_pci_slot_enumerate(struct pci_bus *bus,
 static inline void acpi_pci_slot_remove(struct pci_bus *bus) { }
 #endif
 
+#ifdef CONFIG_HOTPLUG_PCI_ACPI
+void acpiphp_init(void);
+void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle);
+void acpiphp_remove_slots(struct pci_bus *bus);
+#else
+static inline void acpiphp_init(void) { }
+static inline void acpiphp_enumerate_slots(struct pci_bus *bus,
+                                          acpi_handle handle) { }
+static inline void acpiphp_remove_slots(struct pci_bus *bus) { }
+#endif
+
 #else  /* CONFIG_ACPI */
 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
 static inline void acpi_pci_remove_bus(struct pci_bus *bus) { }