xref: /freebsd/sys/dev/acpica/Osd/OsdSchedule.c (revision e89c3ac5df58a9eae9d01f325ffc9b34258c4b29)
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/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/malloc.h>
39 #include <sys/proc.h>
40 #include <sys/taskqueue.h>
41 
42 #include <contrib/dev/acpica/include/acpi.h>
43 #include <contrib/dev/acpica/include/accommon.h>
44 
45 #include <dev/acpica/acpivar.h>
46 
47 #define _COMPONENT	ACPI_OS_SERVICES
48 ACPI_MODULE_NAME("SCHEDULE")
49 
50 /*
51  * Allow the user to tune the maximum number of tasks we may enqueue.
52  */
53 static int acpi_max_tasks = ACPI_MAX_TASKS;
54 SYSCTL_INT(_debug_acpi, OID_AUTO, max_tasks, CTLFLAG_RDTUN, &acpi_max_tasks,
55     0, "Maximum acpi tasks");
56 
57 /*
58  * Track and report the system's demand for task slots.
59  */
60 static int acpi_tasks_hiwater;
61 SYSCTL_INT(_debug_acpi, OID_AUTO, tasks_hiwater, CTLFLAG_RD,
62     &acpi_tasks_hiwater, 1, "Peak demand for ACPI event task slots.");
63 
64 /*
65  * Allow the user to tune the number of task threads we start.  It seems
66  * some systems have problems with increased parallelism.
67  */
68 static int acpi_max_threads = ACPI_MAX_THREADS;
69 SYSCTL_INT(_debug_acpi, OID_AUTO, max_threads, CTLFLAG_RDTUN, &acpi_max_threads,
70     0, "Maximum acpi threads");
71 
72 static MALLOC_DEFINE(M_ACPITASK, "acpitask", "ACPI deferred task");
73 
74 struct acpi_task_ctx {
75     struct task			at_task;
76     ACPI_OSD_EXEC_CALLBACK	at_function;
77     void			*at_context;
78     int				at_flag;
79 #define	ACPI_TASK_FREE		0
80 #define	ACPI_TASK_USED		1
81 #define	ACPI_TASK_ENQUEUED	2
82 };
83 
84 struct taskqueue		*acpi_taskq;
85 static struct acpi_task_ctx	*acpi_tasks;
86 /* Unsynchronized as only useful in the mono-CPU startup phase. */
87 static int			acpi_task_count;
88 static int			acpi_taskq_started;
89 
90 /*
91  * Preallocate some memory for tasks early enough.
92  * malloc(9) cannot be used with spin lock held.
93  */
94 static void
95 acpi_task_init(void *arg)
96 {
97 
98     acpi_tasks = malloc(sizeof(*acpi_tasks) * acpi_max_tasks, M_ACPITASK,
99 	M_WAITOK | M_ZERO);
100 }
101 
102 SYSINIT(acpi_tasks, SI_SUB_DRIVERS, SI_ORDER_FIRST, acpi_task_init, NULL);
103 
104 /*
105  * Initialize ACPI task queue.
106  */
107 static void
108 acpi_taskq_init(void *arg)
109 {
110     int i;
111 
112     acpi_taskq = taskqueue_create_fast("acpi_task", M_NOWAIT,
113 	&taskqueue_thread_enqueue, &acpi_taskq);
114     taskqueue_start_threads(&acpi_taskq, acpi_max_threads, PWAIT, "acpi_task");
115     if (acpi_task_count > 0) {
116 	if (bootverbose)
117 	    printf("AcpiOsExecute: enqueue %d pending tasks\n",
118 		acpi_task_count);
119 	for (i = 0; i < acpi_max_tasks; i++)
120 	    if (atomic_cmpset_int(&acpi_tasks[i].at_flag, ACPI_TASK_USED,
121 		ACPI_TASK_USED | ACPI_TASK_ENQUEUED))
122 		taskqueue_enqueue(acpi_taskq, &acpi_tasks[i].at_task);
123     }
124     acpi_taskq_started = 1;
125 }
126 
127 SYSINIT(acpi_taskq, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, acpi_taskq_init, NULL);
128 
129 /*
130  * Bounce through this wrapper function since ACPI-CA doesn't understand
131  * the pending argument for its callbacks.
132  */
133 static void
134 acpi_task_execute(void *context, int pending)
135 {
136     struct acpi_task_ctx *at;
137 
138     at = (struct acpi_task_ctx *)context;
139     at->at_function(at->at_context);
140     atomic_clear_int(&at->at_flag, ACPI_TASK_USED | ACPI_TASK_ENQUEUED);
141     acpi_task_count--;
142 }
143 
144 static ACPI_STATUS
145 acpi_task_enqueue(int priority, ACPI_OSD_EXEC_CALLBACK Function, void *Context)
146 {
147     struct acpi_task_ctx *at;
148     int i;
149 
150     for (at = NULL, i = 0; i < acpi_max_tasks; i++)
151 	if (atomic_cmpset_int(&acpi_tasks[i].at_flag, ACPI_TASK_FREE,
152 	    ACPI_TASK_USED)) {
153 	    at = &acpi_tasks[i];
154 	    acpi_task_count++;
155 	    break;
156 	}
157 
158     if (i > acpi_tasks_hiwater)
159 	atomic_cmpset_int(&acpi_tasks_hiwater, acpi_tasks_hiwater, i);
160 
161     if (at == NULL) {
162 	printf("AcpiOsExecute: failed to enqueue task, consider increasing "
163 	    "the debug.acpi.max_tasks tunable\n");
164 	return (AE_NO_MEMORY);
165     }
166 
167     TASK_INIT(&at->at_task, priority, acpi_task_execute, at);
168     at->at_function = Function;
169     at->at_context = Context;
170 
171     /*
172      * If the task queue is ready, enqueue it now.
173      */
174     if (acpi_taskq_started) {
175 	atomic_set_int(&at->at_flag, ACPI_TASK_ENQUEUED);
176 	taskqueue_enqueue(acpi_taskq, &at->at_task);
177 	return (AE_OK);
178     }
179     if (bootverbose)
180 	printf("AcpiOsExecute: task queue not started\n");
181 
182     return (AE_OK);
183 }
184 
185 /*
186  * This function may be called in interrupt context, i.e. when a GPE fires.
187  * We allocate and queue a task for one of our taskqueue threads to process.
188  */
189 ACPI_STATUS
190 AcpiOsExecute(ACPI_EXECUTE_TYPE Type, ACPI_OSD_EXEC_CALLBACK Function,
191     void *Context)
192 {
193     ACPI_STATUS status;
194     int pri;
195 
196     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
197 
198     if (Function == NULL)
199 	return_ACPI_STATUS(AE_BAD_PARAMETER);
200 
201     switch (Type) {
202     case OSL_GPE_HANDLER:
203     case OSL_NOTIFY_HANDLER:
204 	/*
205 	 * Run GPEs and Notifies at the same priority.  This allows
206 	 * Notifies that are generated by running a GPE's method (e.g., _L00)
207 	 * to not be pre-empted by a later GPE that arrives during the
208 	 * Notify handler execution.
209 	 */
210 	pri = 10;
211 	break;
212     case OSL_GLOBAL_LOCK_HANDLER:
213     case OSL_EC_POLL_HANDLER:
214     case OSL_EC_BURST_HANDLER:
215 	pri = 5;
216 	break;
217     case OSL_DEBUGGER_MAIN_THREAD:
218     case OSL_DEBUGGER_EXEC_THREAD:
219 	pri = 0;
220 	break;
221     default:
222 	return_ACPI_STATUS(AE_BAD_PARAMETER);
223     }
224 
225     status = acpi_task_enqueue(pri, Function, Context);
226     return_ACPI_STATUS(status);
227 }
228 
229 void
230 AcpiOsWaitEventsComplete(void)
231 {
232 	int i;
233 
234 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
235 
236 	for (i = 0; i < acpi_max_tasks; i++)
237 		if ((atomic_load_acq_int(&acpi_tasks[i].at_flag) &
238 		    ACPI_TASK_ENQUEUED) != 0)
239 			taskqueue_drain(acpi_taskq, &acpi_tasks[i].at_task);
240 	return_VOID;
241 }
242 
243 void
244 AcpiOsSleep(UINT64 Milliseconds)
245 {
246     int		timo;
247 
248     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
249 
250     timo = Milliseconds * hz / 1000;
251 
252     /*
253      * If requested sleep time is less than our hz resolution, use
254      * DELAY instead for better granularity.
255      */
256     if (timo > 0)
257 	pause("acpislp", timo);
258     else
259 	DELAY(Milliseconds * 1000);
260 
261     return_VOID;
262 }
263 
264 /*
265  * Return the current time in 100 nanosecond units
266  */
267 UINT64
268 AcpiOsGetTimer(void)
269 {
270     struct bintime bt;
271     UINT64 t;
272 
273     binuptime(&bt);
274     t = (uint64_t)bt.sec * 10000000;
275     t += ((uint64_t)10000000 * (uint32_t)(bt.frac >> 32)) >> 32;
276 
277     return (t);
278 }
279 
280 void
281 AcpiOsStall(UINT32 Microseconds)
282 {
283     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
284 
285     DELAY(Microseconds);
286     return_VOID;
287 }
288 
289 ACPI_THREAD_ID
290 AcpiOsGetThreadId(void)
291 {
292 
293     /* XXX do not add ACPI_FUNCTION_TRACE here, results in recursive call. */
294 
295     /* Returning 0 is not allowed. */
296     return (curthread->td_tid);
297 }
298