1 /*-
2 * Copyright (c) 2000 Michael Smith
3 * Copyright (c) 2000 BSDi
4 * Copyright (c) 2007-2009 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.1 : Mutual Exclusion and Synchronisation
31 */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <contrib/dev/acpica/include/acpi.h>
37 #include <contrib/dev/acpica/include/accommon.h>
38
39 #include <sys/condvar.h>
40 #include <sys/kernel.h>
41 #include <sys/lock.h>
42 #include <sys/malloc.h>
43 #include <sys/mutex.h>
44
45 #define _COMPONENT ACPI_OS_SERVICES
46 ACPI_MODULE_NAME("SYNCH")
47
48 static MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
49
50 /*
51 * Convert milliseconds to ticks.
52 */
53 static int
54 timeout2hz(UINT16 Timeout)
55 {
56 struct timeval tv;
57
58 tv.tv_sec = (time_t)(Timeout / 1000);
59 tv.tv_usec = (suseconds_t)(Timeout % 1000) * 1000;
60
61 return (tvtohz(&tv));
62 }
63
64 /*
65 * ACPI_SEMAPHORE
66 */
67 struct acpi_sema {
68 struct mtx as_lock;
69 char as_name[32];
70 struct cv as_cv;
71 UINT32 as_maxunits;
72 UINT32 as_units;
73 int as_waiters;
74 int as_reset;
75 };
76
77 ACPI_STATUS
78 AcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits,
79 ACPI_SEMAPHORE *OutHandle)
80 {
81 struct acpi_sema *as;
82
83 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
84
85 if (OutHandle == NULL || MaxUnits == 0 || InitialUnits > MaxUnits)
86 return_ACPI_STATUS (AE_BAD_PARAMETER);
87
88 if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
89 return_ACPI_STATUS (AE_NO_MEMORY);
90
91 snprintf(as->as_name, sizeof(as->as_name), "ACPI sema (%p)", as);
92 mtx_init(&as->as_lock, as->as_name, NULL, MTX_DEF);
93 cv_init(&as->as_cv, as->as_name);
94 as->as_maxunits = MaxUnits;
95 as->as_units = InitialUnits;
96
97 *OutHandle = (ACPI_SEMAPHORE)as;
98
99 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s, max %u, initial %u\n",
100 as->as_name, MaxUnits, InitialUnits));
101
102 return_ACPI_STATUS (AE_OK);
103 }
104
105 ACPI_STATUS
106 AcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle)
107 {
108 struct acpi_sema *as = (struct acpi_sema *)Handle;
109
110 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
111
112 if (as == NULL)
113 return_ACPI_STATUS (AE_BAD_PARAMETER);
114
115 mtx_lock(&as->as_lock);
116
117 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", as->as_name));
118
119 if (as->as_waiters > 0) {
120 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
121 "reset %s, units %u, waiters %d\n",
122 as->as_name, as->as_units, as->as_waiters));
123 as->as_reset = 1;
124 cv_broadcast(&as->as_cv);
125 while (as->as_waiters > 0) {
126 if (mtx_sleep(&as->as_reset, &as->as_lock,
127 PCATCH, "acsrst", hz) == EINTR) {
128 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
129 "failed to reset %s, waiters %d\n",
130 as->as_name, as->as_waiters));
131 mtx_unlock(&as->as_lock);
132 return_ACPI_STATUS (AE_ERROR);
133 }
134 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
135 "wait %s, units %u, waiters %d\n",
136 as->as_name, as->as_units, as->as_waiters));
137 }
138 }
139
140 mtx_unlock(&as->as_lock);
141
142 mtx_destroy(&as->as_lock);
143 cv_destroy(&as->as_cv);
144 free(as, M_ACPISEM);
145
146 return_ACPI_STATUS (AE_OK);
147 }
148
149 #define ACPISEM_AVAIL(s, u) ((s)->as_units >= (u))
150
151 ACPI_STATUS
152 AcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout)
153 {
154 struct acpi_sema *as = (struct acpi_sema *)Handle;
155 int error, prevtick, slptick, tmo;
156 ACPI_STATUS status = AE_OK;
157
158 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
159
160 if (as == NULL || Units == 0)
161 return_ACPI_STATUS (AE_BAD_PARAMETER);
162
163 mtx_lock(&as->as_lock);
164
165 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
166 "get %u unit(s) from %s, units %u, waiters %d, timeout %u\n",
167 Units, as->as_name, as->as_units, as->as_waiters, Timeout));
168
169 if (as->as_maxunits != ACPI_NO_UNIT_LIMIT && as->as_maxunits < Units) {
170 mtx_unlock(&as->as_lock);
171 return_ACPI_STATUS (AE_LIMIT);
172 }
173
174 switch (Timeout) {
175 case ACPI_DO_NOT_WAIT:
176 if (!ACPISEM_AVAIL(as, Units))
177 status = AE_TIME;
178 break;
179 case ACPI_WAIT_FOREVER:
180 while (!ACPISEM_AVAIL(as, Units)) {
181 as->as_waiters++;
182 error = cv_wait_sig(&as->as_cv, &as->as_lock);
183 as->as_waiters--;
184 if (error == EINTR || as->as_reset) {
185 status = AE_ERROR;
186 break;
187 }
188 }
189 break;
190 default:
191 if (cold) {
192 /*
193 * Just spin polling the semaphore once a
194 * millisecond.
195 */
196 while (!ACPISEM_AVAIL(as, Units)) {
197 if (Timeout == 0) {
198 status = AE_TIME;
199 break;
200 }
201 Timeout--;
202 mtx_unlock(&as->as_lock);
203 DELAY(1000);
204 mtx_lock(&as->as_lock);
205 }
206 break;
207 }
208 tmo = timeout2hz(Timeout);
209 while (!ACPISEM_AVAIL(as, Units)) {
210 prevtick = ticks;
211 as->as_waiters++;
212 error = cv_timedwait_sig(&as->as_cv, &as->as_lock, tmo);
213 as->as_waiters--;
214 if (error == EINTR || as->as_reset) {
215 status = AE_ERROR;
216 break;
217 }
218 if (ACPISEM_AVAIL(as, Units))
219 break;
220 slptick = ticks - prevtick;
221 if (slptick >= tmo || slptick < 0) {
222 status = AE_TIME;
223 break;
224 }
225 tmo -= slptick;
226 }
227 }
228 if (ACPI_SUCCESS(status))
229 as->as_units -= Units;
230
231 mtx_unlock(&as->as_lock);
232
233 return_ACPI_STATUS (status);
234 }
235
236 ACPI_STATUS
237 AcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units)
238 {
239 struct acpi_sema *as = (struct acpi_sema *)Handle;
240 UINT32 i;
241
242 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
243
244 if (as == NULL || Units == 0)
245 return_ACPI_STATUS (AE_BAD_PARAMETER);
246
247 mtx_lock(&as->as_lock);
248
249 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
250 "return %u units to %s, units %u, waiters %d\n",
251 Units, as->as_name, as->as_units, as->as_waiters));
252
253 if (as->as_maxunits != ACPI_NO_UNIT_LIMIT &&
254 (as->as_maxunits < Units ||
255 as->as_maxunits - Units < as->as_units)) {
256 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
257 "exceeded max units %u\n", as->as_maxunits));
258 mtx_unlock(&as->as_lock);
259 return_ACPI_STATUS (AE_LIMIT);
260 }
261
262 as->as_units += Units;
263 if (as->as_waiters > 0 && ACPISEM_AVAIL(as, Units))
264 for (i = 0; i < Units; i++)
265 cv_signal(&as->as_cv);
266
267 mtx_unlock(&as->as_lock);
268
269 return_ACPI_STATUS (AE_OK);
270 }
271
272 #undef ACPISEM_AVAIL
273
274 /*
275 * ACPI_MUTEX
276 */
277 struct acpi_mutex {
278 struct mtx am_lock;
279 char am_name[32];
280 struct thread *am_owner;
281 int am_nested;
282 int am_waiters;
283 int am_reset;
284 };
285
286 ACPI_STATUS
287 AcpiOsCreateMutex(ACPI_MUTEX *OutHandle)
288 {
289 struct acpi_mutex *am;
290
291 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
292
293 if (OutHandle == NULL)
294 return_ACPI_STATUS (AE_BAD_PARAMETER);
295
296 if ((am = malloc(sizeof(*am), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
297 return_ACPI_STATUS (AE_NO_MEMORY);
298
299 snprintf(am->am_name, sizeof(am->am_name), "ACPI mutex (%p)", am);
300 mtx_init(&am->am_lock, am->am_name, NULL, MTX_DEF);
301
302 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s\n", am->am_name));
303
304 *OutHandle = (ACPI_MUTEX)am;
305
306 return_ACPI_STATUS (AE_OK);
307 }
308
309 #define ACPIMTX_AVAIL(m) ((m)->am_owner == NULL)
310 #define ACPIMTX_OWNED(m) ((m)->am_owner == curthread)
311
312 void
313 AcpiOsDeleteMutex(ACPI_MUTEX Handle)
314 {
315 struct acpi_mutex *am = (struct acpi_mutex *)Handle;
316
317 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
318
319 if (am == NULL) {
320 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "cannot delete null mutex\n"));
321 return_VOID;
322 }
323
324 mtx_lock(&am->am_lock);
325
326 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", am->am_name));
327
328 if (am->am_waiters > 0) {
329 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
330 "reset %s, owner %p\n", am->am_name, am->am_owner));
331 am->am_reset = 1;
332 wakeup(am);
333 while (am->am_waiters > 0) {
334 if (mtx_sleep(&am->am_reset, &am->am_lock,
335 PCATCH, "acmrst", hz) == EINTR) {
336 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
337 "failed to reset %s, waiters %d\n",
338 am->am_name, am->am_waiters));
339 mtx_unlock(&am->am_lock);
340 return_VOID;
341 }
342 if (ACPIMTX_AVAIL(am))
343 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
344 "wait %s, waiters %d\n",
345 am->am_name, am->am_waiters));
346 else
347 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
348 "wait %s, owner %p, waiters %d\n",
349 am->am_name, am->am_owner, am->am_waiters));
350 }
351 }
352
353 mtx_unlock(&am->am_lock);
354
355 mtx_destroy(&am->am_lock);
356 free(am, M_ACPISEM);
357 }
358
359 ACPI_STATUS
360 AcpiOsAcquireMutex(ACPI_MUTEX Handle, UINT16 Timeout)
361 {
362 struct acpi_mutex *am = (struct acpi_mutex *)Handle;
363 int error, prevtick, slptick, tmo;
364 ACPI_STATUS status = AE_OK;
365
366 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
367
368 if (am == NULL)
369 return_ACPI_STATUS (AE_BAD_PARAMETER);
370
371 mtx_lock(&am->am_lock);
372
373 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "acquire %s\n", am->am_name));
374
375 if (ACPIMTX_OWNED(am)) {
376 am->am_nested++;
377 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
378 "acquire nested %s, depth %d\n",
379 am->am_name, am->am_nested));
380 mtx_unlock(&am->am_lock);
381 return_ACPI_STATUS (AE_OK);
382 }
383
384 switch (Timeout) {
385 case ACPI_DO_NOT_WAIT:
386 if (!ACPIMTX_AVAIL(am))
387 status = AE_TIME;
388 break;
389 case ACPI_WAIT_FOREVER:
390 while (!ACPIMTX_AVAIL(am)) {
391 am->am_waiters++;
392 error = mtx_sleep(am, &am->am_lock, PCATCH, "acmtx", 0);
393 am->am_waiters--;
394 if (error == EINTR || am->am_reset) {
395 status = AE_ERROR;
396 break;
397 }
398 }
399 break;
400 default:
401 if (cold) {
402 /*
403 * Just spin polling the mutex once a
404 * millisecond.
405 */
406 while (!ACPIMTX_AVAIL(am)) {
407 if (Timeout == 0) {
408 status = AE_TIME;
409 break;
410 }
411 Timeout--;
412 mtx_unlock(&am->am_lock);
413 DELAY(1000);
414 mtx_lock(&am->am_lock);
415 }
416 break;
417 }
418 tmo = timeout2hz(Timeout);
419 while (!ACPIMTX_AVAIL(am)) {
420 prevtick = ticks;
421 am->am_waiters++;
422 error = mtx_sleep(am, &am->am_lock, PCATCH,
423 "acmtx", tmo);
424 am->am_waiters--;
425 if (error == EINTR || am->am_reset) {
426 status = AE_ERROR;
427 break;
428 }
429 if (ACPIMTX_AVAIL(am))
430 break;
431 slptick = ticks - prevtick;
432 if (slptick >= tmo || slptick < 0) {
433 status = AE_TIME;
434 break;
435 }
436 tmo -= slptick;
437 }
438 }
439 if (ACPI_SUCCESS(status))
440 am->am_owner = curthread;
441
442 mtx_unlock(&am->am_lock);
443
444 return_ACPI_STATUS (status);
445 }
446
447 void
448 AcpiOsReleaseMutex(ACPI_MUTEX Handle)
449 {
450 struct acpi_mutex *am = (struct acpi_mutex *)Handle;
451
452 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
453
454 if (am == NULL) {
455 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
456 "cannot release null mutex\n"));
457 return_VOID;
458 }
459
460 mtx_lock(&am->am_lock);
461
462 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "release %s\n", am->am_name));
463
464 if (ACPIMTX_OWNED(am)) {
465 if (am->am_nested > 0) {
466 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
467 "release nested %s, depth %d\n",
468 am->am_name, am->am_nested));
469 am->am_nested--;
470 } else
471 am->am_owner = NULL;
472 } else {
473 if (ACPIMTX_AVAIL(am))
474 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
475 "release already available %s\n", am->am_name));
476 else
477 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
478 "release unowned %s from %p, depth %d\n",
479 am->am_name, am->am_owner, am->am_nested));
480 }
481 if (am->am_waiters > 0 && ACPIMTX_AVAIL(am))
482 wakeup_one(am);
483
484 mtx_unlock(&am->am_lock);
485 }
486
487 #undef ACPIMTX_AVAIL
488 #undef ACPIMTX_OWNED
489
490 /*
491 * ACPI_SPINLOCK
492 */
493 struct acpi_spinlock {
494 struct mtx al_lock;
495 char al_name[32];
496 int al_nested;
497 };
498
499 ACPI_STATUS
500 AcpiOsCreateLock(ACPI_SPINLOCK *OutHandle)
501 {
502 struct acpi_spinlock *al;
503
504 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
505
506 if (OutHandle == NULL)
507 return_ACPI_STATUS (AE_BAD_PARAMETER);
508
509 if ((al = malloc(sizeof(*al), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
510 return_ACPI_STATUS (AE_NO_MEMORY);
511
512 #ifdef ACPI_DEBUG
513 if (OutHandle == &AcpiGbl_GpeLock)
514 snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (GPE)");
515 else if (OutHandle == &AcpiGbl_HardwareLock)
516 snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (HW)");
517 else
518 #endif
519 snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (%p)", al);
520 mtx_init(&al->al_lock, al->al_name, NULL, MTX_SPIN);
521
522 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s\n", al->al_name));
523
524 *OutHandle = (ACPI_SPINLOCK)al;
525
526 return_ACPI_STATUS (AE_OK);
527 }
528
529 void
530 AcpiOsDeleteLock(ACPI_SPINLOCK Handle)
531 {
532 struct acpi_spinlock *al = (struct acpi_spinlock *)Handle;
533
534 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
535
536 if (al == NULL) {
537 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
538 "cannot delete null spinlock\n"));
539 return_VOID;
540 }
541
542 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", al->al_name));
543
544 mtx_destroy(&al->al_lock);
545 free(al, M_ACPISEM);
546 }
547
548 ACPI_CPU_FLAGS
549 AcpiOsAcquireLock(ACPI_SPINLOCK Handle)
550 {
551 struct acpi_spinlock *al = (struct acpi_spinlock *)Handle;
552
553 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
554
555 if (al == NULL) {
556 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
557 "cannot acquire null spinlock\n"));
558 return (0);
559 }
560
561 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "acquire %s\n", al->al_name));
562
563 if (mtx_owned(&al->al_lock)) {
564 al->al_nested++;
565 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
566 "acquire nested %s, depth %d\n",
567 al->al_name, al->al_nested));
568 } else
569 mtx_lock_spin(&al->al_lock);
570
571 return (0);
572 }
573
574 void
575 AcpiOsReleaseLock(ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags)
576 {
577 struct acpi_spinlock *al = (struct acpi_spinlock *)Handle;
578
579 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
580
581 if (al == NULL) {
582 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
583 "cannot release null spinlock\n"));
584 return_VOID;
585 }
586
587 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "release %s\n", al->al_name));
588
589 if (mtx_owned(&al->al_lock)) {
590 if (al->al_nested > 0) {
591 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
592 "release nested %s, depth %d\n",
593 al->al_name, al->al_nested));
594 al->al_nested--;
595 } else
596 mtx_unlock_spin(&al->al_lock);
597 } else
598 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
599 "cannot release unowned %s\n", al->al_name));
600 }
601
602 /* Section 5.2.10.1: global lock acquire/release functions */
603
604 /*
605 * Acquire the global lock. If busy, set the pending bit. The caller
606 * will wait for notification from the BIOS that the lock is available
607 * and then attempt to acquire it again.
608 */
609 int
610 acpi_acquire_global_lock(volatile uint32_t *lock)
611 {
612 uint32_t new, old;
613
614 do {
615 old = *lock;
616 new = (old & ~ACPI_GLOCK_PENDING) | ACPI_GLOCK_OWNED;
617 if ((old & ACPI_GLOCK_OWNED) != 0)
618 new |= ACPI_GLOCK_PENDING;
619 } while (atomic_cmpset_32(lock, old, new) == 0);
620
621 return ((new & ACPI_GLOCK_PENDING) == 0);
622 }
623
624 /*
625 * Release the global lock, returning whether there is a waiter pending.
626 * If the BIOS set the pending bit, OSPM must notify the BIOS when it
627 * releases the lock.
628 */
629 int
630 acpi_release_global_lock(volatile uint32_t *lock)
631 {
632 uint32_t new, old;
633
634 do {
635 old = *lock;
636 new = old & ~(ACPI_GLOCK_PENDING | ACPI_GLOCK_OWNED);
637 } while (atomic_cmpset_32(lock, old, new) == 0);
638
639 return ((old & ACPI_GLOCK_PENDING) != 0);
640 }
Cache object: 8b0c9b5bf75e7aea610be012aba01d27
|