1 /*- 2 * Copyright (c) 2000 Michael Smith 3 * Copyright (c) 2000 BSDi 4 * Copyright (c) 2007-2012 Jung-uk Kim <jkim@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * 6.3 : Scheduling services 31 */ 32 33 #include <sys/cdefs.h> 34 #include "opt_acpi.h" 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/bus.h> 38 #include <sys/kernel.h> 39 #include <sys/kthread.h> 40 #include <sys/malloc.h> 41 #include <sys/proc.h> 42 #include <sys/taskqueue.h> 43 44 #include <contrib/dev/acpica/include/acpi.h> 45 #include <contrib/dev/acpica/include/accommon.h> 46 47 #include <dev/acpica/acpivar.h> 48 49 #define _COMPONENT ACPI_OS_SERVICES 50 ACPI_MODULE_NAME("SCHEDULE") 51 52 /* 53 * Allow the user to tune the maximum number of tasks we may enqueue. 54 */ 55 static int acpi_max_tasks = ACPI_MAX_TASKS; 56 SYSCTL_INT(_debug_acpi, OID_AUTO, max_tasks, CTLFLAG_RDTUN, &acpi_max_tasks, 57 0, "Maximum acpi tasks"); 58 59 /* 60 * Track and report the system's demand for task slots. 61 */ 62 static int acpi_tasks_hiwater; 63 SYSCTL_INT(_debug_acpi, OID_AUTO, tasks_hiwater, CTLFLAG_RD, 64 &acpi_tasks_hiwater, 1, "Peak demand for ACPI event task slots."); 65 66 /* 67 * Allow the user to tune the number of task threads we start. It seems 68 * some systems have problems with increased parallelism. 69 */ 70 static int acpi_max_threads = ACPI_MAX_THREADS; 71 SYSCTL_INT(_debug_acpi, OID_AUTO, max_threads, CTLFLAG_RDTUN, &acpi_max_threads, 72 0, "Maximum acpi threads"); 73 74 static MALLOC_DEFINE(M_ACPITASK, "acpitask", "ACPI deferred task"); 75 76 struct acpi_task_ctx { 77 struct task at_task; 78 ACPI_OSD_EXEC_CALLBACK at_function; 79 void *at_context; 80 int at_flag; 81 #define ACPI_TASK_FREE 0 82 #define ACPI_TASK_USED 1 83 #define ACPI_TASK_ENQUEUED 2 84 }; 85 86 struct taskqueue *acpi_taskq; 87 static struct acpi_task_ctx *acpi_tasks; 88 static int acpi_task_count; 89 static int acpi_taskq_started; 90 91 /* 92 * Preallocate some memory for tasks early enough. 93 * malloc(9) cannot be used with spin lock held. 94 */ 95 static void 96 acpi_task_init(void *arg) 97 { 98 99 acpi_tasks = malloc(sizeof(*acpi_tasks) * acpi_max_tasks, M_ACPITASK, 100 M_WAITOK | M_ZERO); 101 } 102 103 SYSINIT(acpi_tasks, SI_SUB_DRIVERS, SI_ORDER_FIRST, acpi_task_init, NULL); 104 105 /* 106 * Initialize ACPI task queue. 107 */ 108 static void 109 acpi_taskq_init(void *arg) 110 { 111 int i; 112 113 acpi_taskq = taskqueue_create_fast("acpi_task", M_NOWAIT, 114 &taskqueue_thread_enqueue, &acpi_taskq); 115 taskqueue_start_threads(&acpi_taskq, acpi_max_threads, PWAIT, "acpi_task"); 116 if (acpi_task_count > 0) { 117 if (bootverbose) 118 printf("AcpiOsExecute: enqueue %d pending tasks\n", 119 acpi_task_count); 120 for (i = 0; i < acpi_max_tasks; i++) 121 if (atomic_cmpset_int(&acpi_tasks[i].at_flag, ACPI_TASK_USED, 122 ACPI_TASK_USED | ACPI_TASK_ENQUEUED)) 123 taskqueue_enqueue(acpi_taskq, &acpi_tasks[i].at_task); 124 } 125 acpi_taskq_started = 1; 126 } 127 128 SYSINIT(acpi_taskq, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, acpi_taskq_init, NULL); 129 130 /* 131 * Bounce through this wrapper function since ACPI-CA doesn't understand 132 * the pending argument for its callbacks. 133 */ 134 static void 135 acpi_task_execute(void *context, int pending) 136 { 137 struct acpi_task_ctx *at; 138 139 at = (struct acpi_task_ctx *)context; 140 at->at_function(at->at_context); 141 atomic_clear_int(&at->at_flag, ACPI_TASK_USED | ACPI_TASK_ENQUEUED); 142 acpi_task_count--; 143 } 144 145 static ACPI_STATUS 146 acpi_task_enqueue(int priority, ACPI_OSD_EXEC_CALLBACK Function, void *Context) 147 { 148 struct acpi_task_ctx *at; 149 int i; 150 151 for (at = NULL, i = 0; i < acpi_max_tasks; i++) 152 if (atomic_cmpset_int(&acpi_tasks[i].at_flag, ACPI_TASK_FREE, 153 ACPI_TASK_USED)) { 154 at = &acpi_tasks[i]; 155 acpi_task_count++; 156 break; 157 } 158 159 if (i > acpi_tasks_hiwater) 160 atomic_cmpset_int(&acpi_tasks_hiwater, acpi_tasks_hiwater, i); 161 162 if (at == NULL) { 163 printf("AcpiOsExecute: failed to enqueue task, consider increasing " 164 "the debug.acpi.max_tasks tunable\n"); 165 return (AE_NO_MEMORY); 166 } 167 168 TASK_INIT(&at->at_task, priority, acpi_task_execute, at); 169 at->at_function = Function; 170 at->at_context = Context; 171 172 /* 173 * If the task queue is ready, enqueue it now. 174 */ 175 if (acpi_taskq_started) { 176 atomic_set_int(&at->at_flag, ACPI_TASK_ENQUEUED); 177 taskqueue_enqueue(acpi_taskq, &at->at_task); 178 return (AE_OK); 179 } 180 if (bootverbose) 181 printf("AcpiOsExecute: task queue not started\n"); 182 183 return (AE_OK); 184 } 185 186 /* 187 * This function may be called in interrupt context, i.e. when a GPE fires. 188 * We allocate and queue a task for one of our taskqueue threads to process. 189 */ 190 ACPI_STATUS 191 AcpiOsExecute(ACPI_EXECUTE_TYPE Type, ACPI_OSD_EXEC_CALLBACK Function, 192 void *Context) 193 { 194 ACPI_STATUS status; 195 int pri; 196 197 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 198 199 if (Function == NULL) 200 return_ACPI_STATUS(AE_BAD_PARAMETER); 201 202 switch (Type) { 203 case OSL_GPE_HANDLER: 204 case OSL_NOTIFY_HANDLER: 205 /* 206 * Run GPEs and Notifies at the same priority. This allows 207 * Notifies that are generated by running a GPE's method (e.g., _L00) 208 * to not be pre-empted by a later GPE that arrives during the 209 * Notify handler execution. 210 */ 211 pri = 10; 212 break; 213 case OSL_GLOBAL_LOCK_HANDLER: 214 case OSL_EC_POLL_HANDLER: 215 case OSL_EC_BURST_HANDLER: 216 pri = 5; 217 break; 218 case OSL_DEBUGGER_MAIN_THREAD: 219 case OSL_DEBUGGER_EXEC_THREAD: 220 pri = 0; 221 break; 222 default: 223 return_ACPI_STATUS(AE_BAD_PARAMETER); 224 } 225 226 status = acpi_task_enqueue(pri, Function, Context); 227 return_ACPI_STATUS(status); 228 } 229 230 void 231 AcpiOsWaitEventsComplete(void) 232 { 233 int i; 234 235 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 236 237 for (i = 0; i < acpi_max_tasks; i++) 238 if ((atomic_load_acq_int(&acpi_tasks[i].at_flag) & 239 ACPI_TASK_ENQUEUED) != 0) 240 taskqueue_drain(acpi_taskq, &acpi_tasks[i].at_task); 241 return_VOID; 242 } 243 244 void 245 AcpiOsSleep(UINT64 Milliseconds) 246 { 247 int timo; 248 249 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 250 251 timo = Milliseconds * hz / 1000; 252 253 /* 254 * If requested sleep time is less than our hz resolution, use 255 * DELAY instead for better granularity. 256 */ 257 if (timo > 0) 258 pause("acpislp", timo); 259 else 260 DELAY(Milliseconds * 1000); 261 262 return_VOID; 263 } 264 265 /* 266 * Return the current time in 100 nanosecond units 267 */ 268 UINT64 269 AcpiOsGetTimer(void) 270 { 271 struct bintime bt; 272 UINT64 t; 273 274 binuptime(&bt); 275 t = (uint64_t)bt.sec * 10000000; 276 t += ((uint64_t)10000000 * (uint32_t)(bt.frac >> 32)) >> 32; 277 278 return (t); 279 } 280 281 void 282 AcpiOsStall(UINT32 Microseconds) 283 { 284 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 285 286 DELAY(Microseconds); 287 return_VOID; 288 } 289 290 ACPI_THREAD_ID 291 AcpiOsGetThreadId(void) 292 { 293 294 /* XXX do not add ACPI_FUNCTION_TRACE here, results in recursive call. */ 295 296 /* Returning 0 is not allowed. */ 297 return (curthread->td_tid); 298 } 299