From d783156ea38431b20af0d4f910a6f9f9054d33b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:52:12 +0100 Subject: ACPI / scan: Define non-empty device removal handler If an ACPI namespace node is removed (usually, as a result of a table unload), and there is a data object attached to that node, acpi_ns_delete_node() executes the removal handler submitted to acpi_attach_data() for that object. That handler is currently empty for struct acpi_device objects, so it is necessary to detach those objects from the corresponding ACPI namespace nodes in advance every time a table unload may happen. That is cumbersome and inefficient and leads to some design constraints that turn out to be quite inconvenient (in particular, struct acpi_device objects cannot be registered for namespace nodes representing devices that are not reported as present or functional by _STA). For this reason, introduce a non-empty removal handler for ACPI device objects that will unregister them when their ACPI namespace nodes go away. This code modification alone should not change functionality except for the ordering of the ACPI hotplug workqueue which should not matter (without subsequent code changes). Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/internal.h | 2 + drivers/acpi/osl.c | 6 ++- drivers/acpi/scan.c | 114 +++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 101 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index a29739c0ba79..d8606498bf6f 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -73,6 +73,8 @@ void acpi_lpss_init(void); static inline void acpi_lpss_init(void) {} #endif +bool acpi_queue_hotplug_work(struct work_struct *work); + /* -------------------------------------------------------------------------- Device Node Initialization / Removal -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 54a20ff4b864..5b9a785e2155 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1215,6 +1215,10 @@ acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src) return AE_OK; } +bool acpi_queue_hotplug_work(struct work_struct *work) +{ + return queue_work(kacpi_hotplug_wq, work); +} acpi_status acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle) @@ -1794,7 +1798,7 @@ acpi_status __init acpi_os_initialize1(void) { kacpid_wq = alloc_workqueue("kacpid", 0, 1); kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1); - kacpi_hotplug_wq = alloc_workqueue("kacpi_hotplug", 0, 1); + kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0); BUG_ON(!kacpid_wq); BUG_ON(!kacpi_notify_wq); BUG_ON(!kacpi_hotplug_wq); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fd39459926b1..ad2522015e5e 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -14,6 +14,8 @@ #include +#include + #include "internal.h" #define _COMPONENT ACPI_BUS_COMPONENT @@ -27,6 +29,8 @@ extern struct acpi_device *acpi_root; #define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) +#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page) + /* * If set, devices will be hot-removed even if they cannot be put offline * gracefully (from the kernel's standpoint). @@ -907,9 +911,91 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; -static void acpi_bus_data_handler(acpi_handle handle, void *context) +static void acpi_device_del(struct acpi_device *device) +{ + mutex_lock(&acpi_device_lock); + if (device->parent) + list_del(&device->node); + + list_del(&device->wakeup_list); + mutex_unlock(&acpi_device_lock); + + acpi_power_add_remove_device(device, false); + acpi_device_remove_files(device); + if (device->remove) + device->remove(device); + + device_del(&device->dev); +} + +static LIST_HEAD(acpi_device_del_list); +static DEFINE_MUTEX(acpi_device_del_lock); + +static void acpi_device_del_work_fn(struct work_struct *work_not_used) +{ + for (;;) { + struct acpi_device *adev; + + mutex_lock(&acpi_device_del_lock); + + if (list_empty(&acpi_device_del_list)) { + mutex_unlock(&acpi_device_del_lock); + break; + } + adev = list_first_entry(&acpi_device_del_list, + struct acpi_device, del_list); + list_del(&adev->del_list); + + mutex_unlock(&acpi_device_del_lock); + + acpi_device_del(adev); + /* + * Drop references to all power resources that might have been + * used by the device. + */ + acpi_power_transition(adev, ACPI_STATE_D3_COLD); + put_device(&adev->dev); + } +} + +/** + * acpi_scan_drop_device - Drop an ACPI device object. + * @handle: Handle of an ACPI namespace node, not used. + * @context: Address of the ACPI device object to drop. + * + * This is invoked by acpi_ns_delete_node() during the removal of the ACPI + * namespace node the device object pointed to by @context is attached to. + * + * The unregistration is carried out asynchronously to avoid running + * acpi_device_del() under the ACPICA's namespace mutex and the list is used to + * ensure the correct ordering (the device objects must be unregistered in the + * same order in which the corresponding namespace nodes are deleted). + */ +static void acpi_scan_drop_device(acpi_handle handle, void *context) { - /* Intentionally empty. */ + static DECLARE_WORK(work, acpi_device_del_work_fn); + struct acpi_device *adev = context; + + mutex_lock(&acpi_device_del_lock); + + /* + * Use the ACPI hotplug workqueue which is ordered, so this work item + * won't run after any hotplug work items submitted subsequently. That + * prevents attempts to register device objects identical to those being + * deleted from happening concurrently (such attempts result from + * hotplug events handled via the ACPI hotplug workqueue). It also will + * run after all of the work items submitted previosuly, which helps + * those work items to ensure that they are not accessing stale device + * objects. + */ + if (list_empty(&acpi_device_del_list)) + acpi_queue_hotplug_work(&work); + + list_add_tail(&adev->del_list, &acpi_device_del_list); + /* Make acpi_ns_validate_handle() return NULL for this handle. */ + adev->handle = INVALID_ACPI_HANDLE; + + mutex_unlock(&acpi_device_del_lock); } int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) @@ -919,7 +1005,7 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) if (!device) return -EINVAL; - status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device); + status = acpi_get_data(handle, acpi_scan_drop_device, (void **)device); if (ACPI_FAILURE(status) || !*device) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", handle)); @@ -939,7 +1025,7 @@ int acpi_device_add(struct acpi_device *device, if (device->handle) { acpi_status status; - status = acpi_attach_data(device->handle, acpi_bus_data_handler, + status = acpi_attach_data(device->handle, acpi_scan_drop_device, device); if (ACPI_FAILURE(status)) { acpi_handle_err(device->handle, @@ -957,6 +1043,7 @@ int acpi_device_add(struct acpi_device *device, INIT_LIST_HEAD(&device->node); INIT_LIST_HEAD(&device->wakeup_list); INIT_LIST_HEAD(&device->physical_node_list); + INIT_LIST_HEAD(&device->del_list); mutex_init(&device->physical_node_lock); new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); @@ -1020,27 +1107,14 @@ int acpi_device_add(struct acpi_device *device, mutex_unlock(&acpi_device_lock); err_detach: - acpi_detach_data(device->handle, acpi_bus_data_handler); + acpi_detach_data(device->handle, acpi_scan_drop_device); return result; } static void acpi_device_unregister(struct acpi_device *device) { - mutex_lock(&acpi_device_lock); - if (device->parent) - list_del(&device->node); - - list_del(&device->wakeup_list); - mutex_unlock(&acpi_device_lock); - - acpi_detach_data(device->handle, acpi_bus_data_handler); - - acpi_power_add_remove_device(device, false); - acpi_device_remove_files(device); - if (device->remove) - device->remove(device); - - device_del(&device->dev); + 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 -- cgit v1.2.3 From 202317a573b20d77a9abb7c16a3fd5b40cef3d9d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:54:37 +0100 Subject: ACPI / scan: Add acpi_device objects for all device nodes in the namespace 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 Tested-by: Mika Westerberg --- drivers/acpi/device_pm.c | 22 ++++++-- drivers/acpi/dock.c | 9 ++-- drivers/acpi/internal.h | 3 ++ drivers/acpi/pci_root.c | 5 +- drivers/acpi/scan.c | 107 ++++++++++++++++++------------------- drivers/pci/hotplug/acpiphp_glue.c | 2 +- drivers/xen/xen-acpi-cpuhotplug.c | 8 +-- drivers/xen/xen-acpi-memhotplug.c | 7 +-- 8 files changed, 88 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index b3480cf7db1a..d49f1e464703 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -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) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index dcd73ccb514c..de032010da3c 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -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); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index d8606498bf6f..809b8082c134 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -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); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 20360e480bd8..4076491c6ded 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -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; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ad2522015e5e..bc52192785f1 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -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; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 1cf605f67673..67be6bab7535 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -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); } diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c index 8dae6c13063a..73496c3d18ae 100644 --- a/drivers/xen/xen-acpi-cpuhotplug.c +++ b/drivers/xen/xen-acpi-cpuhotplug.c @@ -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; } diff --git a/drivers/xen/xen-acpi-memhotplug.c b/drivers/xen/xen-acpi-memhotplug.c index 9083f1e474f8..9b056f06691f 100644 --- a/drivers/xen/xen-acpi-memhotplug.c +++ b/drivers/xen/xen-acpi-memhotplug.c @@ -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; -- cgit v1.2.3 From 1ceaba05b4afb4bd7b4b4801f2718c13f59321eb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:54:52 +0100 Subject: ACPI / hotplug: Do not fail bus and device checks for disabled hotplug If the scan handler for the given device has hotplug.enabled unset, it doesn't really make sense to fail bus check and device check notifications. First, bus check may not have anything to do with the device it is signaled for, but it may concern another device on the bus below this one. For this reason, bus check notifications should not be failed if hotplug is disabled for the target device. Second, device check notifications are signaled only after a device has already appeared (or disappeared), so failing it can only prevent scan handlers and drivers from attaching to that (already existing) device, which is not very useful. Consequently, if device hotplug is disabled through the device's scan handler, fail eject request notifications only. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/scan.c | 40 +++++++--------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index bc52192785f1..4fa416f94f52 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -363,43 +363,13 @@ static void acpi_scan_bus_device_check(void *data, u32 ost_source) unlock_device_hotplug(); } -static void acpi_hotplug_unsupported(acpi_handle handle, u32 type) -{ - u32 ost_status; - - switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - acpi_handle_debug(handle, - "ACPI_NOTIFY_BUS_CHECK event: unsupported\n"); - ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED; - break; - case ACPI_NOTIFY_DEVICE_CHECK: - acpi_handle_debug(handle, - "ACPI_NOTIFY_DEVICE_CHECK event: unsupported\n"); - ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED; - break; - case ACPI_NOTIFY_EJECT_REQUEST: - acpi_handle_debug(handle, - "ACPI_NOTIFY_EJECT_REQUEST event: unsupported\n"); - ost_status = ACPI_OST_SC_EJECT_NOT_SUPPORTED; - break; - default: - /* non-hotplug event; possibly handled by other handler */ - return; - } - - acpi_evaluate_hotplug_ost(handle, type, ost_status, NULL); -} - static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) { + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; struct acpi_scan_handler *handler = data; struct acpi_device *adev; acpi_status status; - if (!handler->hotplug.enabled) - return acpi_hotplug_unsupported(handle, type); - switch (type) { case ACPI_NOTIFY_BUS_CHECK: acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); @@ -409,6 +379,11 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) break; case ACPI_NOTIFY_EJECT_REQUEST: acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); + if (!handler->hotplug.enabled) { + acpi_handle_err(handle, "Eject disabled\n"); + ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; + goto err_out; + } if (acpi_bus_get_device(handle, &adev)) goto err_out; @@ -428,8 +403,7 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) return; err_out: - acpi_evaluate_hotplug_ost(handle, type, - ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); } static ssize_t real_power_state_show(struct device *dev, -- cgit v1.2.3 From c27b2c33b6215eeb3d5c290ac889ab6d543f6207 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:55:07 +0100 Subject: ACPI / hotplug: Introduce common hotplug function acpi_device_hotplug() Modify the common ACPI device hotplug code to always queue up the same function, acpi_device_hotplug(), using acpi_hotplug_execute() and make the PCI host bridge hotplug code use that function too for device hot removal. This allows some code duplication to be reduced and a race condition where the relevant ACPI handle may become invalid between the notification handler and the function queued up by it via acpi_hotplug_execute() to be avoided. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/internal.h | 2 +- drivers/acpi/pci_root.c | 4 +- drivers/acpi/scan.c | 141 ++++++++++++++++++++++-------------------------- 3 files changed, 68 insertions(+), 79 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 809b8082c134..a0d42cf5b0c5 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -89,7 +89,7 @@ void acpi_device_add_finalize(struct acpi_device *device); 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); +void acpi_device_hotplug(void *data, u32 ost_src); bool acpi_device_is_present(struct acpi_device *adev); /* -------------------------------------------------------------------------- diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 4076491c6ded..ca05064f3ff7 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -683,11 +683,13 @@ static void hotplug_event_root(void *data, u32 type) if (!root) break; + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); get_device(&root->device->dev); acpi_scan_lock_release(); - acpi_bus_device_eject(root->device, ACPI_NOTIFY_EJECT_REQUEST); + acpi_device_hotplug(root->device, ACPI_NOTIFY_EJECT_REQUEST); return; default: acpi_handle_warn(handle, diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 4fa416f94f52..dd0ff9de9277 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -206,12 +206,8 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_status status; unsigned long long sta; - /* If there is no handle, the device node has been unregistered. */ - if (!handle) { - dev_dbg(&device->dev, "ACPI handle missing\n"); - put_device(&device->dev); - return -EINVAL; - } + if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); /* * Carry out two passes here and ignore errors in the first pass, @@ -230,7 +226,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device) dev_warn(errdev, "Offline disabled.\n"); acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_online, NULL, NULL, NULL); - put_device(&device->dev); return -EPERM; } acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev); @@ -249,7 +244,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_online, NULL, NULL, NULL); - put_device(&device->dev); return -EBUSY; } } @@ -259,9 +253,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_bus_trim(device); - put_device(&device->dev); - device = NULL; - acpi_evaluate_lck(handle, 0); /* * TBD: _EJD support. @@ -288,77 +279,74 @@ static int acpi_scan_hot_remove(struct acpi_device *device) return 0; } -void acpi_bus_device_eject(void *data, u32 ost_src) +static int acpi_scan_device_check(struct acpi_device *adev) { - struct acpi_device *device = data; - acpi_handle handle = device->handle; - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; int error; - lock_device_hotplug(); - mutex_lock(&acpi_scan_lock); - - if (ost_src == ACPI_NOTIFY_EJECT_REQUEST) - acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, - ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); - - if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - - error = acpi_scan_hot_remove(device); - if (error == -EPERM) { - goto err_support; - } else if (error) { - goto err_out; + /* + * This function is only called for device objects for which matching + * scan handlers exist. The only situation in which the scan handler is + * not attached to this device object yet is when the device has just + * appeared (either it wasn't present at all before or it was removed + * and then added again). + */ + if (adev->handler) { + dev_warn(&adev->dev, "Already enumerated\n"); + return -EBUSY; } - - out: - mutex_unlock(&acpi_scan_lock); - unlock_device_hotplug(); - return; - - err_support: - ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; - err_out: - acpi_evaluate_hotplug_ost(handle, ost_src, ost_code, NULL); - goto out; + error = acpi_bus_scan(adev->handle); + if (error) { + dev_warn(&adev->dev, "Namespace scan failure\n"); + return error; + } + if (adev->handler) { + if (adev->handler->hotplug.mode == AHM_CONTAINER) + kobject_uevent(&adev->dev.kobj, KOBJ_ONLINE); + } else { + dev_warn(&adev->dev, "Enumeration failure\n"); + return -ENODEV; + } + return 0; } -static void acpi_scan_bus_device_check(void *data, u32 ost_source) +void acpi_device_hotplug(void *data, u32 src) { - acpi_handle handle = data; - struct acpi_device *device; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + struct acpi_device *adev = data; int error; lock_device_hotplug(); mutex_lock(&acpi_scan_lock); - if (ost_source != ACPI_NOTIFY_BUS_CHECK) { - device = NULL; - acpi_bus_get_device(handle, &device); - if (acpi_device_enumerated(device)) { - dev_warn(&device->dev, "Attempt to re-insert\n"); - goto out; - } - } - error = acpi_bus_scan(handle); - if (error) { - acpi_handle_warn(handle, "Namespace scan failure\n"); - goto out; - } - device = NULL; - acpi_bus_get_device(handle, &device); - if (!acpi_device_enumerated(device)) { - acpi_handle_warn(handle, "Device not enumerated\n"); + /* + * The device object's ACPI handle cannot become invalid as long as we + * are holding acpi_scan_lock, but it may have become invalid before + * that lock was acquired. + */ + if (adev->handle == INVALID_ACPI_HANDLE) goto out; + + switch (src) { + case ACPI_NOTIFY_BUS_CHECK: + error = acpi_bus_scan(adev->handle); + break; + case ACPI_NOTIFY_DEVICE_CHECK: + error = acpi_scan_device_check(adev); + break; + case ACPI_NOTIFY_EJECT_REQUEST: + case ACPI_OST_EC_OSPM_EJECT: + error = acpi_scan_hot_remove(adev); + break; + default: + error = -EINVAL; + break; } - ost_code = ACPI_OST_SC_SUCCESS; - if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) - kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); + if (!error) + ost_code = ACPI_OST_SC_SUCCESS; out: - acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); + acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL); + put_device(&adev->dev); mutex_unlock(&acpi_scan_lock); unlock_device_hotplug(); } @@ -370,6 +358,9 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) struct acpi_device *adev; acpi_status status; + if (acpi_bus_get_device(handle, &adev)) + goto err_out; + switch (type) { case ACPI_NOTIFY_BUS_CHECK: acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); @@ -384,24 +375,20 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; goto err_out; } - if (acpi_bus_get_device(handle, &adev)) - goto err_out; - - get_device(&adev->dev); - status = acpi_hotplug_execute(acpi_bus_device_eject, adev, type); - if (ACPI_SUCCESS(status)) - return; - - put_device(&adev->dev); - goto err_out; + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + break; default: /* non-hotplug event; possibly handled by other handler */ return; } - status = acpi_hotplug_execute(acpi_scan_bus_device_check, handle, type); + get_device(&adev->dev); + status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); if (ACPI_SUCCESS(status)) return; + put_device(&adev->dev); + err_out: acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); } @@ -454,7 +441,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); get_device(&acpi_device->dev); - status = acpi_hotplug_execute(acpi_bus_device_eject, acpi_device, + status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device, ACPI_OST_EC_OSPM_EJECT); if (ACPI_SUCCESS(status)) return count; -- cgit v1.2.3 From 3338db0057ed9f554050bd06863731c515d79672 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:55:20 +0100 Subject: ACPI / hotplug: Make ACPI PCI root hotplug use common hotplug code Rework the common ACPI device hotplug code so that it is suitable for PCI host bridge hotplug and switch the PCI host bridge scan handler to using the common hotplug code. This allows quite a few lines of code that are not necessary any more to be dropped from the PCI host bridge scan handler and removes arbitrary differences in behavior between PCI host bridge hotplug and ACPI-based hotplug of other components, like CPUs and memory. Also acpi_device_hotplug() can be static now. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/internal.h | 2 - drivers/acpi/pci_root.c | 125 +++++------------------------------------------- drivers/acpi/scan.c | 42 +++++++++------- 3 files changed, 36 insertions(+), 133 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index a0d42cf5b0c5..f4aa467c407e 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -28,7 +28,6 @@ int init_acpi_device_notify(void); int acpi_scan_init(void); void acpi_pci_root_init(void); void acpi_pci_link_init(void); -void acpi_pci_root_hp_init(void); void acpi_processor_init(void); void acpi_platform_init(void); int acpi_sysfs_init(void); @@ -89,7 +88,6 @@ void acpi_device_add_finalize(struct acpi_device *device); 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_device_hotplug(void *data, u32 ost_src); bool acpi_device_is_present(struct acpi_device *adev); /* -------------------------------------------------------------------------- diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index ca05064f3ff7..2dd11e0bac24 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -51,6 +51,12 @@ static int acpi_pci_root_add(struct acpi_device *device, const struct acpi_device_id *not_used); static void acpi_pci_root_remove(struct acpi_device *device); +static int acpi_pci_root_scan_dependent(struct acpi_device *adev) +{ + acpiphp_check_host_bridge(adev->handle); + return 0; +} + #define ACPI_PCIE_REQ_SUPPORT (OSC_PCI_EXT_CONFIG_SUPPORT \ | OSC_PCI_ASPM_SUPPORT \ | OSC_PCI_CLOCK_PM_SUPPORT \ @@ -66,7 +72,8 @@ static struct acpi_scan_handler pci_root_handler = { .attach = acpi_pci_root_add, .detach = acpi_pci_root_remove, .hotplug = { - .ignore = true, + .enabled = true, + .scan_dependent = acpi_pci_root_scan_dependent, }, }; @@ -624,119 +631,9 @@ static void acpi_pci_root_remove(struct acpi_device *device) void __init acpi_pci_root_init(void) { acpi_hest_init(); - - if (!acpi_pci_disabled) { - pci_acpi_crs_quirks(); - acpi_scan_add_handler(&pci_root_handler); - } -} -/* Support root bridge hotplug */ - -static void handle_root_bridge_insertion(acpi_handle handle) -{ - struct acpi_device *device = NULL; - - 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; - } - - if (acpi_bus_scan(handle)) - acpi_handle_err(handle, "cannot add bridge to acpi list\n"); -} - -static void hotplug_event_root(void *data, u32 type) -{ - acpi_handle handle = data; - struct acpi_pci_root *root; - - acpi_scan_lock_acquire(); - - root = acpi_pci_find_root(handle); - - switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - /* bus enumerate */ - acpi_handle_printk(KERN_DEBUG, handle, - "Bus check notify on %s\n", __func__); - if (root) - acpiphp_check_host_bridge(handle); - else - handle_root_bridge_insertion(handle); - - break; - - case ACPI_NOTIFY_DEVICE_CHECK: - /* device check */ - acpi_handle_printk(KERN_DEBUG, handle, - "Device check notify on %s\n", __func__); - if (!root) - handle_root_bridge_insertion(handle); - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - /* request device eject */ - acpi_handle_printk(KERN_DEBUG, handle, - "Device eject notify on %s\n", __func__); - if (!root) - break; - - acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, - ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); - get_device(&root->device->dev); - - acpi_scan_lock_release(); - - acpi_device_hotplug(root->device, ACPI_NOTIFY_EJECT_REQUEST); + if (acpi_pci_disabled) return; - default: - acpi_handle_warn(handle, - "notify_handler: unknown event type 0x%x\n", - type); - break; - } - - acpi_scan_lock_release(); -} - -static void handle_hotplug_event_root(acpi_handle handle, u32 type, - void *context) -{ - acpi_hotplug_execute(hotplug_event_root, handle, type); -} - -static acpi_status __init -find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - acpi_status status; - int *count = (int *)context; - - if (!acpi_is_root_bridge(handle)) - return AE_OK; - - (*count)++; - - status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - handle_hotplug_event_root, NULL); - if (ACPI_FAILURE(status)) - acpi_handle_printk(KERN_DEBUG, handle, - "notify handler is not installed, exit status: %u\n", - (unsigned int)status); - else - acpi_handle_printk(KERN_DEBUG, handle, - "notify handler is installed\n"); - - return AE_OK; -} - -void __init acpi_pci_root_hp_init(void) -{ - int num = 0; - - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL); - printk(KERN_DEBUG "Found %d acpi root devices\n", num); + pci_acpi_crs_quirks(); + acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root"); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index dd0ff9de9277..18865c86c463 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -283,17 +283,6 @@ static int acpi_scan_device_check(struct acpi_device *adev) { int error; - /* - * This function is only called for device objects for which matching - * scan handlers exist. The only situation in which the scan handler is - * not attached to this device object yet is when the device has just - * appeared (either it wasn't present at all before or it was removed - * and then added again). - */ - if (adev->handler) { - dev_warn(&adev->dev, "Already enumerated\n"); - return -EBUSY; - } error = acpi_bus_scan(adev->handle); if (error) { dev_warn(&adev->dev, "Namespace scan failure\n"); @@ -309,10 +298,11 @@ static int acpi_scan_device_check(struct acpi_device *adev) return 0; } -void acpi_device_hotplug(void *data, u32 src) +static void acpi_device_hotplug(void *data, u32 src) { u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; struct acpi_device *adev = data; + struct acpi_scan_handler *handler; int error; lock_device_hotplug(); @@ -326,12 +316,32 @@ void acpi_device_hotplug(void *data, u32 src) if (adev->handle == INVALID_ACPI_HANDLE) goto out; + handler = adev->handler; + switch (src) { case ACPI_NOTIFY_BUS_CHECK: - error = acpi_bus_scan(adev->handle); + if (handler) { + error = handler->hotplug.scan_dependent ? + handler->hotplug.scan_dependent(adev) : + acpi_bus_scan(adev->handle); + } else { + error = acpi_scan_device_check(adev); + } break; case ACPI_NOTIFY_DEVICE_CHECK: - error = acpi_scan_device_check(adev); + /* + * This code is only run for device objects for which matching + * scan handlers exist. The only situation in which the scan + * handler is not attached to this device object yet is when the + * device has just appeared (either it wasn't present at all + * before or it was removed and then added again). + */ + if (adev->handler) { + dev_warn(&adev->dev, "Already enumerated\n"); + error = -EBUSY; + } else { + error = acpi_scan_device_check(adev); + } break; case ACPI_NOTIFY_EJECT_REQUEST: case ACPI_OST_EC_OSPM_EJECT: @@ -1805,7 +1815,7 @@ static void acpi_scan_init_hotplug(acpi_handle handle, int type) */ list_for_each_entry(hwid, &pnp.ids, list) { handler = acpi_scan_match_handler(hwid->id, NULL); - if (handler && !handler->hotplug.ignore) { + if (handler) { acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, acpi_hotplug_notify_cb, handler); break; @@ -2083,8 +2093,6 @@ int __init acpi_scan_init(void) acpi_update_all_gpes(); - acpi_pci_root_hp_init(); - out: mutex_unlock(&acpi_scan_lock); return result; -- cgit v1.2.3 From 46394fd017c0615982a3d29d45ced14bea9c526d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:55:32 +0100 Subject: ACPI / hotplug: Move container-specific code out of the core Move container-specific uevents from the core hotplug code to the container scan handler's .attach() and .detach() callbacks. This way the core will not have to special-case containers and the uevents will be guaranteed to happen every time a container is either scanned or trimmed as appropriate. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/container.c | 11 ++++++++--- drivers/acpi/scan.c | 8 +------- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index e23151667655..83d232c10f13 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -44,19 +44,24 @@ static const struct acpi_device_id container_device_ids[] = { {"", 0}, }; -static int container_device_attach(struct acpi_device *device, +static int container_device_attach(struct acpi_device *adev, const struct acpi_device_id *not_used) { - /* This is necessary for container hotplug to work. */ + kobject_uevent(&adev->dev.kobj, KOBJ_ONLINE); return 1; } +static void container_device_detach(struct acpi_device *adev) +{ + kobject_uevent(&adev->dev.kobj, KOBJ_OFFLINE); +} + static struct acpi_scan_handler container_handler = { .ids = container_device_ids, .attach = container_device_attach, + .detach = container_device_detach, .hotplug = { .enabled = true, - .mode = AHM_CONTAINER, }, }; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 18865c86c463..b1b8f4304597 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -206,9 +206,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_status status; unsigned long long sta; - if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) - kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - /* * Carry out two passes here and ignore errors in the first pass, * because if the devices in question are memory blocks and @@ -288,10 +285,7 @@ static int acpi_scan_device_check(struct acpi_device *adev) dev_warn(&adev->dev, "Namespace scan failure\n"); return error; } - if (adev->handler) { - if (adev->handler->hotplug.mode == AHM_CONTAINER) - kobject_uevent(&adev->dev.kobj, KOBJ_ONLINE); - } else { + if (!adev->handler) { dev_warn(&adev->dev, "Enumeration failure\n"); return -ENODEV; } -- cgit v1.2.3 From 443fc8202272190c4693209b772edba46cd7fe61 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:55:43 +0100 Subject: ACPI / hotplug: Rework generic code to handle suprise removals The generic ACPI hotplug code used for several types of device doesn't handle surprise removals, mostly because those devices currently cannot be removed by surprise in the majority of systems. However, surprise removals should be handled by that code as well as surprise additions of devices, so make it do that. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/scan.c | 85 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b1b8f4304597..06db2c036085 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -276,18 +276,72 @@ static int acpi_scan_hot_remove(struct acpi_device *device) return 0; } +static int acpi_scan_device_not_present(struct acpi_device *adev) +{ + if (!acpi_device_enumerated(adev)) { + dev_warn(&adev->dev, "Still not present\n"); + return -EALREADY; + } + acpi_bus_trim(adev); + return 0; +} + static int acpi_scan_device_check(struct acpi_device *adev) { int error; + acpi_bus_get_status(adev); + if (adev->status.present || adev->status.functional) { + /* + * This function is only called for device objects for which + * matching scan handlers exist. The only situation in which + * the scan handler is not attached to this device object yet + * is when the device has just appeared (either it wasn't + * present at all before or it was removed and then added + * again). + */ + if (adev->handler) { + dev_warn(&adev->dev, "Already enumerated\n"); + return -EALREADY; + } + error = acpi_bus_scan(adev->handle); + if (error) { + dev_warn(&adev->dev, "Namespace scan failure\n"); + return error; + } + if (!adev->handler) { + dev_warn(&adev->dev, "Enumeration failure\n"); + error = -ENODEV; + } + } else { + error = acpi_scan_device_not_present(adev); + } + return error; +} + +static int acpi_scan_bus_check(struct acpi_device *adev) +{ + struct acpi_scan_handler *handler = adev->handler; + struct acpi_device *child; + int error; + + acpi_bus_get_status(adev); + if (!(adev->status.present || adev->status.functional)) { + acpi_scan_device_not_present(adev); + return 0; + } + if (handler && handler->hotplug.scan_dependent) + return handler->hotplug.scan_dependent(adev); + error = acpi_bus_scan(adev->handle); if (error) { dev_warn(&adev->dev, "Namespace scan failure\n"); return error; } - if (!adev->handler) { - dev_warn(&adev->dev, "Enumeration failure\n"); - return -ENODEV; + list_for_each_entry(child, &adev->children, node) { + error = acpi_scan_bus_check(child); + if (error) + return error; } return 0; } @@ -296,7 +350,6 @@ static void acpi_device_hotplug(void *data, u32 src) { u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; struct acpi_device *adev = data; - struct acpi_scan_handler *handler; int error; lock_device_hotplug(); @@ -310,32 +363,12 @@ static void acpi_device_hotplug(void *data, u32 src) if (adev->handle == INVALID_ACPI_HANDLE) goto out; - handler = adev->handler; - switch (src) { case ACPI_NOTIFY_BUS_CHECK: - if (handler) { - error = handler->hotplug.scan_dependent ? - handler->hotplug.scan_dependent(adev) : - acpi_bus_scan(adev->handle); - } else { - error = acpi_scan_device_check(adev); - } + error = acpi_scan_bus_check(adev); break; case ACPI_NOTIFY_DEVICE_CHECK: - /* - * This code is only run for device objects for which matching - * scan handlers exist. The only situation in which the scan - * handler is not attached to this device object yet is when the - * device has just appeared (either it wasn't present at all - * before or it was removed and then added again). - */ - if (adev->handler) { - dev_warn(&adev->dev, "Already enumerated\n"); - error = -EBUSY; - } else { - error = acpi_scan_device_check(adev); - } + error = acpi_scan_device_check(adev); break; case ACPI_NOTIFY_EJECT_REQUEST: case ACPI_OST_EC_OSPM_EJECT: -- cgit v1.2.3 From f4b734c35e98c343cd99824153de09e48867b97e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:55:55 +0100 Subject: ACPI / hotplug: Drop unfinished global notification handling routines There are two global hotplug notification handling routines in bus.c, acpi_bus_check_device() and acpi_bus_check_scope(), that have never been finished and don't do anything useful, so drop them. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 64 ++---------------------------------------------------- 1 file changed, 2 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index bba9b72e25f8..8b09a75f6579 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -329,58 +329,6 @@ static void acpi_bus_osc_support(void) Notification Handling -------------------------------------------------------------------------- */ -static void acpi_bus_check_device(acpi_handle handle) -{ - struct acpi_device *device; - acpi_status status; - struct acpi_device_status old_status; - - if (acpi_bus_get_device(handle, &device)) - return; - if (!device) - return; - - old_status = device->status; - - /* - * Make sure this device's parent is present before we go about - * messing with the device. - */ - if (device->parent && !device->parent->status.present) { - device->status = device->parent->status; - return; - } - - status = acpi_bus_get_status(device); - if (ACPI_FAILURE(status)) - return; - - if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status)) - return; - - /* - * Device Insertion/Removal - */ - if ((device->status.present) && !(old_status.present)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device insertion detected\n")); - /* TBD: Handle device insertion */ - } else if (!(device->status.present) && (old_status.present)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n")); - /* TBD: Handle device removal */ - } -} - -static void acpi_bus_check_scope(acpi_handle handle) -{ - /* Status Change? */ - acpi_bus_check_device(handle); - - /* - * TBD: Enumerate child devices within this device's scope and - * run acpi_bus_check_device()'s on them. - */ -} - /** * acpi_bus_notify * --------------- @@ -397,19 +345,11 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) switch (type) { case ACPI_NOTIFY_BUS_CHECK: - acpi_bus_check_scope(handle); - /* - * TBD: We'll need to outsource certain events to non-ACPI - * drivers via the device manager (device.c). - */ + /* TBD */ break; case ACPI_NOTIFY_DEVICE_CHECK: - acpi_bus_check_device(handle); - /* - * TBD: We'll need to outsource certain events to non-ACPI - * drivers via the device manager (device.c). - */ + /* TBD */ break; case ACPI_NOTIFY_DEVICE_WAKE: -- cgit v1.2.3 From 25db115b0bf72acfdf8a339fa8e37d8b895214d6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Nov 2013 21:56:06 +0100 Subject: ACPI: Introduce acpi_set_device_status() Introduce a static inline function for setting the status field of struct acpi_device on the basis of a supplied u32 number, acpi_set_device_status(), and use it instead of the horrible horrible STRUCT_TO_INT() macro wherever applicable. Having done that, drop STRUCT_TO_INT() (and pretend that it has never existed). Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/bus.c | 11 +++-------- drivers/acpi/scan.c | 5 ++--- 2 files changed, 5 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 8b09a75f6579..f509019ee2c7 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -52,9 +52,6 @@ struct acpi_device *acpi_root; struct proc_dir_entry *acpi_root_dir; EXPORT_SYMBOL(acpi_root_dir); -#define STRUCT_TO_INT(s) (*((int*)&s)) - - #ifdef CONFIG_X86 static int set_copy_dsdt(const struct dmi_system_id *id) { @@ -115,18 +112,16 @@ int acpi_bus_get_status(struct acpi_device *device) if (ACPI_FAILURE(status)) return -ENODEV; - STRUCT_TO_INT(device->status) = (int) sta; + acpi_set_device_status(device, sta); if (device->status.functional && !device->status.present) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: " "functional but not present;\n", - device->pnp.bus_id, - (u32) STRUCT_TO_INT(device->status))); + device->pnp.bus_id, (u32)sta)); } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n", - device->pnp.bus_id, - (u32) STRUCT_TO_INT(device->status))); + device->pnp.bus_id, (u32)sta)); return 0; } EXPORT_SYMBOL(acpi_bus_get_status); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 06db2c036085..cf773c918102 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -20,7 +20,6 @@ #define _COMPONENT ACPI_BUS_COMPONENT ACPI_MODULE_NAME("scan"); -#define STRUCT_TO_INT(s) (*((int*)&s)) extern struct acpi_device *acpi_root; #define ACPI_BUS_CLASS "system_bus" @@ -1683,7 +1682,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, device->device_type = type; device->handle = handle; device->parent = acpi_bus_get_parent(handle); - STRUCT_TO_INT(device->status) = sta; + acpi_set_device_status(device, sta); acpi_device_get_busid(device); acpi_set_pnp_ids(handle, &device->pnp, type); acpi_bus_get_flags(device); @@ -1927,7 +1926,7 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, if (acpi_bus_get_device(handle, &device)) return AE_CTRL_DEPTH; - STRUCT_TO_INT(device->status) = sta; + acpi_set_device_status(device, sta); /* Skip devices that are not present. */ if (!acpi_device_is_present(device)) goto err; -- cgit v1.2.3 From 2c22e6520ac87d8b12d4d9941e81d4119f2d903c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 25 Nov 2013 00:52:21 +0100 Subject: ACPI / scan: Use direct recurrence for device hierarchy walks Rework acpi_bus_trim() and acpi_bus_device_attach(), which is renamed as acpi_bus_attach(), to walk the list of each device object's children directly and call themselves recursively for each child instead of using acpi_walk_namespace(). This simplifies the code quite a bit and avoids the overhead of callbacks and the ACPICA's internal processing which are not really necessary for these two routines. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 120 ++++++++++++++++++++-------------------------------- 1 file changed, 45 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index cf773c918102..311904c720b1 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1909,54 +1909,40 @@ static int acpi_scan_attach_handler(struct acpi_device *device) return ret; } -static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, - void *not_used, void **ret_not_used) +static void acpi_bus_attach(struct acpi_device *device) { - struct acpi_device *device; - unsigned long long sta; + struct acpi_device *child; 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)) - return AE_OK; - - if (acpi_bus_get_device(handle, &device)) - return AE_CTRL_DEPTH; - - acpi_set_device_status(device, sta); + acpi_bus_get_status(device); /* Skip devices that are not present. */ - if (!acpi_device_is_present(device)) - goto err; - + if (!acpi_device_is_present(device)) { + device->flags.visited = false; + return; + } if (device->handler) - return AE_OK; + goto ok; if (!device->flags.initialized) { acpi_bus_update_power(device, NULL); device->flags.initialized = true; } + device->flags.visited = false; ret = acpi_scan_attach_handler(device); if (ret < 0) - goto err; + return; device->flags.match_driver = true; - if (ret > 0) - goto ok; - - ret = device_attach(&device->dev); - if (ret < 0) - goto err; - - ok: + if (!ret) { + ret = device_attach(&device->dev); + if (ret < 0) + return; + } device->flags.visited = true; - return AE_OK; - err: - device->flags.visited = false; - return AE_CTRL_DEPTH; + ok: + list_for_each_entry(child, &device->children, node) + acpi_bus_attach(child); } /** @@ -1976,64 +1962,48 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, int acpi_bus_scan(acpi_handle handle) { void *device = NULL; - int error = 0; if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_check_add, NULL, NULL, &device); - if (!device) - error = -ENODEV; - else if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) - acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, - acpi_bus_device_attach, NULL, NULL, NULL); - - return error; -} -EXPORT_SYMBOL(acpi_bus_scan); - -static acpi_status acpi_bus_device_detach(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)) { - struct acpi_scan_handler *dev_handler = device->handler; - - if (dev_handler) { - if (dev_handler->detach) - dev_handler->detach(device); - - device->handler = NULL; - } 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; + if (device) { + acpi_bus_attach(device); + return 0; } - return AE_OK; + return -ENODEV; } +EXPORT_SYMBOL(acpi_bus_scan); /** - * acpi_bus_trim - Remove ACPI device node and all of its descendants - * @start: Root of the ACPI device nodes subtree to remove. + * acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects. + * @adev: Root of the ACPI namespace scope to walk. * * Must be called under acpi_scan_lock. */ -void acpi_bus_trim(struct acpi_device *start) +void acpi_bus_trim(struct acpi_device *adev) { + struct acpi_scan_handler *handler = adev->handler; + struct acpi_device *child; + + list_for_each_entry_reverse(child, &adev->children, node) + acpi_bus_trim(child); + + if (handler) { + if (handler->detach) + handler->detach(adev); + + adev->handler = NULL; + } else { + device_release_driver(&adev->dev); + } /* - * Execute acpi_bus_device_detach() as a post-order callback to detach - * all ACPI drivers from the device nodes being removed. + * Most likely, the device is going away, so put it into D3cold before + * that. */ - 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); + acpi_device_set_power(adev, ACPI_STATE_D3_COLD); + adev->flags.initialized = false; + adev->flags.visited = false; } EXPORT_SYMBOL_GPL(acpi_bus_trim); -- cgit v1.2.3 From 2209b0c964c7c402b07d0df6fcfbecf8ff53db30 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 25 Nov 2013 18:01:18 -0800 Subject: cpufreq: cpufreq-cpu0: clk_round_rate() can return a zero upon error Treat both negative and zero return values from clk_round_rate() as errors. This is needed since subsequent patches will convert clk_round_rate()'s return value to be an unsigned type, rather than a signed type, since some clock sources can generate rates higher than (2^31)-1 Hz. Eventually, when calling clk_round_rate(), only a return value of zero will be considered a error. All other values will be considered valid rates. The comparison against values less than 0 is kept to preserve the correct behavior in the meantime. Signed-off-by: Paul Walmsley Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index d4585ce2346c..0faf756f6197 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -44,7 +44,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) int ret; freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); - if (fre