]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
ACPI: execute Notify() handlers on new thread
authorAlexey Starikovskiy <alexey.y.starikovskiy@intel.com>
Fri, 5 May 2006 07:23:00 +0000 (03:23 -0400)
committerLen Brown <len.brown@intel.com>
Wed, 14 Jun 2006 06:43:23 +0000 (02:43 -0400)
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>
drivers/acpi/ec.c
drivers/acpi/osl.c
drivers/acpi/thermal.c

index 79b09d76c180204e488a924dc4827572a51d92f1..1e3222762bf3772758d19308d778ad1ff3618197 100644 (file)
@@ -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;
@@ -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;
index 109c3f8ae7df36f1cbb31688db4ef4143eab942f..e80ca4730a4451c9f7540a270db9345f41a3d5fc 100644 (file)
@@ -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>
@@ -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
@@ -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)
 {
index 19f3ea48475e369ec9ba1b39405e5d14da9a2e6f..fba9c230a84db10b830757ca9aaff0d9340b1bf0 100644 (file)
@@ -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)