--- /dev/null
+From: Shaohua Li <shaohua.li@intel.com>
+Subject: fix hotplug race
+Patch-mainline: submitted 2008-08-28
+References: fate#304731,bnc#401740
+
+hotplug notification handler and drivers' notification handler are all
+running in one workqueue. Before hotplug removes an acpi device, the
+device driver's notification handler is already be recorded to run just
+after global notification handler. After hotplug notification handler
+runs, acpica will notice a NULL notification handler and crash. This
+patch runs hotplug in other workqueue and wait for all acpi notication
+handlers finish. This is found in battery hotplug, but actually all
+hotplug can be affected.
+
+Signed-off-by: Zhang Rui <rui.zhang@intel.com>
+Signed-off-by: Shaohua Li <shaohua.li@intel.com>
+Signed-off-by: Holger Macht <hmacht@suse.de>
+---
+
+---
+ drivers/acpi/dock.c | 25 ++++++++++++++++++++++++-
+ drivers/acpi/osl.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
+ include/acpi/acpiosxf.h | 3 +++
+ 3 files changed, 68 insertions(+), 6 deletions(-)
+
+--- a/drivers/acpi/dock.c
++++ b/drivers/acpi/dock.c
+@@ -748,6 +748,20 @@ static void dock_notify(acpi_handle hand
+ }
+ }
+
++struct dock_data {
++ acpi_handle handle;
++ unsigned long event;
++ struct dock_station *ds;
++};
++
++static void acpi_dock_deferred_cb(void *context)
++{
++ struct dock_data *data = (struct dock_data *)context;
++
++ dock_notify(data->handle, data->event, data->ds);
++ kfree(data);
++}
++
+ static int acpi_dock_notifier_call(struct notifier_block *this,
+ unsigned long event, void *data)
+ {
+@@ -759,7 +773,16 @@ static int acpi_dock_notifier_call(struc
+ return 0;
+ list_for_each_entry(dock_station, &dock_stations, sibiling) {
+ if (dock_station->handle == handle) {
+- dock_notify(handle, event, dock_station);
++ struct dock_data *dock_data;
++
++ dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL);
++ if (!dock_data)
++ return 0;
++ dock_data->handle = handle;
++ dock_data->event = event;
++ dock_data->ds = dock_station;
++ acpi_os_hotplug_execute(acpi_dock_deferred_cb,
++ dock_data);
+ return 0 ;
+ }
+ }
+--- a/drivers/acpi/osl.c
++++ b/drivers/acpi/osl.c
+@@ -705,6 +705,22 @@ static void acpi_os_execute_deferred(str
+ return;
+ }
+
++static void acpi_os_execute_hp_deferred(struct work_struct *work)
++{
++ struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
++ if (!dpc) {
++ printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
++ return;
++ }
++
++ acpi_os_wait_events_complete(NULL);
++
++ dpc->function(dpc->context);
++ kfree(dpc);
++
++ return;
++}
++
+ /*******************************************************************************
+ *
+ * FUNCTION: acpi_os_execute
+@@ -720,12 +736,13 @@ static void acpi_os_execute_deferred(str
+ *
+ ******************************************************************************/
+
+-acpi_status acpi_os_execute(acpi_execute_type type,
+- acpi_osd_exec_callback function, void *context)
++static acpi_status __acpi_os_execute(acpi_execute_type type,
++ acpi_osd_exec_callback function, void *context, int hp)
+ {
+ acpi_status status = AE_OK;
+ struct acpi_os_dpc *dpc;
+ struct workqueue_struct *queue;
++ int ret;
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Scheduling function [%p(%p)] for deferred execution.\n",
+ function, context));
+@@ -749,9 +766,17 @@ acpi_status acpi_os_execute(acpi_execute
+ dpc->function = function;
+ dpc->context = context;
+
+- INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+- queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq;
+- if (!queue_work(queue, &dpc->work)) {
++ if (!hp) {
++ INIT_WORK(&dpc->work, acpi_os_execute_deferred);
++ queue = (type == OSL_NOTIFY_HANDLER) ?
++ kacpi_notify_wq : kacpid_wq;
++ ret = queue_work(queue, &dpc->work);
++ } else {
++ INIT_WORK(&dpc->work, acpi_os_execute_hp_deferred);
++ ret = schedule_work(&dpc->work);
++ }
++
++ if (!ret) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Call to queue_work() failed.\n"));
+ status = AE_ERROR;
+@@ -760,8 +785,19 @@ acpi_status acpi_os_execute(acpi_execute
+ return_ACPI_STATUS(status);
+ }
+
++acpi_status acpi_os_execute(acpi_execute_type type,
++ acpi_osd_exec_callback function, void *context)
++{
++ return __acpi_os_execute(type, function, context, 0);
++}
+ EXPORT_SYMBOL(acpi_os_execute);
+
++acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function,
++ void *context)
++{
++ return __acpi_os_execute(0, function, context, 1);
++}
++
+ void acpi_os_wait_events_complete(void *context)
+ {
+ flush_workqueue(kacpid_wq);
+--- a/include/acpi/acpiosxf.h
++++ b/include/acpi/acpiosxf.h
+@@ -193,6 +193,9 @@ 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 *context);
+
+ void acpi_os_sleep(acpi_integer milliseconds);