]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/acpica/Osd/OsdSynch.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / acpica / Osd / OsdSynch.c
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                 tmo = timeout2hz(Timeout);
192                 while (!ACPISEM_AVAIL(as, Units)) {
193                         prevtick = ticks;
194                         as->as_waiters++;
195                         error = cv_timedwait_sig(&as->as_cv, &as->as_lock, tmo);
196                         as->as_waiters--;
197                         if (error == EINTR || as->as_reset) {
198                                 status = AE_ERROR;
199                                 break;
200                         }
201                         if (ACPISEM_AVAIL(as, Units))
202                                 break;
203                         slptick = ticks - prevtick;
204                         if (slptick >= tmo || slptick < 0) {
205                                 status = AE_TIME;
206                                 break;
207                         }
208                         tmo -= slptick;
209                 }
210         }
211         if (ACPI_SUCCESS(status))
212                 as->as_units -= Units;
213
214         mtx_unlock(&as->as_lock);
215
216         return_ACPI_STATUS (status);
217 }
218
219 ACPI_STATUS
220 AcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units)
221 {
222         struct acpi_sema        *as = (struct acpi_sema *)Handle;
223         UINT32                  i;
224
225         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
226
227         if (as == NULL || Units == 0)
228                 return_ACPI_STATUS (AE_BAD_PARAMETER);
229
230         mtx_lock(&as->as_lock);
231
232         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
233             "return %u units to %s, units %u, waiters %d\n",
234             Units, as->as_name, as->as_units, as->as_waiters));
235
236         if (as->as_maxunits != ACPI_NO_UNIT_LIMIT &&
237             (as->as_maxunits < Units ||
238             as->as_maxunits - Units < as->as_units)) {
239                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
240                     "exceeded max units %u\n", as->as_maxunits));
241                 mtx_unlock(&as->as_lock);
242                 return_ACPI_STATUS (AE_LIMIT);
243         }
244
245         as->as_units += Units;
246         if (as->as_waiters > 0 && ACPISEM_AVAIL(as, Units))
247                 for (i = 0; i < Units; i++)
248                         cv_signal(&as->as_cv);
249
250         mtx_unlock(&as->as_lock);
251
252         return_ACPI_STATUS (AE_OK);
253 }
254
255 #undef ACPISEM_AVAIL
256
257 /*
258  * ACPI_MUTEX
259  */
260 struct acpi_mutex {
261         struct mtx      am_lock;
262         char            am_name[32];
263         struct thread   *am_owner;
264         int             am_nested;
265         int             am_waiters;
266         int             am_reset;
267 };
268
269 ACPI_STATUS
270 AcpiOsCreateMutex(ACPI_MUTEX *OutHandle)
271 {
272         struct acpi_mutex       *am;
273
274         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
275
276         if (OutHandle == NULL)
277                 return_ACPI_STATUS (AE_BAD_PARAMETER);
278
279         if ((am = malloc(sizeof(*am), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
280                 return_ACPI_STATUS (AE_NO_MEMORY);
281
282         snprintf(am->am_name, sizeof(am->am_name), "ACPI mutex (%p)", am);
283         mtx_init(&am->am_lock, am->am_name, NULL, MTX_DEF);
284
285         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s\n", am->am_name));
286
287         *OutHandle = (ACPI_MUTEX)am;
288
289         return_ACPI_STATUS (AE_OK);
290 }
291
292 #define ACPIMTX_AVAIL(m)        ((m)->am_owner == NULL)
293 #define ACPIMTX_OWNED(m)        ((m)->am_owner == curthread)
294
295 void
296 AcpiOsDeleteMutex(ACPI_MUTEX Handle)
297 {
298         struct acpi_mutex       *am = (struct acpi_mutex *)Handle;
299
300         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
301
302         if (am == NULL) {
303                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "cannot delete null mutex\n"));
304                 return_VOID;
305         }
306
307         mtx_lock(&am->am_lock);
308
309         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", am->am_name));
310
311         if (am->am_waiters > 0) {
312                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
313                     "reset %s, owner %p\n", am->am_name, am->am_owner));
314                 am->am_reset = 1;
315                 wakeup(am);
316                 while (am->am_waiters > 0) {
317                         if (mtx_sleep(&am->am_reset, &am->am_lock,
318                             PCATCH, "acmrst", hz) == EINTR) {
319                                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
320                                     "failed to reset %s, waiters %d\n",
321                                     am->am_name, am->am_waiters));
322                                 mtx_unlock(&am->am_lock);
323                                 return_VOID;
324                         }
325                         if (ACPIMTX_AVAIL(am))
326                                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
327                                     "wait %s, waiters %d\n",
328                                     am->am_name, am->am_waiters));
329                         else
330                                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
331                                     "wait %s, owner %p, waiters %d\n",
332                                     am->am_name, am->am_owner, am->am_waiters));
333                 }
334         }
335
336         mtx_unlock(&am->am_lock);
337
338         mtx_destroy(&am->am_lock);
339         free(am, M_ACPISEM);
340 }
341
342 ACPI_STATUS
343 AcpiOsAcquireMutex(ACPI_MUTEX Handle, UINT16 Timeout)
344 {
345         struct acpi_mutex       *am = (struct acpi_mutex *)Handle;
346         int                     error, prevtick, slptick, tmo;
347         ACPI_STATUS             status = AE_OK;
348
349         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
350
351         if (am == NULL)
352                 return_ACPI_STATUS (AE_BAD_PARAMETER);
353
354         mtx_lock(&am->am_lock);
355
356         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "acquire %s\n", am->am_name));
357
358         if (ACPIMTX_OWNED(am)) {
359                 am->am_nested++;
360                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
361                     "acquire nested %s, depth %d\n",
362                     am->am_name, am->am_nested));
363                 mtx_unlock(&am->am_lock);
364                 return_ACPI_STATUS (AE_OK);
365         }
366
367         switch (Timeout) {
368         case ACPI_DO_NOT_WAIT:
369                 if (!ACPIMTX_AVAIL(am))
370                         status = AE_TIME;
371                 break;
372         case ACPI_WAIT_FOREVER:
373                 while (!ACPIMTX_AVAIL(am)) {
374                         am->am_waiters++;
375                         error = mtx_sleep(am, &am->am_lock, PCATCH, "acmtx", 0);
376                         am->am_waiters--;
377                         if (error == EINTR || am->am_reset) {
378                                 status = AE_ERROR;
379                                 break;
380                         }
381                 }
382                 break;
383         default:
384                 tmo = timeout2hz(Timeout);
385                 while (!ACPIMTX_AVAIL(am)) {
386                         prevtick = ticks;
387                         am->am_waiters++;
388                         error = mtx_sleep(am, &am->am_lock, PCATCH,
389                             "acmtx", tmo);
390                         am->am_waiters--;
391                         if (error == EINTR || am->am_reset) {
392                                 status = AE_ERROR;
393                                 break;
394                         }
395                         if (ACPIMTX_AVAIL(am))
396                                 break;
397                         slptick = ticks - prevtick;
398                         if (slptick >= tmo || slptick < 0) {
399                                 status = AE_TIME;
400                                 break;
401                         }
402                         tmo -= slptick;
403                 }
404         }
405         if (ACPI_SUCCESS(status))
406                 am->am_owner = curthread;
407
408         mtx_unlock(&am->am_lock);
409
410         return_ACPI_STATUS (status);
411 }
412
413 void
414 AcpiOsReleaseMutex(ACPI_MUTEX Handle)
415 {
416         struct acpi_mutex       *am = (struct acpi_mutex *)Handle;
417
418         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
419
420         if (am == NULL) {
421                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
422                     "cannot release null mutex\n"));
423                 return_VOID;
424         }
425
426         mtx_lock(&am->am_lock);
427
428         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "release %s\n", am->am_name));
429
430         if (ACPIMTX_OWNED(am)) {
431                 if (am->am_nested > 0) {
432                         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
433                             "release nested %s, depth %d\n",
434                             am->am_name, am->am_nested));
435                         am->am_nested--;
436                 } else
437                         am->am_owner = NULL;
438         } else {
439                 if (ACPIMTX_AVAIL(am))
440                         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
441                             "release already available %s\n", am->am_name));
442                 else
443                         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
444                             "release unowned %s from %p, depth %d\n",
445                             am->am_name, am->am_owner, am->am_nested));
446         }
447         if (am->am_waiters > 0 && ACPIMTX_AVAIL(am))
448                 wakeup_one(am);
449
450         mtx_unlock(&am->am_lock);
451 }
452
453 #undef ACPIMTX_AVAIL
454 #undef ACPIMTX_OWNED
455
456 /*
457  * ACPI_SPINLOCK
458  */
459 struct acpi_spinlock {
460         struct mtx      al_lock;
461         char            al_name[32];
462         int             al_nested;
463 };
464
465 ACPI_STATUS
466 AcpiOsCreateLock(ACPI_SPINLOCK *OutHandle)
467 {
468         struct acpi_spinlock    *al;
469
470         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
471
472         if (OutHandle == NULL)
473                 return_ACPI_STATUS (AE_BAD_PARAMETER);
474
475         if ((al = malloc(sizeof(*al), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
476                 return_ACPI_STATUS (AE_NO_MEMORY);
477
478 #ifdef ACPI_DEBUG
479         if (OutHandle == &AcpiGbl_GpeLock)
480                 snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (GPE)");
481         else if (OutHandle == &AcpiGbl_HardwareLock)
482                 snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (HW)");
483         else
484 #endif
485         snprintf(al->al_name, sizeof(al->al_name), "ACPI lock (%p)", al);
486         mtx_init(&al->al_lock, al->al_name, NULL, MTX_SPIN);
487
488         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "created %s\n", al->al_name));
489
490         *OutHandle = (ACPI_SPINLOCK)al;
491
492         return_ACPI_STATUS (AE_OK);
493 }
494
495 void
496 AcpiOsDeleteLock(ACPI_SPINLOCK Handle)
497 {
498         struct acpi_spinlock    *al = (struct acpi_spinlock *)Handle;
499
500         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
501
502         if (al == NULL) {
503                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
504                     "cannot delete null spinlock\n"));
505                 return_VOID;
506         }
507
508         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "delete %s\n", al->al_name));
509
510         mtx_destroy(&al->al_lock);
511         free(al, M_ACPISEM);
512 }
513
514 ACPI_CPU_FLAGS
515 AcpiOsAcquireLock(ACPI_SPINLOCK Handle)
516 {
517         struct acpi_spinlock    *al = (struct acpi_spinlock *)Handle;
518
519         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
520
521         if (al == NULL) {
522                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
523                     "cannot acquire null spinlock\n"));
524                 return (0);
525         }
526
527         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "acquire %s\n", al->al_name));
528
529         if (mtx_owned(&al->al_lock)) {
530                 al->al_nested++;
531                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
532                     "acquire nested %s, depth %d\n",
533                     al->al_name, al->al_nested));
534         } else
535                 mtx_lock_spin(&al->al_lock);
536
537         return (0);
538 }
539
540 void
541 AcpiOsReleaseLock(ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags)
542 {
543         struct acpi_spinlock    *al = (struct acpi_spinlock *)Handle;
544
545         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
546
547         if (al == NULL) {
548                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
549                     "cannot release null spinlock\n"));
550                 return_VOID;
551         }
552
553         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "release %s\n", al->al_name));
554
555         if (mtx_owned(&al->al_lock)) {
556                 if (al->al_nested > 0) {
557                         ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
558                             "release nested %s, depth %d\n",
559                             al->al_name, al->al_nested));
560                         al->al_nested--;
561                 } else
562                         mtx_unlock_spin(&al->al_lock);
563         } else
564                 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
565                     "cannot release unowned %s\n", al->al_name));
566 }
567
568 /* Section 5.2.10.1: global lock acquire/release functions */
569
570 /*
571  * Acquire the global lock.  If busy, set the pending bit.  The caller
572  * will wait for notification from the BIOS that the lock is available
573  * and then attempt to acquire it again.
574  */
575 int
576 acpi_acquire_global_lock(volatile uint32_t *lock)
577 {
578         uint32_t        new, old;
579
580         do {
581                 old = *lock;
582                 new = (old & ~ACPI_GLOCK_PENDING) | ACPI_GLOCK_OWNED;
583                 if ((old & ACPI_GLOCK_OWNED) != 0)
584                         new |= ACPI_GLOCK_PENDING;
585         } while (atomic_cmpset_32(lock, old, new) == 0);
586
587         return ((new & ACPI_GLOCK_PENDING) == 0);
588 }
589
590 /*
591  * Release the global lock, returning whether there is a waiter pending.
592  * If the BIOS set the pending bit, OSPM must notify the BIOS when it
593  * releases the lock.
594  */
595 int
596 acpi_release_global_lock(volatile uint32_t *lock)
597 {
598         uint32_t        new, old;
599
600         do {
601                 old = *lock;
602                 new = old & ~(ACPI_GLOCK_PENDING | ACPI_GLOCK_OWNED);
603         } while (atomic_cmpset_32(lock, old, new) == 0);
604
605         return ((old & ACPI_GLOCK_PENDING) != 0);
606 }