ACPI / scan: Add acpi_device objects for all device nodes in the namespace
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 22 Nov 2013 20:54:37 +0000 (21:54 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 22 Nov 2013 20:54:37 +0000 (21:54 +0100)
Modify the ACPI namespace scanning code to register a struct
acpi_device object for every namespace node representing a device,
processor and so on, even if the device represented by that namespace
node is reported to be not present and not functional by _STA.

There are multiple reasons to do that.  First of all, it avoids
quite a lot of overhead when struct acpi_device objects are
deleted every time acpi_bus_trim() is run and then added again
by a subsequent acpi_bus_scan() for the same scope, although the
namespace objects they correspond to stay in memory all the time
(which always is the case on a vast majority of systems).

Second, it will allow user space to see that there are namespace
nodes representing devices that are not present at the moment and may
be added to the system.  It will also allow user space to evaluate
_SUN for those nodes to check what physical slots the "missing"
devices may be put into and it will make sense to add a sysfs
attribute for _STA evaluation after this change (that will be
useful for thermal management on some systems).

Next, it will help to consolidate the ACPI hotplug handling among
subsystems by making it possible to store hotplug-related information
in struct acpi_device objects in a standard common way.

Finally, it will help to avoid a race condition related to the
deletion of ACPI namespace nodes.  Namely, namespace nodes may be
deleted as a result of a table unload triggered by _EJ0 or _DCK.
If a hotplug notification for one of those nodes is triggered
right before the deletion and it executes a hotplug callback
via acpi_hotplug_execute(), the ACPI handle passed to that
callback may be stale when the callback actually runs.  One way
to work around that is to always pass struct acpi_device pointers
to hotplug callbacks after doing a get_device() on the objects in
question which eliminates the use-after-free possibility (the ACPI
handles in those objects are invalidated by acpi_scan_drop_device(),
so they will trigger ACPICA errors on attempts to use them).

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Documentation/acpi/namespace.txt
drivers/acpi/device_pm.c
drivers/acpi/dock.c
drivers/acpi/internal.h
drivers/acpi/pci_root.c
drivers/acpi/scan.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/xen/xen-acpi-cpuhotplug.c
drivers/xen/xen-acpi-memhotplug.c
include/acpi/acpi_bus.h

index 260f6a3661faff625aabda6bc3dc6ba9eda84012..1860cb3865c6b26474a2d07d3944a9858a9f07c6 100644 (file)
@@ -235,10 +235,6 @@ Wysocki <rafael.j.wysocki@intel.com>.
       named object's type in the second column).  In that case the object's
       directory in sysfs will contain the 'path' attribute whose value is
       the full path to the node from the namespace root.
-      struct acpi_device objects are created for the ACPI namespace nodes
-      whose _STA control methods return PRESENT or FUNCTIONING.  The power
-      resource nodes or nodes without _STA are assumed to be both PRESENT
-      and FUNCTIONING.
    F:
       The struct acpi_device object is created for a fixed hardware
       feature (as indicated by the fixed feature flag's name in the second
@@ -340,7 +336,7 @@ Wysocki <rafael.j.wysocki@intel.com>.
      | +-------------+-------+----------------+
      |   |
      |   | +- - - - - - - +- - - - - - +- - - - - - - -+
-     |   +-| PNP0C0D:00 | \_SB_.LID0 | acpi:PNP0C0D: |
+     |   +-| PNP0C0D:00 | \_SB_.LID0 | acpi:PNP0C0D: |
      |   | +- - - - - - - +- - - - - - +- - - - - - - -+
      |   |
      |   | +------------+------------+-----------------------+
@@ -390,6 +386,3 @@ Wysocki <rafael.j.wysocki@intel.com>.
             attribute (as described earlier in this document).
    NOTE: N/A indicates the device object does not have the 'path' or the
          'modalias' attribute.
-   NOTE: The PNP0C0D device listed above is highlighted (marked by "*")
-         to indicate it will be created only when its _STA methods return
-         PRESENT or FUNCTIONING.
index b3480cf7db1a1d1eba0e8d884cb456f92b76b660..d49f1e46470370908d72b6efb7677d70f80ded98 100644 (file)
@@ -256,6 +256,8 @@ int acpi_bus_init_power(struct acpi_device *device)
                return -EINVAL;
 
        device->power.state = ACPI_STATE_UNKNOWN;
+       if (!acpi_device_is_present(device))
+               return 0;
 
        result = acpi_device_get_power(device, &state);
        if (result)
@@ -302,15 +304,18 @@ int acpi_device_fix_up_power(struct acpi_device *device)
        return ret;
 }
 
-int acpi_bus_update_power(acpi_handle handle, int *state_p)
+int acpi_device_update_power(struct acpi_device *device, int *state_p)
 {
-       struct acpi_device *device;
        int state;
        int result;
 
-       result = acpi_bus_get_device(handle, &device);
-       if (result)
+       if (device->power.state == ACPI_STATE_UNKNOWN) {
+               result = acpi_bus_init_power(device);
+               if (!result && state_p)
+                       *state_p = device->power.state;
+
                return result;
+       }
 
        result = acpi_device_get_power(device, &state);
        if (result)
@@ -338,6 +343,15 @@ int acpi_bus_update_power(acpi_handle handle, int *state_p)
 
        return 0;
 }
+
+int acpi_bus_update_power(acpi_handle handle, int *state_p)
+{
+       struct acpi_device *device;
+       int result;
+
+       result = acpi_bus_get_device(handle, &device);
+       return result ? result : acpi_device_update_power(device, state_p);
+}
 EXPORT_SYMBOL_GPL(acpi_bus_update_power);
 
 bool acpi_bus_power_manageable(acpi_handle handle)
index dcd73ccb514c601df4efdee7043f2a7cffeb718e..de032010da3c18613900a1a9951e24610af644d1 100644 (file)
@@ -323,14 +323,11 @@ static int dock_present(struct dock_station *ds)
  */
 static void dock_create_acpi_device(acpi_handle handle)
 {
-       struct acpi_device *device;
+       struct acpi_device *device = NULL;
        int ret;
 
-       if (acpi_bus_get_device(handle, &device)) {
-               /*
-                * no device created for this object,
-                * so we should create one.
-                */
+       acpi_bus_get_device(handle, &device);
+       if (!acpi_device_enumerated(device)) {
                ret = acpi_bus_scan(handle);
                if (ret)
                        pr_debug("error adding bus, %x\n", -ret);
index d8606498bf6f0d97ae2a30fae4287abf2c7614ec..809b8082c134029250ef9c7b9f2167a7c04b78a4 100644 (file)
@@ -90,6 +90,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
 int acpi_bind_one(struct device *dev, acpi_handle handle);
 int acpi_unbind_one(struct device *dev);
 void acpi_bus_device_eject(void *data, u32 ost_src);
+bool acpi_device_is_present(struct acpi_device *adev);
 
 /* --------------------------------------------------------------------------
                                   Power Resource
@@ -107,6 +108,8 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
 int acpi_power_on_resources(struct acpi_device *device, int state);
 int acpi_power_transition(struct acpi_device *device, int state);
 
+int acpi_device_update_power(struct acpi_device *device, int *state_p);
+
 int acpi_wakeup_device_init(void);
 void acpi_early_processor_set_pdc(void);
 
index 20360e480bd8d81c0070cc25b5cd2cddd05d0447..4076491c6ded1840000e4521ef7e129e0d547092 100644 (file)
@@ -634,9 +634,10 @@ void __init acpi_pci_root_init(void)
 
 static void handle_root_bridge_insertion(acpi_handle handle)
 {
-       struct acpi_device *device;
+       struct acpi_device *device = NULL;
 
-       if (!acpi_bus_get_device(handle, &device)) {
+       acpi_bus_get_device(handle, &device);
+       if (acpi_device_enumerated(device)) {
                dev_printk(KERN_DEBUG, &device->dev,
                           "acpi device already exists; ignoring notify\n");
                return;
index ad2522015e5ea51ed1f7b759da3629e3bd595996..bc52192785f1175ee31a7f58d9cd6fcb048a305f 100644 (file)
@@ -259,7 +259,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
 
        acpi_bus_trim(device);
 
-       /* Device node has been unregistered. */
        put_device(&device->dev);
        device = NULL;
 
@@ -328,7 +327,7 @@ void acpi_bus_device_eject(void *data, u32 ost_src)
 static void acpi_scan_bus_device_check(void *data, u32 ost_source)
 {
        acpi_handle handle = data;
-       struct acpi_device *device = NULL;
+       struct acpi_device *device;
        u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
        int error;
 
@@ -336,8 +335,9 @@ static void acpi_scan_bus_device_check(void *data, u32 ost_source)
        mutex_lock(&acpi_scan_lock);
 
        if (ost_source != ACPI_NOTIFY_BUS_CHECK) {
+               device = NULL;
                acpi_bus_get_device(handle, &device);
-               if (device) {
+               if (acpi_device_enumerated(device)) {
                        dev_warn(&device->dev, "Attempt to re-insert\n");
                        goto out;
                }
@@ -347,9 +347,10 @@ static void acpi_scan_bus_device_check(void *data, u32 ost_source)
                acpi_handle_warn(handle, "Namespace scan failure\n");
                goto out;
        }
-       error = acpi_bus_get_device(handle, &device);
-       if (error) {
-               acpi_handle_warn(handle, "Missing device node object\n");
+       device = NULL;
+       acpi_bus_get_device(handle, &device);
+       if (!acpi_device_enumerated(device)) {
+               acpi_handle_warn(handle, "Device not enumerated\n");
                goto out;
        }
        ost_code = ACPI_OST_SC_SUCCESS;
@@ -1111,20 +1112,6 @@ int acpi_device_add(struct acpi_device *device,
        return result;
 }
 
-static void acpi_device_unregister(struct acpi_device *device)
-{
-       acpi_detach_data(device->handle, acpi_scan_drop_device);
-       acpi_device_del(device);
-       /*
-        * Transition the device to D3cold to drop the reference counts of all
-        * power resources the device depends on and turn off the ones that have
-        * no more references.
-        */
-       acpi_device_set_power(device, ACPI_STATE_D3_COLD);
-       device->handle = NULL;
-       put_device(&device->dev);
-}
-
 /* --------------------------------------------------------------------------
                                  Driver Management
    -------------------------------------------------------------------------- */
@@ -1703,6 +1690,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
        acpi_set_pnp_ids(handle, &device->pnp, type);
        acpi_bus_get_flags(device);
        device->flags.match_driver = false;
+       device->flags.initialized = true;
+       device->flags.visited = false;
        device_initialize(&device->dev);
        dev_set_uevent_suppress(&device->dev, true);
 }
@@ -1787,6 +1776,15 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type,
        return 0;
 }
 
+bool acpi_device_is_present(struct acpi_device *adev)
+{
+       if (adev->status.present || adev->status.functional)
+               return true;
+
+       adev->flags.initialized = false;
+       return false;
+}
+
 static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
                                       char *idstr,
                                       const struct acpi_device_id **matchid)
@@ -1880,18 +1878,6 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
 
        acpi_scan_init_hotplug(handle, type);
 
-       if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
-           !(sta & ACPI_STA_DEVICE_FUNCTIONING)) {
-               struct acpi_device_wakeup wakeup;
-
-               if (acpi_has_method(handle, "_PRW")) {
-                       acpi_bus_extract_wakeup_device_power_package(handle,
-                                                                    &wakeup);
-                       acpi_power_resources_list_free(&wakeup.resources);
-               }
-               return AE_CTRL_DEPTH;
-       }
-
        acpi_add_single_object(&device, handle, type, sta);
        if (!device)
                return AE_CTRL_DEPTH;
@@ -1930,32 +1916,50 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
                                          void *not_used, void **ret_not_used)
 {
        struct acpi_device *device;
-       unsigned long long sta_not_used;
+       unsigned long long sta;
        int ret;
 
        /*
         * Ignore errors ignored by acpi_bus_check_add() to avoid terminating
         * namespace walks prematurely.
         */
-       if (acpi_bus_type_and_status(handle, &ret, &sta_not_used))
+       if (acpi_bus_type_and_status(handle, &ret, &sta))
                return AE_OK;
 
        if (acpi_bus_get_device(handle, &device))
                return AE_CTRL_DEPTH;
 
+       STRUCT_TO_INT(device->status) = sta;
+       /* Skip devices that are not present. */
+       if (!acpi_device_is_present(device))
+               goto err;
+
        if (device->handler)
                return AE_OK;
 
+       if (!device->flags.initialized) {
+               acpi_bus_update_power(device, NULL);
+               device->flags.initialized = true;
+       }
        ret = acpi_scan_attach_handler(device);
        if (ret < 0)
-               return AE_CTRL_DEPTH;
+               goto err;
 
        device->flags.match_driver = true;
        if (ret > 0)
-               return AE_OK;
+               goto ok;
 
        ret = device_attach(&device->dev);
-       return ret >= 0 ? AE_OK : AE_CTRL_DEPTH;
+       if (ret < 0)
+               goto err;
+
+ ok:
+       device->flags.visited = true;
+       return AE_OK;
+
+ err:
+       device->flags.visited = false;
+       return AE_CTRL_DEPTH;
 }
 
 /**
@@ -2007,21 +2011,17 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used,
                } else {
                        device_release_driver(&device->dev);
                }
+               /*
+                * Most likely, the device is going away, so put it into D3cold
+                * before that.
+                */
+               acpi_device_set_power(device, ACPI_STATE_D3_COLD);
+               device->flags.initialized = false;
+               device->flags.visited = false;
        }
        return AE_OK;
 }
 
