OsdSynch.c revision 193530
18705Sjkh/*- 28705Sjkh * Copyright (c) 2000 Michael Smith 38705Sjkh * Copyright (c) 2000 BSDi 48705Sjkh * All rights reserved. 58705Sjkh * 68705Sjkh * Redistribution and use in source and binary forms, with or without 712661Speter * modification, are permitted provided that the following conditions 88705Sjkh * are met: 98705Sjkh * 1. Redistributions of source code must retain the above copyright 108705Sjkh * notice, this list of conditions and the following disclaimer. 118705Sjkh * 2. Redistributions in binary form must reproduce the above copyright 128705Sjkh * notice, this list of conditions and the following disclaimer in the 138705Sjkh * documentation and/or other materials provided with the distribution. 148705Sjkh * 158705Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 168881Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 178881Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 188705Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 198705Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 208705Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 218705Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 228705Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 238705Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 248705Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 258705Sjkh * SUCH DAMAGE. 268705Sjkh */ 278705Sjkh 288705Sjkh/* 298705Sjkh * 6.1 : Mutual Exclusion and Synchronisation 308705Sjkh */ 318705Sjkh 328705Sjkh#include <sys/cdefs.h> 338705Sjkh__FBSDID("$FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 193530 2009-06-05 18:44:36Z jkim $"); 348705Sjkh 358705Sjkh#include <contrib/dev/acpica/include/acpi.h> 368705Sjkh#include <contrib/dev/acpica/include/accommon.h> 378705Sjkh 388705Sjkh#include "opt_acpi.h" 398705Sjkh#include <sys/kernel.h> 408705Sjkh#include <sys/malloc.h> 418705Sjkh#include <sys/sysctl.h> 428705Sjkh#include <sys/lock.h> 438705Sjkh#include <sys/mutex.h> 448705Sjkh 458705Sjkh#define _COMPONENT ACPI_OS_SERVICES 468768SjkhACPI_MODULE_NAME("SYNCH") 478705Sjkh 488705SjkhMALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 498705Sjkh 508705Sjkh#define AS_LOCK(as) mtx_lock(&(as)->as_mtx) 518705Sjkh#define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx) 528705Sjkh 5312661Speter/* 548705Sjkh * Simple counting semaphore implemented using a mutex. (Subsequently used 5512661Speter * in the OSI code to implement a mutex. Go figure.) 568705Sjkh */ 5712661Speterstruct acpi_semaphore { 5812661Speter struct mtx as_mtx; 5912661Speter UINT32 as_units; 6012661Speter UINT32 as_maxunits; 6112661Speter UINT32 as_pendings; 6212661Speter UINT32 as_resetting; 638705Sjkh UINT32 as_timeouts; 6412661Speter}; 6512661Speter 668705Sjkh/* Default number of maximum pending threads. */ 678705Sjkh#ifndef ACPI_NO_SEMAPHORES 6812661Speter#ifndef ACPI_SEMAPHORES_MAX_PENDING 698705Sjkh#define ACPI_SEMAPHORES_MAX_PENDING 4 708705Sjkh#endif 7112661Speter 7212661Speterstatic int acpi_semaphore_debug = 0; 7312661SpeterTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 7412661SpeterSYSCTL_DECL(_debug_acpi); 7512661SpeterSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW, 7612661Speter &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages"); 7712661Speter#endif /* !ACPI_NO_SEMAPHORES */ 7812661Speter 7912661SpeterACPI_STATUS 8012661SpeterAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, 8112661Speter ACPI_SEMAPHORE *OutHandle) 8212661Speter{ 8312661Speter#ifndef ACPI_NO_SEMAPHORES 8412661Speter struct acpi_semaphore *as; 8512661Speter 8612661Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 8712661Speter 888705Sjkh if (OutHandle == NULL) 8912661Speter return_ACPI_STATUS (AE_BAD_PARAMETER); 908751Sjkh if (InitialUnits > MaxUnits) 9112661Speter return_ACPI_STATUS (AE_BAD_PARAMETER); 928751Sjkh 938751Sjkh if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL) 948751Sjkh return_ACPI_STATUS (AE_NO_MEMORY); 9512661Speter 9612661Speter mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 978751Sjkh as->as_units = InitialUnits; 988751Sjkh as->as_maxunits = MaxUnits; 9912661Speter as->as_pendings = as->as_resetting = as->as_timeouts = 0; 1008751Sjkh 1018751Sjkh ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 1028751Sjkh "created semaphore %p max %d, initial %d\n", 1038705Sjkh as, InitialUnits, MaxUnits)); 1048705Sjkh 1059202Srgrimes *OutHandle = (ACPI_HANDLE)as; 1069202Srgrimes#else 1079202Srgrimes *OutHandle = (ACPI_HANDLE)OutHandle; 1088705Sjkh#endif /* !ACPI_NO_SEMAPHORES */ 1098705Sjkh 1108705Sjkh return_ACPI_STATUS (AE_OK); 1118705Sjkh} 1128705Sjkh 1138705SjkhACPI_STATUS 1148705SjkhAcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle) 1158756Sjkh{ 1168705Sjkh#ifndef ACPI_NO_SEMAPHORES 1178705Sjkh struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 1188705Sjkh 1198705Sjkh ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1208705Sjkh 1218705Sjkh ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 1228705Sjkh mtx_destroy(&as->as_mtx); 1239202Srgrimes free(Handle, M_ACPISEM); 1248705Sjkh#endif /* !ACPI_NO_SEMAPHORES */ 1258705Sjkh 1268705Sjkh return_ACPI_STATUS (AE_OK); 1278705Sjkh} 1288705Sjkh 1298705Sjkh/* 1308705Sjkh * This implementation has a bug, in that it has to stall for the entire 1318705Sjkh * timeout before it will return AE_TIME. A better implementation would 1328705Sjkh * use getmicrotime() to correctly adjust the timeout after being woken up. 1338705Sjkh */ 1348705SjkhACPI_STATUS 1358820SjkhAcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout) 13610882Speter{ 1378705Sjkh#ifndef ACPI_NO_SEMAPHORES 1388705Sjkh ACPI_STATUS result; 1398705Sjkh struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 1408705Sjkh int rv, tmo; 1418705Sjkh struct timeval timeouttv, currenttv, timelefttv; 1428705Sjkh 1439202Srgrimes ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1449202Srgrimes 1459202Srgrimes if (as == NULL) 1468705Sjkh return_ACPI_STATUS (AE_BAD_PARAMETER); 1478705Sjkh 14812661Speter if (cold) 1498722Sjkh return_ACPI_STATUS (AE_OK); 1508705Sjkh 1518705Sjkh#if 0 1528705Sjkh if (as->as_units < Units && as->as_timeouts > 10) { 1538705Sjkh printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 1548705Sjkh AS_LOCK(as); 1558705Sjkh as->as_units = as->as_maxunits; 1568705Sjkh if (as->as_pendings) 1579202Srgrimes as->as_resetting = 1; 1589202Srgrimes as->as_timeouts = 0; 15912661Speter wakeup(as); 16012661Speter AS_UNLOCK(as); 16112661Speter return_ACPI_STATUS (AE_TIME); 16212661Speter } 16312661Speter 16412661Speter if (as->as_resetting) 16512661Speter return_ACPI_STATUS (AE_TIME); 1669202Srgrimes#endif 16712661Speter 1688705Sjkh /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 1698705Sjkh if (Timeout == ACPI_WAIT_FOREVER) { 17012661Speter tmo = 0; 1718705Sjkh timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 17212661Speter timeouttv.tv_usec = 0; 1738705Sjkh } else { 1748705Sjkh /* compute timeout using microseconds per tick */ 1758705Sjkh tmo = (Timeout * 1000) / (1000000 / hz); 1769202Srgrimes if (tmo <= 0) 1778705Sjkh tmo = 1; 1788705Sjkh timeouttv.tv_sec = Timeout / 1000; 1798705Sjkh timeouttv.tv_usec = (Timeout % 1000) * 1000; 1808705Sjkh } 1818705Sjkh 1828705Sjkh /* calculate timeout value in timeval */ 1838751Sjkh getmicrotime(¤ttv); 1848751Sjkh timevaladd(&timeouttv, ¤ttv); 1858751Sjkh 1869202Srgrimes AS_LOCK(as); 1878705Sjkh ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 1888751Sjkh "get %d units from semaphore %p (has %d), timeout %d\n", 1898751Sjkh Units, as, as->as_units, Timeout)); 1909202Srgrimes for (;;) { 1918751Sjkh if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 1928751Sjkh result = AE_OK; 1938705Sjkh break; 19412661Speter } 19512661Speter if (as->as_units >= Units) { 1968705Sjkh as->as_units -= Units; 1978705Sjkh result = AE_OK; 1988705Sjkh break; 19912661Speter } 20012661Speter 20112661Speter /* limit number of pending threads */ 20212661Speter if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 2038705Sjkh result = AE_TIME; 2048705Sjkh break; 2058705Sjkh } 2068837Sjkh 2079202Srgrimes /* if timeout values of zero is specified, return immediately */ 20812661Speter if (Timeout == 0) { 2099202Srgrimes result = AE_TIME; 2108705Sjkh break; 2119202Srgrimes } 2128705Sjkh 2138705Sjkh ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 2148705Sjkh "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 2158705Sjkh as, &as->as_mtx, PCATCH, tmo)); 2168705Sjkh 2178705Sjkh as->as_pendings++; 2188705Sjkh 21912661Speter if (acpi_semaphore_debug) { 22012661Speter printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 22112661Speter __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 22212661Speter } 22312661Speter 22412661Speter rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 2258705Sjkh 2268705Sjkh as->as_pendings--; 2278705Sjkh 2288705Sjkh#if 0 2298705Sjkh if (as->as_resetting) { 2308705Sjkh /* semaphore reset, return immediately */ 2318705Sjkh if (as->as_pendings == 0) { 23212661Speter as->as_resetting = 0; 23312661Speter } 23412661Speter result = AE_TIME; 23512661Speter break; 23612661Speter } 23712661Speter#endif 2388705Sjkh 2398705Sjkh ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 2409202Srgrimes if (rv == EWOULDBLOCK) { 2419202Srgrimes result = AE_TIME; 24212661Speter break; 2438705Sjkh } 2448705Sjkh 2458722Sjkh /* check if we already awaited enough */ 2468722Sjkh timelefttv = timeouttv; 2478722Sjkh getmicrotime(¤ttv); 2488722Sjkh timevalsub(&timelefttv, ¤ttv); 24912661Speter if (timelefttv.tv_sec < 0) { 25012661Speter ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", 2518705Sjkh as)); 2528722Sjkh result = AE_TIME; 2538705Sjkh break; 2548722Sjkh } 25512661Speter 2568722Sjkh /* adjust timeout for the next sleep */ 2578722Sjkh tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / 25812661Speter (1000000 / hz); 2598722Sjkh if (tmo <= 0) 2608722Sjkh tmo = 1; 2618722Sjkh 26212661Speter if (acpi_semaphore_debug) { 26312661Speter printf("%s: Wakeup timeleft(%jd, %lu), tmo %u, sem %p, thread %d\n", 26412661Speter __func__, (intmax_t)timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, 2658722Sjkh AcpiOsGetThreadId()); 2668722Sjkh } 26712661Speter } 26812661Speter 26912661Speter if (acpi_semaphore_debug) { 27012661Speter if (result == AE_TIME && Timeout > 0) { 27112661Speter printf("%s: Timeout %d, pending %d, semaphore %p\n", 2728756Sjkh __func__, Timeout, as->as_pendings, as); 2738722Sjkh } 2748722Sjkh if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 2758722Sjkh printf("%s: Acquire %d, units %d, pending %d, sem %p, thread %d\n", 27612661Speter __func__, Units, as->as_units, as->as_pendings, as, 27712661Speter AcpiOsGetThreadId()); 2788722Sjkh } 2798722Sjkh } 28012661Speter 2818756Sjkh if (result == AE_TIME) 28212661Speter as->as_timeouts++; 28312661Speter else 2848756Sjkh as->as_timeouts = 0; 28512661Speter 28612661Speter AS_UNLOCK(as); 2878756Sjkh return_ACPI_STATUS (result); 2888756Sjkh#else 2898756Sjkh return_ACPI_STATUS (AE_OK); 29012661Speter#endif /* !ACPI_NO_SEMAPHORES */ 2918756Sjkh} 29212661Speter 2938773SjkhACPI_STATUS 29412661SpeterAcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units) 2958756Sjkh{ 2968722Sjkh#ifndef ACPI_NO_SEMAPHORES 2978722Sjkh struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 29812661Speter 29912661Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 30012661Speter 3018756Sjkh if (as == NULL) 3028768Sjkh return_ACPI_STATUS(AE_BAD_PARAMETER); 30312661Speter 30412661Speter AS_LOCK(as); 3058756Sjkh ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 3068768Sjkh "return %d units to semaphore %p (has %d)\n", 30712661Speter Units, as, as->as_units)); 3088768Sjkh if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 3098768Sjkh as->as_units += Units; 3108768Sjkh if (as->as_units > as->as_maxunits) 3118768Sjkh as->as_units = as->as_maxunits; 3128768Sjkh } 3138768Sjkh 31412661Speter if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 3158768Sjkh printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 31612661Speter __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 31712661Speter } 31812661Speter 3198768Sjkh wakeup(as); 3208768Sjkh AS_UNLOCK(as); 32112661Speter#endif /* !ACPI_NO_SEMAPHORES */ 3228768Sjkh 32312661Speter return_ACPI_STATUS (AE_OK); 3248768Sjkh} 3258756Sjkh 3268705Sjkh/* Combined mutex + mutex name storage since the latter must persist. */ 3278705Sjkhstruct acpi_spinlock { 3288722Sjkh struct mtx lock; 3298722Sjkh char name[32]; 3308722Sjkh}; 33112661Speter 33212661SpeterACPI_STATUS 3338722SjkhAcpiOsCreateLock (ACPI_SPINLOCK *OutHandle) 3348722Sjkh{ 3359202Srgrimes struct acpi_spinlock *h; 3369202Srgrimes 3379202Srgrimes if (OutHandle == NULL) 33812661Speter return (AE_BAD_PARAMETER); 3399202Srgrimes h = malloc(sizeof(*h), M_ACPISEM, M_NOWAIT | M_ZERO); 3409202Srgrimes if (h == NULL) 3418705Sjkh return (AE_NO_MEMORY); 3428722Sjkh 3438705Sjkh /* Build a unique name based on the address of the handle. */ 3448709Sjkh if (OutHandle == &AcpiGbl_GpeLock) 34512661Speter snprintf(h->name, sizeof(h->name), "acpi subsystem GPE lock"); 3468709Sjkh else if (OutHandle == &AcpiGbl_HardwareLock) 3479202Srgrimes snprintf(h->name, sizeof(h->name), "acpi subsystem HW lock"); 3488709Sjkh else 3499202Srgrimes snprintf(h->name, sizeof(h->name), "acpi subsys %p", OutHandle); 35012661Speter mtx_init(&h->lock, h->name, NULL, MTX_DEF|MTX_RECURSE); 35112661Speter *OutHandle = (ACPI_SPINLOCK)h; 35212661Speter return (AE_OK); 35312661Speter} 35412661Speter 35512661Spetervoid 3569202SrgrimesAcpiOsDeleteLock (ACPI_SPINLOCK Handle) 3578709Sjkh{ 35812661Speter struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 35912661Speter 36012661Speter if (Handle == NULL) 36112661Speter return; 36212661Speter mtx_destroy(&h->lock); 36312661Speter free(h, M_ACPISEM); 3648709Sjkh} 3658709Sjkh 36612661Speter/* 3678709Sjkh * The Flags parameter seems to state whether or not caller is an ISR 3688709Sjkh * (and thus can't block) but since we have ithreads, we don't worry 3698709Sjkh * about potentially blocking. 37012661Speter */ 37112661SpeterACPI_CPU_FLAGS 37212661SpeterAcpiOsAcquireLock (ACPI_SPINLOCK Handle) 3738709Sjkh{ 3749202Srgrimes struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 3759202Srgrimes 3769202Srgrimes if (Handle == NULL) 3779202Srgrimes return (0); 37812661Speter mtx_lock(&h->lock); 37912661Speter return (0); 38012661Speter} 38112661Speter 38212661Spetervoid 38312661SpeterAcpiOsReleaseLock (ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags) 38412661Speter{ 38512661Speter struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 38612661Speter 38712661Speter if (Handle == NULL) 38812661Speter return; 38912661Speter mtx_unlock(&h->lock); 39012661Speter} 39112661Speter 39212661Speter/* Section 5.2.9.1: global lock acquire/release functions */ 39312661Speter#define GL_ACQUIRED (-1) 3949202Srgrimes#define GL_BUSY 0 3959202Srgrimes#define GL_BIT_PENDING 0x1 39612661Speter#define GL_BIT_OWNED 0x2 3979202Srgrimes#define GL_BIT_MASK (GL_BIT_PENDING | GL_BIT_OWNED) 3988705Sjkh 3998715Sjkh/* 4008715Sjkh * Acquire the global lock. If busy, set the pending bit. The caller 4019202Srgrimes * will wait for notification from the BIOS that the lock is available 4029202Srgrimes * and then attempt to acquire it again. 40312661Speter */ 40412661Speterint 40512661Speteracpi_acquire_global_lock(uint32_t *lock) 4069202Srgrimes{ 4079202Srgrimes uint32_t new, old; 4089202Srgrimes 4098722Sjkh do { 4108715Sjkh old = *lock; 41112661Speter new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) | 41212661Speter ((old >> 1) & GL_BIT_PENDING); 4138768Sjkh } while (atomic_cmpset_acq_int(lock, old, new) == 0); 41412661Speter 41512661Speter return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY); 41612661Speter} 41712661Speter 41812661Speter/* 41912661Speter * Release the global lock, returning whether there is a waiter pending. 42012661Speter * If the BIOS set the pending bit, OSPM must notify the BIOS when it 42112661Speter * releases the lock. 42212661Speter */ 42312661Speterint 42412661Speteracpi_release_global_lock(uint32_t *lock) 42512661Speter{ 42612661Speter uint32_t new, old; 42712661Speter 42812661Speter do { 42912661Speter old = *lock; 43012661Speter new = old & ~GL_BIT_MASK; 43112661Speter } while (atomic_cmpset_rel_int(lock, old, new) == 0); 43212661Speter 43312661Speter return (old & GL_BIT_PENDING); 43412661Speter} 43512661Speter