From 87b510d505b5a57cae8d9c14ec2e0c758d4d863d Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Mon, 2 Aug 2004 19:51:09 +0000 Subject: [PATCH] Rip out the badly designed softint-based taskqueue used by ACPI for callbacks. This was deadlocking tsleep() calls made from AcpiOsSleep() when early ACPI interrupts occured, such as when a laptop is booted without AC connected. Use a kernel thread and LWKT messaging to queue callbacks requests and add code to use DELAY instead of tsleep() for calls made to AcpiOsSleep() in early boot before the system timeout/ticks subsystem is initialized. Fix two bugged AcpiOsSleep() calls that were sleeping for 10 seconds instead of 10 milliseconds. --- sys/dev/acpica5/Osd/OsdSchedule.c | 186 ++++++++++-------------------- sys/dev/acpica5/acpi.c | 4 +- sys/dev/acpica5/acpi_acad.c | 4 +- sys/dev/acpica5/acpi_cmbat.c | 4 +- sys/dev/acpica5/acpivar.h | 13 +-- 5 files changed, 67 insertions(+), 144 deletions(-) diff --git a/sys/dev/acpica5/Osd/OsdSchedule.c b/sys/dev/acpica5/Osd/OsdSchedule.c index af0b6eaa70..a9f2b74adf 100644 --- a/sys/dev/acpica5/Osd/OsdSchedule.c +++ b/sys/dev/acpica5/Osd/OsdSchedule.c @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/Osd/OsdSchedule.c,v 1.28 2004/05/06 02:18:58 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/Osd/OsdSchedule.c,v 1.3 2004/06/27 08:52:42 dillon Exp $ + * $DragonFly: src/sys/dev/acpica5/Osd/OsdSchedule.c,v 1.4 2004/08/02 19:51:09 dillon Exp $ */ /* @@ -41,9 +41,13 @@ #include #include #include +#include #include #include +#include +#include + #include "acpi.h" #include @@ -57,102 +61,55 @@ ACPI_MODULE_NAME("SCHEDULE") MALLOC_DEFINE(M_ACPITASK, "acpitask", "ACPI deferred task"); -static void AcpiOsExecuteQueue(void *arg, int pending); +static void acpi_task_thread(void *arg); +static void acpi_autofree_reply(lwkt_port_t port, lwkt_msg_t msg); struct acpi_task { - struct task at_task; + struct lwkt_msg at_msg; OSD_EXECUTION_CALLBACK at_function; void *at_context; + int at_priority; }; -struct acpi_task_queue { - STAILQ_ENTRY(acpi_task_queue) at_q; - struct acpi_task *at; -}; +static struct thread *acpi_task_td; +struct lwkt_port acpi_afree_rport; -#if __FreeBSD_version >= 500000 /* - * Private task queue definition for ACPI + * Initialize the ACPI helper thread. */ -TASKQUEUE_DECLARE(acpi); -static void *taskqueue_acpi_ih; - -static void -taskqueue_acpi_enqueue(void *context) -{ - swi_sched(taskqueue_acpi_ih, 0); -} - -static void -taskqueue_acpi_run(void *dummy) +int +acpi_task_thread_init(void) { - taskqueue_run(taskqueue_acpi); + lwkt_initport(&acpi_afree_rport, NULL); + acpi_afree_rport.mp_replyport = acpi_autofree_reply; + kthread_create(acpi_task_thread, NULL, &acpi_task_td, + 0, 0, "acpi_task"); + return (0); } -TASKQUEUE_DEFINE(acpi, taskqueue_acpi_enqueue, 0, - swi_add(NULL, "acpitaskq", taskqueue_acpi_run, NULL, - SWI_TQ, 0, &taskqueue_acpi_ih)); - -#ifdef ACPI_USE_THREADS -static STAILQ_HEAD(, acpi_task_queue) acpi_task_queue; -static struct mtx acpi_task_mtx; - +/* + * The ACPI helper thread processes OSD execution callback messages. + */ static void acpi_task_thread(void *arg) { - struct acpi_task_queue *atq; - OSD_EXECUTION_CALLBACK Function; - void *Context; + OSD_EXECUTION_CALLBACK func; + struct acpi_task *at; for (;;) { - mtx_lock(&acpi_task_mtx); - if ((atq = STAILQ_FIRST(&acpi_task_queue)) == NULL) { - msleep(&acpi_task_queue, &acpi_task_mtx, PCATCH, "actask", 0); - mtx_unlock(&acpi_task_mtx); - continue; - } - - STAILQ_REMOVE_HEAD(&acpi_task_queue, at_q); - mtx_unlock(&acpi_task_mtx); - - Function = (OSD_EXECUTION_CALLBACK)atq->at->at_function; - Context = atq->at->at_context; - - mtx_lock(&Giant); - Function(Context); - - free(atq->at, M_ACPITASK); - free(atq, M_ACPITASK); - mtx_unlock(&Giant); - } - - kthread_exit(0); -} - -int -acpi_task_thread_init(void) -{ - int i, err; - struct proc *acpi_kthread_proc; - - err = 0; - STAILQ_INIT(&acpi_task_queue); - mtx_init(&acpi_task_mtx, "ACPI task", NULL, MTX_DEF); - - for (i = 0; i < ACPI_MAX_THREADS; i++) { - err = kthread_create(acpi_task_thread, NULL, &acpi_kthread_proc, - 0, 0, "acpi_task%d", i); - if (err != 0) { - printf("%s: kthread_create failed(%d)\n", __func__, err); - break; - } + at = (void *)lwkt_waitport(&curthread->td_msgport, NULL); + func = (OSD_EXECUTION_CALLBACK)at->at_function; + func((void *)at->at_context); + lwkt_replymsg(&at->at_msg, 0); } - return (err); + kthread_exit(); } -#endif /* ACPI_USE_THREADS */ -#endif /* __FreeBSD_version >= 500000 */ -/* This function is called in interrupt context. */ +/* + * Queue an ACPI message for execution by allocating a LWKT message structure + * and sending the message to the helper thread. The reply port is setup + * to automatically free the message. + */ ACPI_STATUS AcpiOsQueueForExecution(UINT32 Priority, OSD_EXECUTION_CALLBACK Function, void *Context) @@ -165,10 +122,6 @@ AcpiOsQueueForExecution(UINT32 Priority, OSD_EXECUTION_CALLBACK Function, if (Function == NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); - /* Note: Interrupt Context */ - at = malloc(sizeof(*at), M_ACPITASK, M_INTWAIT | M_ZERO); - at->at_function = Function; - at->at_context = Context; switch (Priority) { case OSD_PRIORITY_GPE: pri = 4; @@ -183,53 +136,30 @@ AcpiOsQueueForExecution(UINT32 Priority, OSD_EXECUTION_CALLBACK Function, pri = 1; break; default: - free(at, M_ACPITASK); return_ACPI_STATUS (AE_BAD_PARAMETER); } - TASK_INIT(&at->at_task, pri, AcpiOsExecuteQueue, at); -#if __FreeBSD_version >= 500000 - taskqueue_enqueue(taskqueue_acpi, (struct task *)at); -#else - taskqueue_enqueue(taskqueue_swi, (struct task *)at); -#endif + /* Note: Interrupt Context */ + at = malloc(sizeof(*at), M_ACPITASK, M_INTWAIT | M_ZERO); + lwkt_initmsg(&at->at_msg, &acpi_afree_rport, 0, + lwkt_cmd_op_none, lwkt_cmd_op_none); + at->at_function = Function; + at->at_context = Context; + at->at_priority = pri; + lwkt_sendmsg(&acpi_task_td->td_msgport, &at->at_msg); return_ACPI_STATUS (AE_OK); } +/* + * The message's reply port just frees the message. + */ static void -AcpiOsExecuteQueue(void *arg, int pending) +acpi_autofree_reply(lwkt_port_t port, lwkt_msg_t msg) { - struct acpi_task *at; - struct acpi_task_queue *atq; - OSD_EXECUTION_CALLBACK Function; - void *Context; - - ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); - - at = (struct acpi_task *)arg; - atq = NULL; - Function = NULL; - Context = NULL; - -#ifdef ACPI_USE_THREADS - atq = malloc(sizeof(*atq), M_ACPITASK, M_INTWAIT); - atq->at = at; - - mtx_lock(&acpi_task_mtx); - STAILQ_INSERT_TAIL(&acpi_task_queue, atq, at_q); - mtx_unlock(&acpi_task_mtx); - wakeup_one(&acpi_task_queue); -#else - Function = (OSD_EXECUTION_CALLBACK)at->at_function; - Context = at->at_context; - - Function(Context); - free(at, M_ACPITASK); -#endif - - return_VOID; + free(msg, M_ACPITASK); } + void AcpiOsSleep(UINT32 Seconds, UINT32 Milliseconds) { @@ -241,14 +171,22 @@ AcpiOsSleep(UINT32 Seconds, UINT32 Milliseconds) timo = (Seconds * hz) + Milliseconds * hz / 1000; /* - * If requested sleep time is less than our hz resolution, use - * DELAY instead for better granularity. + * If requested sleep time is less than our hz resolution, or if + * the system is in early boot before the system tick is operational, + * use DELAY instead for better granularity. */ - if (timo > 0) + if (clocks_running == 0) { + while (Seconds) { + DELAY(1000000); + --Seconds; + } + if (Milliseconds) + DELAY(Milliseconds * 1000); + } else if (timo > 0) { tsleep(&dummy, 0, "acpislp", timo); - else + } else { DELAY(Milliseconds * 1000); - + } return_VOID; } @@ -269,10 +207,8 @@ AcpiOsGetThreadId(void) /* XXX do not add ACPI_FUNCTION_TRACE here, results in recursive call. */ p = curproc; -#if __FreeBSD_version < 500000 if (p == NULL) p = &proc0; -#endif KASSERT(p != NULL, ("%s: curproc is NULL!", __func__)); /* Returning 0 is not allowed. */ diff --git a/sys/dev/acpica5/acpi.c b/sys/dev/acpica5/acpi.c index a42b0980e6..c24c879ca0 100644 --- a/sys/dev/acpica5/acpi.c +++ b/sys/dev/acpica5/acpi.c @@ -27,7 +27,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/acpi.c,v 1.156 2004/06/05 07:25:58 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi.c,v 1.7 2004/08/02 18:41:50 dillon Exp $ + * $DragonFly: src/sys/dev/acpica5/acpi.c,v 1.8 2004/08/02 19:51:07 dillon Exp $ */ #include "opt_acpi.h" @@ -596,10 +596,8 @@ acpi_attach(device_t dev) } #endif -#ifdef ACPI_USE_THREADS if ((error = acpi_task_thread_init())) goto out; -#endif if ((error = acpi_machdep_init(dev))) goto out; diff --git a/sys/dev/acpica5/acpi_acad.c b/sys/dev/acpica5/acpi_acad.c index 81a1287c30..ec833460f9 100644 --- a/sys/dev/acpica5/acpi_acad.c +++ b/sys/dev/acpica5/acpi_acad.c @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/acpi_acad.c,v 1.26 2004/05/30 20:08:23 phk Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_acad.c,v 1.3 2004/07/05 00:07:35 dillon Exp $ + * $DragonFly: src/sys/dev/acpica5/acpi_acad.c,v 1.4 2004/08/02 19:51:07 dillon Exp $ */ #include "opt_acpi.h" @@ -248,7 +248,7 @@ acpi_acad_init_acline(void *arg) acpi_acad_get_status(dev); if (status != sc->status) break; - AcpiOsSleep(10, 0); + AcpiOsSleep(0, 10); } sc->initializing = 0; diff --git a/sys/dev/acpica5/acpi_cmbat.c b/sys/dev/acpica5/acpi_cmbat.c index bcbed41db6..adea3c23ca 100644 --- a/sys/dev/acpica5/acpi_cmbat.c +++ b/sys/dev/acpica5/acpi_cmbat.c @@ -26,7 +26,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/acpi_cmbat.c,v 1.29 2004/05/30 20:08:23 phk Exp $ - * $DragonFly: src/sys/dev/acpica5/acpi_cmbat.c,v 1.4 2004/07/05 00:07:35 dillon Exp $ + * $DragonFly: src/sys/dev/acpica5/acpi_cmbat.c,v 1.5 2004/08/02 19:51:07 dillon Exp $ */ #include "opt_acpi.h" @@ -583,7 +583,7 @@ acpi_cmbat_init_battery(void *arg) ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev), "battery initialization start\n"); - for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10, 0)) { + for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(0, 10)) { sc->present = acpi_BatteryIsPresent(dev); if (!sc->present) continue; diff --git a/sys/dev/acpica5/acpivar.h b/sys/dev/acpica5/acpivar.h index 1e413434a1..90ae5d6bdf 100644 --- a/sys/dev/acpica5/acpivar.h +++ b/sys/dev/acpica5/acpivar.h @@ -26,7 +26,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/dev/acpica/acpivar.h,v 1.69 2004/05/28 07:15:55 njl Exp $ - * $DragonFly: src/sys/dev/acpica5/acpivar.h,v 1.3 2004/07/05 00:07:35 dillon Exp $ + * $DragonFly: src/sys/dev/acpica5/acpivar.h,v 1.4 2004/08/02 19:51:07 dillon Exp $ */ #include "bus_if.h" @@ -343,16 +343,5 @@ int acpi_PkgGas(device_t dev, ACPI_OBJECT *res, int idx, int *rid, struct resource **dst); ACPI_HANDLE acpi_GetReference(ACPI_HANDLE scope, ACPI_OBJECT *obj); -#if __FreeBSD_version >= 500000 -#ifndef ACPI_MAX_THREADS -#define ACPI_MAX_THREADS 3 -#endif -#if ACPI_MAX_THREADS > 0 -#define ACPI_USE_THREADS -#endif -#endif - -#ifdef ACPI_USE_THREADS /* ACPI task kernel thread initialization. */ int acpi_task_thread_init(void); -#endif -- 2.41.0