-static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used,
-                                  void *not_used, void **ret_not_used)
-{
-       struct acpi_device *device = NULL;
-
-       if (!acpi_bus_get_device(handle, &device))
-               acpi_device_unregister(device);
-
-       return AE_OK;
-}
-
 /**
  * acpi_bus_trim - Remove ACPI device node and all of its descendants
  * @start: Root of the ACPI device nodes subtree to remove.
@@ -2037,13 +2037,6 @@ void acpi_bus_trim(struct acpi_device *start)
        acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL,
                            acpi_bus_device_detach, NULL, NULL);
        acpi_bus_device_detach(start->handle, 0, NULL, NULL);
-       /*
-        * Execute acpi_bus_remove() as a post-order callback to remove device
-        * nodes in the given namespace scope.
-        */
-       acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL,
-                           acpi_bus_remove, NULL, NULL);
-       acpi_bus_remove(start->handle, 0, NULL, NULL);
 }
 EXPORT_SYMBOL_GPL(acpi_bus_trim);
 
@@ -2121,7 +2114,9 @@ int __init acpi_scan_init(void)
 
        result = acpi_bus_scan_fixed();
        if (result) {
-               acpi_device_unregister(acpi_root);
+               acpi_detach_data(acpi_root->handle, acpi_scan_drop_device);
+               acpi_device_del(acpi_root);
+               put_device(&acpi_root->dev);
                goto out;
        }
 
