From: Rafael J. Wysocki Date: Thu, 7 Nov 2013 00:45:40 +0000 (+0100) Subject: ACPI / hotplug: Consolidate deferred execution of ACPI hotplug routines X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=7b98118aaa5d75644c48f41fc5d0cc181e478383;p=GitHub%2Fexynos8895%2Fandroid_kernel_samsung_universal8895.git ACPI / hotplug: Consolidate deferred execution of ACPI hotplug routines There are two different interfaces for queuing up work items on the ACPI hotplug workqueue, alloc_acpi_hp_work() used by PCI and PCI host bridge hotplug code and acpi_os_hotplug_execute() used by the common ACPI hotplug code and docking stations. They both are somewhat cumbersome to use and work slightly differently. The users of alloc_acpi_hp_work() have to submit a work function that will extract the necessary data items from a struct acpi_hp_work object allocated by alloc_acpi_hp_work() and then will free that object, while it would be more straightforward to simply use a work function with one more argument and let the interface take care of the execution details. The users of acpi_os_hotplug_execute() also have to deal with the fact that it takes only one argument in addition to the work function pointer, although acpi_os_execute_deferred() actually takes care of the allocation and freeing of memory, so it would have been able to pass more arguments to the work function if it hadn't been constrained by the connection with acpi_os_execute(). Moreover, while alloc_acpi_hp_work() makes GFP_KERNEL memory allocations, which is correct, because hotplug work items are always queued up from process context, acpi_os_hotplug_execute() uses GFP_ATOMIC, as that is needed by acpi_os_execute(). Also, acpi_os_execute_deferred() queued up by it waits for the ACPI event workqueues to flush before executing the work function, whereas alloc_acpi_hp_work() can't do anything similar. That leads to somewhat arbitrary differences in behavior between various ACPI hotplug code paths and has to be straightened up. For this reason, replace both alloc_acpi_hp_work() and acpi_os_hotplug_execute() with a single interface, acpi_hotplug_execute(), combining their behavior and being more friendly to its users than any of the two. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 05ea4be01a83..eab7d1145bfa 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -671,39 +671,20 @@ static void dock_notify(struct dock_station *ds, u32 event) } } -struct dock_data { - struct dock_station *ds; - u32 event; -}; - -static void acpi_dock_deferred_cb(void *context) +static void acpi_dock_deferred_cb(void *data, u32 event) { - struct dock_data *data = context; - acpi_scan_lock_acquire(); - dock_notify(data->ds, data->event); + dock_notify(data, event); acpi_scan_lock_release(); - kfree(data); } static void dock_notify_handler(acpi_handle handle, u32 event, void *data) { - struct dock_data *dd; - if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK && event != ACPI_NOTIFY_EJECT_REQUEST) return; - dd = kmalloc(sizeof(*dd), GFP_KERNEL); - if (dd) { - acpi_status status; - - dd->ds = data; - dd->event = event; - status = acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd); - if (ACPI_FAILURE(status)) - kfree(dd); - } + acpi_hotplug_execute(acpi_dock_deferred_cb, data, event); } /** diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 80cd1a10c4c3..c6db680c826c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -87,7 +87,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(struct acpi_device *device, u32 ost_src); +void acpi_bus_device_eject(void *data, u32 ost_src); /* -------------------------------------------------------------------------- Power Resource diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index e5f416c7f66e..cfc3e260a688 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -61,7 +61,6 @@ struct acpi_os_dpc { acpi_osd_exec_callback function; void *context; struct work_struct work; - int wait; }; #ifdef CONFIG_ACPI_CUSTOM_DSDT @@ -1067,9 +1066,6 @@ static void acpi_os_execute_deferred(struct work_struct *work) { struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); - if (dpc->wait) - acpi_os_wait_events_complete(); - dpc->function(dpc->context); kfree(dpc); } @@ -1089,8 +1085,8 @@ static void acpi_os_execute_deferred(struct work_struct *work) * ******************************************************************************/ -static acpi_status __acpi_os_execute(acpi_execute_type type, - acpi_osd_exec_callback function, void *context, int hp) +acpi_status acpi_os_execute(acpi_execute_type type, + acpi_osd_exec_callback function, void *context) { acpi_status status = AE_OK; struct acpi_os_dpc *dpc; @@ -1117,20 +1113,11 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, dpc->context = context; /* - * We can't run hotplug code in keventd_wq/kacpid_wq/kacpid_notify_wq - * because the hotplug code may call driver .remove() functions, - * which invoke flush_scheduled_work/acpi_os_wait_events_complete - * to flush these workqueues. - * * To prevent lockdep from complaining unnecessarily, make sure that * there is a different static lockdep key for each workqueue by using * INIT_WORK() for each of them separately. */ - if (hp) { - queue = kacpi_hotplug_wq; - dpc->wait = 1; - INIT_WORK(&dpc->work, acpi_os_execute_deferred); - } else if (type == OSL_NOTIFY_HANDLER) { + if (type == OSL_NOTIFY_HANDLER) { queue = kacpi_notify_wq; INIT_WORK(&dpc->work, acpi_os_execute_deferred); } else { @@ -1155,28 +1142,59 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, } return status; } +EXPORT_SYMBOL(acpi_os_execute); -acpi_status acpi_os_execute(acpi_execute_type type, - acpi_osd_exec_callback function, void *context) +void acpi_os_wait_events_complete(void) { - return __acpi_os_execute(type, function, context, 0); + flush_workqueue(kacpid_wq); + flush_workqueue(kacpi_notify_wq); } -EXPORT_SYMBOL(acpi_os_execute); -acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function, - void *context) +struct acpi_hp_work { + struct work_struct work; + acpi_hp_callback func; + void *data; + u32 src; +}; + +static void acpi_hotplug_work_fn(struct work_struct *work) { - return __acpi_os_execute(0, function, context, 1); + struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work); + + acpi_os_wait_events_complete(); + hpw->func(hpw->data, hpw->src); + kfree(hpw); } -EXPORT_SYMBOL(acpi_os_hotplug_execute); -void acpi_os_wait_events_complete(void) +acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src) { - flush_workqueue(kacpid_wq); - flush_workqueue(kacpi_notify_wq); + struct acpi_hp_work *hpw; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Scheduling function [%p(%p, %u)] for deferred execution.\n", + func, data, src)); + + hpw = kmalloc(sizeof(*hpw), GFP_KERNEL); + if (!hpw) + return AE_NO_MEMORY; + + INIT_WORK(&hpw->work, acpi_hotplug_work_fn); + hpw->func = func; + hpw->data = data; + hpw->src = src; + /* + * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because + * the hotplug code may call driver .remove() functions, which may + * invoke flush_scheduled_work()/acpi_os_wait_events_complete() to flush + * these workqueues. + */ + if (!queue_work(kacpi_hotplug_wq, &hpw->work)) { + kfree(hpw); + return AE_ERROR; + } + return AE_OK; } -EXPORT_SYMBOL(acpi_os_wait_events_complete); acpi_status acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle) @@ -1825,25 +1843,3 @@ void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state, { __acpi_os_prepare_extended_sleep = func; } - - -void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, - void (*func)(struct work_struct *work)) -{ - struct acpi_hp_work *hp_work; - int ret; - - hp_work = kmalloc(sizeof(*hp_work), GFP_KERNEL); - if (!hp_work) - return; - - hp_work->handle = handle; - hp_work->type = type; - hp_work->context = context; - - INIT_WORK(&hp_work->work, func); - ret = queue_work(kacpi_hotplug_wq, &hp_work->work); - if (!ret) - kfree(hp_work); -} -EXPORT_SYMBOL_GPL(alloc_acpi_hp_work); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index d77f0bf7eda0..417876bce854 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -592,17 +592,10 @@ static void handle_root_bridge_insertion(acpi_handle handle) acpi_handle_err(handle, "cannot add bridge to acpi list\n"); } -static void _handle_hotplug_event_root(struct work_struct *work) +static void hotplug_event_root(void *data, u32 type) { + acpi_handle handle = data; struct acpi_pci_root *root; - struct acpi_hp_work *hp_work; - acpi_handle handle; - u32 type; - - hp_work = container_of(work, struct acpi_hp_work, work); - handle = hp_work->handle; - type = hp_work->type; - kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ acpi_scan_lock_acquire(); @@ -654,8 +647,7 @@ static void _handle_hotplug_event_root(struct work_struct *work) static void handle_hotplug_event_root(acpi_handle handle, u32 type, void *context) { - alloc_acpi_hp_work(handle, type, context, - _handle_hotplug_event_root); + acpi_hotplug_execute(hotplug_event_root, handle, type); } static acpi_status __init diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5cd7f8c5666a..276cde70a514 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -285,8 +285,9 @@ static int acpi_scan_hot_remove(struct acpi_device *device) return 0; } -void acpi_bus_device_eject(struct acpi_device *device, u32 ost_src) +void acpi_bus_device_eject(void *data, u32 ost_src) { + struct acpi_device *device = data; acpi_handle handle = device->handle; struct acpi_scan_handler *handler; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; @@ -327,8 +328,9 @@ void acpi_bus_device_eject(struct acpi_device *device, u32 ost_src) goto out; } -static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) +static void acpi_scan_bus_device_check(void *data, u32 ost_source) { + acpi_handle handle = data; struct acpi_device *device = NULL; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; int error; @@ -363,18 +365,6 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) unlock_device_hotplug(); } -static void acpi_scan_bus_check(void *context) -{ - acpi_scan_bus_device_check((acpi_handle)context, - ACPI_NOTIFY_BUS_CHECK); -} - -static void acpi_scan_device_check(void *context) -{ - acpi_scan_bus_device_check((acpi_handle)context, - ACPI_NOTIFY_DEVICE_CHECK); -} - static void acpi_hotplug_unsupported(acpi_handle handle, u32 type) { u32 ost_status; @@ -403,18 +393,8 @@ static void acpi_hotplug_unsupported(acpi_handle handle, u32 type) acpi_evaluate_hotplug_ost(handle, type, ost_status, NULL); } -/** - * acpi_bus_hot_remove_device: Hot-remove a device and its children. - * @context: Address of the ACPI device object to hot-remove. - */ -static void acpi_bus_hot_remove_device(void *context) -{ - acpi_bus_device_eject(context, ACPI_NOTIFY_EJECT_REQUEST); -} - static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) { - acpi_osd_exec_callback callback; struct acpi_scan_handler *handler = data; struct acpi_device *adev; acpi_status status; @@ -425,11 +405,9 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) switch (type) { case ACPI_NOTIFY_BUS_CHECK: acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); - callback = acpi_scan_bus_check; break; case ACPI_NOTIFY_DEVICE_CHECK: acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); - callback = acpi_scan_device_check; break; case ACPI_NOTIFY_EJECT_REQUEST: acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); @@ -438,8 +416,7 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) goto err_out; get_device(&adev->dev); - callback = acpi_bus_hot_remove_device; - status = acpi_os_hotplug_execute(callback, adev); + status = acpi_hotplug_execute(acpi_bus_device_eject, adev, type); if (ACPI_SUCCESS(status)) return; @@ -449,7 +426,7 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) /* non-hotplug event; possibly handled by other handler */ return; } - status = acpi_os_hotplug_execute(callback, handle); + status = acpi_hotplug_execute(acpi_scan_bus_device_check, handle, type); if (ACPI_SUCCESS(status)) return; @@ -484,11 +461,6 @@ static ssize_t power_state_show(struct device *dev, static DEVICE_ATTR(power_state, 0444, power_state_show, NULL); -static void acpi_eject_store_work(void *context) -{ - acpi_bus_device_eject(context, ACPI_OST_EC_OSPM_EJECT); -} - static ssize_t acpi_eject_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -511,7 +483,8 @@ 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_os_hotplug_execute(acpi_eject_store_work, acpi_device); + status = acpi_hotplug_execute(acpi_bus_device_eject, acpi_device, + ACPI_OST_EC_OSPM_EJECT); if (ACPI_SUCCESS(status)) return count; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 0b7d23b4ad95..5246ba297470 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -875,21 +875,17 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) put_bridge(bridge); } -static void hotplug_event_work(struct work_struct *work) +static void hotplug_event_work(void *data, u32 type) { - struct acpiphp_context *context; - struct acpi_hp_work *hp_work; + struct acpiphp_context *context = data; + acpi_handle handle = context->handle; - hp_work = container_of(work, struct acpi_hp_work, work); - context = hp_work->context; acpi_scan_lock_acquire(); - hotplug_event(hp_work->handle, hp_work->type, context); + hotplug_event(handle, type, context); acpi_scan_lock_release(); - acpi_evaluate_hotplug_ost(hp_work->handle, hp_work->type, - ACPI_OST_SC_SUCCESS, NULL); - kfree(hp_work); /* allocated in handle_hotplug_event() */ + acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL); put_bridge(context->func.parent); } @@ -940,10 +936,10 @@ static void handle_hotplug_event(acpi_handle handle, u32 type, void *data) mutex_lock(&acpiphp_context_lock); context = acpiphp_get_context(handle); - if (context) { + if (context && !WARN_ON(context->handle != handle)) { get_bridge(context->func.parent); acpiphp_put_context(context); - alloc_acpi_hp_work(handle, type, context, hotplug_event_work); + acpi_hotplug_execute(hotplug_event_work, context, type); mutex_unlock(&acpiphp_context_lock); return; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index eb37a6fb8665..d64a3010332f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -339,15 +339,6 @@ struct acpi_bus_event { u32 data; }; -struct acpi_hp_work { - struct work_struct work; - acpi_handle handle; - u32 type; - void *context; -}; -void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, - void (*func)(struct work_struct *work)); - extern struct kobject *acpi_kobj; extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); void acpi_bus_private_data_handler(acpi_handle, void *); @@ -393,6 +384,9 @@ int acpi_match_device_ids(struct acpi_device *device, int acpi_create_dir(struct acpi_device *); void acpi_remove_dir(struct acpi_device *); +typedef void (*acpi_hp_callback)(void *data, u32 src); + +acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src); /** * module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 64b8c7639520..505cb4d59f23 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -200,9 +200,6 @@ acpi_status acpi_os_execute(acpi_execute_type type, acpi_osd_exec_callback function, void *context); -acpi_status -acpi_os_hotplug_execute(acpi_osd_exec_callback function, void *context); - void acpi_os_wait_events_complete(void); void acpi_os_sleep(u64 milliseconds);