Skip to content

Commit

Permalink
ACPI: execute Notify() handlers on new thread
Browse files Browse the repository at this point in the history
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: Konstantin Karasyov <konstantin.a.karasyov@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Alexey Starikovskiy authored and Len Brown committed Jun 14, 2006
1 parent 958dd24 commit b8d3519
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 32 deletions.
5 changes: 2 additions & 3 deletions drivers/acpi/ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -763,8 +763,7 @@ static u32 acpi_ec_gpe_poll_handler(void *data)

acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);

status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE,
acpi_ec_gpe_query, ec);
status = acpi_os_execute(OSL_EC_POLL_HANDLER, acpi_ec_gpe_query, ec);

if (status == AE_OK)
return ACPI_INTERRUPT_HANDLED;
Expand Down Expand Up @@ -799,7 +798,7 @@ static u32 acpi_ec_gpe_intr_handler(void *data)

if (value & ACPI_EC_FLAG_SCI) {
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);
return status == AE_OK ?
ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
Expand Down
80 changes: 53 additions & 27 deletions drivers/acpi/osl.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/nmi.h>
#include <linux/kthread.h>
#include <acpi/acpi.h>
#include <asm/io.h>
#include <acpi/acpi_bus.h>
Expand Down Expand Up @@ -600,23 +601,41 @@ static void acpi_os_execute_deferred(void *context)
return_VOID;
}

acpi_status
acpi_os_queue_for_execution(u32 priority,
static int acpi_os_execute_thread(void *context)
{
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_status status = AE_OK;
struct acpi_os_dpc *dpc;
struct work_struct *task;

ACPI_FUNCTION_TRACE("os_queue_for_execution");

ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));
struct task_struct *p;

if (!function)
return_ACPI_STATUS(AE_BAD_PARAMETER);

return AE_BAD_PARAMETER;
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the tq_struct list in a
Expand All @@ -627,30 +646,37 @@ acpi_os_queue_for_execution(u32 priority,
* We can save time and code by allocating the DPC and tq_structs
* from the same memory.
*/

dpc =
kmalloc(sizeof(struct acpi_os_dpc) + sizeof(struct work_struct),
GFP_ATOMIC);
if (type == OSL_NOTIFY_HANDLER) {
dpc = kmalloc(sizeof(struct acpi_os_dpc), GFP_KERNEL);
} else {
dpc = kmalloc(sizeof(struct acpi_os_dpc) +
sizeof(struct work_struct), GFP_ATOMIC);
}
if (!dpc)
return_ACPI_STATUS(AE_NO_MEMORY);

return AE_NO_MEMORY;
dpc->function = function;
dpc->context = context;

task = (void *)(dpc + 1);
INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc);

if (!queue_work(kacpid_wq, task)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Call to queue_work() failed.\n"));
kfree(dpc);
status = AE_ERROR;
if (type == OSL_NOTIFY_HANDLER) {
p = kthread_create(acpi_os_execute_thread, dpc, "kacpid_notify");
if (!IS_ERR(p)) {
wake_up_process(p);
} else {
status = AE_NO_MEMORY;
kfree(dpc);
}
} 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_ACPI_STATUS(status);
return status;
}

EXPORT_SYMBOL(acpi_os_queue_for_execution);
EXPORT_SYMBOL(acpi_os_execute);

void acpi_os_wait_events_complete(void *context)
{
Expand Down
3 changes: 1 addition & 2 deletions drivers/acpi/thermal.c
Original file line number Diff line number Diff line change
Expand Up @@ -684,8 +684,7 @@ static void acpi_thermal_run(unsigned long data)
{
struct acpi_thermal *tz = (struct acpi_thermal *)data;
if (!tz->zombie)
acpi_os_queue_for_execution(OSD_PRIORITY_GPE,
acpi_thermal_check, (void *)data);
acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data);
}

static void acpi_thermal_check(void *data)
Expand Down

0 comments on commit b8d3519

Please sign in to comment.