index 1cf605f6767357947e9a097981caa7d7869ca2f0..67be6bab753599e8dfcaf99b92a729139f288871 100644 (file)
@@ -489,7 +489,7 @@ static void acpiphp_bus_add(acpi_handle handle)
 
        acpi_bus_scan(handle);
        acpi_bus_get_device(handle, &adev);
-       if (adev)
+       if (acpi_device_enumerated(adev))
                acpi_device_set_power(adev, ACPI_STATE_D0);
 }
 
index 8dae6c13063af5df3846d0c3a700876fe23eefe0..73496c3d18ae349b83f418b99b6c0ea0de5e7ce0 100644 (file)
@@ -269,7 +269,8 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
                if (!is_processor_present(handle))
                        break;
 
-               if (!acpi_bus_get_device(handle, &device))
+               acpi_bus_get_device(handle, &device);
+               if (acpi_device_enumerated(device))
                        break;
 
                result = acpi_bus_scan(handle);
@@ -277,8 +278,9 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
                        pr_err(PREFIX "Unable to add the device\n");
                        break;
                }
-               result = acpi_bus_get_device(handle, &device);
-               if (result) {
+               device = NULL;
+               acpi_bus_get_device(handle, &device);
+               if (!acpi_device_enumerated(device)) {
                        pr_err(PREFIX "Missing device object\n");
                        break;
                }
index 9083f1e474f801c07c1531551fa971b5e0e21b38..9b056f06691fd08e20619c4d2226f161251a5d0f 100644 (file)
@@ -169,7 +169,7 @@ static int acpi_memory_get_device(acpi_handle handle,
        acpi_scan_lock_acquire();
 
        acpi_bus_get_device(handle, &device);
-       if (device)
+       if (acpi_device_enumerated(device))
                goto end;
 
        /*
@@ -182,8 +182,9 @@ static int acpi_memory_get_device(acpi_handle handle,
                result = -EINVAL;
                goto out;
        }
-       result = acpi_bus_get_device(handle, &device);
-       if (result) {
+       device = NULL;
+       acpi_bus_get_device(handle, &device);
+       if (!acpi_device_enumerated(device)) {
                pr_warn(PREFIX "Missing device object\n");
                result = -EINVAL;
                goto out;
index 9fe5f63155ede4f93b3c119750a7da9cf94a5501..e748dbfca9d5048762ac7d68cb3de3ebd38772f8 100644 (file)
@@ -169,7 +169,9 @@ struct acpi_device_flags {
        u32 ejectable:1;
        u32 power_manageable:1;
        u32 match_driver:1;
-       u32 reserved:27;
+       u32 initialized:1;
+       u32 visited:1;
+       u32 reserved:25;
 };
 
 /* File System */
@@ -386,6 +388,11 @@ int acpi_match_device_ids(struct acpi_device *device,
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
 
+static inline bool acpi_device_enumerated(struct acpi_device *adev)
+{
+       return adev && adev->flags.initialized && adev->flags.visited;
+}
+
 typedef void (*acpi_hp_callback)(void *data, u32 src);
 
 acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);