Commit b8d35192 authored by Alexey Starikovskiy's avatar Alexey Starikovskiy Committed by Len Brown

ACPI: execute Notify() handlers on new thread

http://bugzilla.kernel.org/show_bug.cgi?id=5534

Thanks to Peter Wainwright for isolating the issue.
Thanks to Andi Kleen and Bob Moore for feedback.
Thanks to Richard Mace and others for testing.
Updates by Konstantin Karasyov.
Signed-off-by: default avatarKonstantin Karasyov <konstantin.a.karasyov@intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 958dd242
...@@ -763,8 +763,7 @@ static u32 acpi_ec_gpe_poll_handler(void *data) ...@@ -763,8 +763,7 @@ static u32 acpi_ec_gpe_poll_handler(void *data)
acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);
status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, status = acpi_os_execute(OSL_EC_POLL_HANDLER, acpi_ec_gpe_query, ec);
acpi_ec_gpe_query, ec);
if (status == AE_OK) if (status == AE_OK)
return ACPI_INTERRUPT_HANDLED; return ACPI_INTERRUPT_HANDLED;
...@@ -799,7 +798,7 @@ static u32 acpi_ec_gpe_intr_handler(void *data) ...@@ -799,7 +798,7 @@ static u32 acpi_ec_gpe_intr_handler(void *data)
if (value & ACPI_EC_FLAG_SCI) { if (value & ACPI_EC_FLAG_SCI) {
atomic_add(1, &ec->intr.pending_gpe); atomic_add(1, &ec->intr.pending_gpe);
status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, status = acpi_os_execute(OSL_EC_BURST_HANDLER,
acpi_ec_gpe_query, ec); acpi_ec_gpe_query, ec);
return status == AE_OK ? return status == AE_OK ?
ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/kthread.h>
#include <acpi/acpi.h> #include <acpi/acpi.h>
#include <asm/io.h> #include <asm/io.h>
#include <acpi/acpi_bus.h> #include <acpi/acpi_bus.h>
...@@ -600,23 +601,41 @@ static void acpi_os_execute_deferred(void *context) ...@@ -600,23 +601,41 @@ static void acpi_os_execute_deferred(void *context)
return_VOID; return_VOID;
} }
acpi_status static int acpi_os_execute_thread(void *context)
acpi_os_queue_for_execution(u32 priority, {
struct acpi_os_dpc *dpc = (struct acpi_os_dpc *)context;
if (dpc) {
dpc->function(dpc->context);
kfree(dpc);
}
do_exit(0);
}
/*******************************************************************************
*
* FUNCTION: acpi_os_execute
*
* PARAMETERS: Type - Type of the callback
* Function - Function to be executed
* Context - Function parameters
*
* RETURN: Status
*
* DESCRIPTION: Depending on type, either queues function for deferred execution or
* immediately executes function on a separate thread.
*
******************************************************************************/
acpi_status acpi_os_execute(acpi_execute_type type,
acpi_osd_exec_callback function, void *context) acpi_osd_exec_callback function, void *context)
{ {
acpi_status status = AE_OK; acpi_status status = AE_OK;
struct acpi_os_dpc *dpc; struct acpi_os_dpc *dpc;
struct work_struct *task; struct work_struct *task;
struct task_struct *p;
ACPI_FUNCTION_TRACE("os_queue_for_execution");
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));
if (!function) if (!function)
return_ACPI_STATUS(AE_BAD_PARAMETER); return AE_BAD_PARAMETER;
/* /*
* Allocate/initialize DPC structure. Note that this memory will be * Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the tq_struct list in a * freed by the callee. The kernel handles the tq_struct list in a
...@@ -627,30 +646,37 @@ acpi_os_queue_for_execution(u32 priority, ...@@ -627,30 +646,37 @@ acpi_os_queue_for_execution(u32 priority,
* We can save time and code by allocating the DPC and tq_structs * We can save time and code by allocating the DPC and tq_structs
* from the same memory. * from the same memory.
*/ */
if (type == OSL_NOTIFY_HANDLER) {
dpc = dpc = kmalloc(sizeof(struct acpi_os_dpc), GFP_KERNEL);
kmalloc(sizeof(struct acpi_os_dpc) + sizeof(struct work_struct), } else {
GFP_ATOMIC); dpc = kmalloc(sizeof(struct acpi_os_dpc) +
sizeof(struct work_struct), GFP_ATOMIC);
}
if (!dpc) if (!dpc)
return_ACPI_STATUS(AE_NO_MEMORY); return AE_NO_MEMORY;
dpc->function = function; dpc->function = function;
dpc->context = context; dpc->context = context;
task = (void *)(dpc + 1); if (type == OSL_NOTIFY_HANDLER) {
INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc); p = kthread_create(acpi_os_execute_thread, dpc, "kacpid_notify");
if (!IS_ERR(p)) {
if (!queue_work(kacpid_wq, task)) { wake_up_process(p);
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, } else {
"Call to queue_work() failed.\n")); status = AE_NO_MEMORY;
kfree(dpc); kfree(dpc);
status = AE_ERROR; }
} else {
task = (void *)(dpc + 1);
INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc);
if (!queue_work(kacpid_wq, task)) {
status = AE_ERROR;
kfree(dpc);
}
} }
return status;
return_ACPI_STATUS(status);
} }
EXPORT_SYMBOL(acpi_os_queue_for_execution); EXPORT_SYMBOL(acpi_os_execute);
void acpi_os_wait_events_complete(void *context) void acpi_os_wait_events_complete(void *context)
{ {
......
...@@ -684,8 +684,7 @@ static void acpi_thermal_run(unsigned long data) ...@@ -684,8 +684,7 @@ static void acpi_thermal_run(unsigned long data)
{ {
struct acpi_thermal *tz = (struct acpi_thermal *)data; struct acpi_thermal *tz = (struct acpi_thermal *)data;
if (!tz->zombie) if (!tz->zombie)
acpi_os_queue_for_execution(OSD_PRIORITY_GPE, acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data);
acpi_thermal_check, (void *)data);
} }
static void acpi_thermal_check(void *data) static void acpi_thermal_check(void *data)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment