OsdSynch.c revision 177934
167760Smsmith/*- 267760Smsmith * Copyright (c) 2000 Michael Smith 367760Smsmith * Copyright (c) 2000 BSDi 467760Smsmith * All rights reserved. 567760Smsmith * 667760Smsmith * Redistribution and use in source and binary forms, with or without 767760Smsmith * modification, are permitted provided that the following conditions 867760Smsmith * are met: 967760Smsmith * 1. Redistributions of source code must retain the above copyright 1067760Smsmith * notice, this list of conditions and the following disclaimer. 1167760Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1267760Smsmith * notice, this list of conditions and the following disclaimer in the 1367760Smsmith * documentation and/or other materials provided with the distribution. 1467760Smsmith * 1567760Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1667760Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1767760Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1867760Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1967760Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2067760Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2167760Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2267760Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2367760Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2467760Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2567760Smsmith * SUCH DAMAGE. 2667760Smsmith */ 2767760Smsmith 2867760Smsmith/* 2967760Smsmith * 6.1 : Mutual Exclusion and Synchronisation 3067760Smsmith */ 3167760Smsmith 32148318Snjl#include <sys/cdefs.h> 33148318Snjl__FBSDID("$FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 177934 2008-04-05 14:21:01Z takawata $"); 34148318Snjl 35150003Sobrien#include <contrib/dev/acpica/acpi.h> 3667760Smsmith 3788420Siwasaki#include "opt_acpi.h" 3867760Smsmith#include <sys/kernel.h> 39105278Sjhb#include <sys/malloc.h> 40105278Sjhb#include <sys/sysctl.h> 4174914Sjhb#include <sys/lock.h> 4267760Smsmith#include <sys/mutex.h> 4367760Smsmith 4477432Smsmith#define _COMPONENT ACPI_OS_SERVICES 4591128SmsmithACPI_MODULE_NAME("SYNCH") 4671876Smsmith 47128227SnjlMALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 4867760Smsmith 49130695Snjl#define AS_LOCK(as) mtx_lock(&(as)->as_mtx) 50130695Snjl#define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx) 51105278Sjhb 5267760Smsmith/* 53128227Snjl * Simple counting semaphore implemented using a mutex. (Subsequently used 5467760Smsmith * in the OSI code to implement a mutex. Go figure.) 5567760Smsmith */ 5667760Smsmithstruct acpi_semaphore { 5767760Smsmith struct mtx as_mtx; 5867760Smsmith UINT32 as_units; 5967760Smsmith UINT32 as_maxunits; 6088420Siwasaki UINT32 as_pendings; 6188420Siwasaki UINT32 as_resetting; 6288420Siwasaki UINT32 as_timeouts; 6367760Smsmith}; 6467760Smsmith 65167814Sjkim/* Default number of maximum pending threads. */ 6688420Siwasaki#ifndef ACPI_NO_SEMAPHORES 6788420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING 6888420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING 4 6988420Siwasaki#endif 70167814Sjkim 7188420Siwasakistatic int acpi_semaphore_debug = 0; 7288420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 73120494SnjlSYSCTL_DECL(_debug_acpi); 74120494SnjlSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW, 75120494Snjl &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages"); 76128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 7788420Siwasaki 7867760SmsmithACPI_STATUS 79128227SnjlAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, 80167915Sjkim ACPI_SEMAPHORE *OutHandle) 8167760Smsmith{ 8271876Smsmith#ifndef ACPI_NO_SEMAPHORES 8367760Smsmith struct acpi_semaphore *as; 8467760Smsmith 8596926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 8671876Smsmith 8767760Smsmith if (OutHandle == NULL) 88128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 8967760Smsmith if (InitialUnits > MaxUnits) 90128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 9167760Smsmith 92128227Snjl if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL) 93128227Snjl return_ACPI_STATUS (AE_NO_MEMORY); 9467760Smsmith 9593818Sjhb mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 9667760Smsmith as->as_units = InitialUnits; 9767760Smsmith as->as_maxunits = MaxUnits; 9888420Siwasaki as->as_pendings = as->as_resetting = as->as_timeouts = 0; 9967760Smsmith 10088420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 10188420Siwasaki "created semaphore %p max %d, initial %d\n", 10288420Siwasaki as, InitialUnits, MaxUnits)); 10371876Smsmith 10467760Smsmith *OutHandle = (ACPI_HANDLE)as; 10571876Smsmith#else 10671876Smsmith *OutHandle = (ACPI_HANDLE)OutHandle; 107128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 108128227Snjl 109128227Snjl return_ACPI_STATUS (AE_OK); 11067760Smsmith} 11167760Smsmith 11267760SmsmithACPI_STATUS 113167915SjkimAcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle) 11467760Smsmith{ 11571876Smsmith#ifndef ACPI_NO_SEMAPHORES 11671359Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 11771876Smsmith 11896926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 11971876Smsmith 12088420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 12171359Smsmith mtx_destroy(&as->as_mtx); 12267760Smsmith free(Handle, M_ACPISEM); 123128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 124128227Snjl 125128227Snjl return_ACPI_STATUS (AE_OK); 12667760Smsmith} 12767760Smsmith 12867760Smsmith/* 12967760Smsmith * This implementation has a bug, in that it has to stall for the entire 13067760Smsmith * timeout before it will return AE_TIME. A better implementation would 13167760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up. 13267760Smsmith */ 13367760SmsmithACPI_STATUS 134167915SjkimAcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout) 13567760Smsmith{ 13671876Smsmith#ifndef ACPI_NO_SEMAPHORES 137128227Snjl ACPI_STATUS result; 13867760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 13971876Smsmith int rv, tmo; 14088420Siwasaki struct timeval timeouttv, currenttv, timelefttv; 14167760Smsmith 14296926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 14371876Smsmith 14467760Smsmith if (as == NULL) 145128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 14667760Smsmith 14788420Siwasaki if (cold) 148128227Snjl return_ACPI_STATUS (AE_OK); 14988420Siwasaki 15088420Siwasaki#if 0 15188420Siwasaki if (as->as_units < Units && as->as_timeouts > 10) { 15288420Siwasaki printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 153105278Sjhb AS_LOCK(as); 15488420Siwasaki as->as_units = as->as_maxunits; 15588420Siwasaki if (as->as_pendings) 15688420Siwasaki as->as_resetting = 1; 15788420Siwasaki as->as_timeouts = 0; 15888420Siwasaki wakeup(as); 159105278Sjhb AS_UNLOCK(as); 160128227Snjl return_ACPI_STATUS (AE_TIME); 16188420Siwasaki } 16288420Siwasaki 163128227Snjl if (as->as_resetting) 164128227Snjl return_ACPI_STATUS (AE_TIME); 16588420Siwasaki#endif 16688420Siwasaki 167107328Siwasaki /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 168107328Siwasaki if (Timeout == ACPI_WAIT_FOREVER) { 16971876Smsmith tmo = 0; 17088420Siwasaki timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 17188420Siwasaki timeouttv.tv_usec = 0; 17271876Smsmith } else { 17371876Smsmith /* compute timeout using microseconds per tick */ 17477432Smsmith tmo = (Timeout * 1000) / (1000000 / hz); 17571876Smsmith if (tmo <= 0) 17671876Smsmith tmo = 1; 17788420Siwasaki timeouttv.tv_sec = Timeout / 1000; 17888420Siwasaki timeouttv.tv_usec = (Timeout % 1000) * 1000; 17971876Smsmith } 18071876Smsmith 18188420Siwasaki /* calculate timeout value in timeval */ 18288420Siwasaki getmicrotime(¤ttv); 18388420Siwasaki timevaladd(&timeouttv, ¤ttv); 18488420Siwasaki 185105278Sjhb AS_LOCK(as); 18688420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 18788420Siwasaki "get %d units from semaphore %p (has %d), timeout %d\n", 18888420Siwasaki Units, as, as->as_units, Timeout)); 18967760Smsmith for (;;) { 19099492Siwasaki if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 19180071Smsmith result = AE_OK; 19280071Smsmith break; 19380071Smsmith } 19467760Smsmith if (as->as_units >= Units) { 19567760Smsmith as->as_units -= Units; 19667760Smsmith result = AE_OK; 19767760Smsmith break; 19867760Smsmith } 19988420Siwasaki 200167814Sjkim /* limit number of pending threads */ 20188420Siwasaki if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 20267760Smsmith result = AE_TIME; 20367760Smsmith break; 20467760Smsmith } 20588420Siwasaki 20688420Siwasaki /* if timeout values of zero is specified, return immediately */ 20788420Siwasaki if (Timeout == 0) { 20888420Siwasaki result = AE_TIME; 20988420Siwasaki break; 21088420Siwasaki } 21188420Siwasaki 21288420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 21388420Siwasaki "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 21488420Siwasaki as, &as->as_mtx, PCATCH, tmo)); 21588420Siwasaki 21688420Siwasaki as->as_pendings++; 21788420Siwasaki 21888420Siwasaki if (acpi_semaphore_debug) { 21988420Siwasaki printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 22088420Siwasaki __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 22188420Siwasaki } 22288420Siwasaki 22388420Siwasaki rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 22488420Siwasaki 22588420Siwasaki as->as_pendings--; 22688420Siwasaki 22788420Siwasaki#if 0 22888420Siwasaki if (as->as_resetting) { 22988420Siwasaki /* semaphore reset, return immediately */ 23088420Siwasaki if (as->as_pendings == 0) { 23188420Siwasaki as->as_resetting = 0; 23288420Siwasaki } 23388420Siwasaki result = AE_TIME; 23488420Siwasaki break; 23588420Siwasaki } 23688420Siwasaki#endif 23788420Siwasaki 23888420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 23971876Smsmith if (rv == EWOULDBLOCK) { 24067760Smsmith result = AE_TIME; 24167760Smsmith break; 24267760Smsmith } 24388420Siwasaki 24488420Siwasaki /* check if we already awaited enough */ 24588420Siwasaki timelefttv = timeouttv; 24688420Siwasaki getmicrotime(¤ttv); 24788420Siwasaki timevalsub(&timelefttv, ¤ttv); 24888420Siwasaki if (timelefttv.tv_sec < 0) { 249128227Snjl ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", 250128227Snjl as)); 25188420Siwasaki result = AE_TIME; 25288420Siwasaki break; 25388420Siwasaki } 25488420Siwasaki 25588420Siwasaki /* adjust timeout for the next sleep */ 256128227Snjl tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / 257128227Snjl (1000000 / hz); 25888420Siwasaki if (tmo <= 0) 25988420Siwasaki tmo = 1; 26088420Siwasaki 26188420Siwasaki if (acpi_semaphore_debug) { 262153706Strhodes printf("%s: Wakeup timeleft(%jd, %lu), tmo %u, sem %p, thread %d\n", 263153706Strhodes __func__, (intmax_t)timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, 264128227Snjl AcpiOsGetThreadId()); 26588420Siwasaki } 26667760Smsmith } 26788420Siwasaki 26888420Siwasaki if (acpi_semaphore_debug) { 26988420Siwasaki if (result == AE_TIME && Timeout > 0) { 27088420Siwasaki printf("%s: Timeout %d, pending %d, semaphore %p\n", 27188420Siwasaki __func__, Timeout, as->as_pendings, as); 27288420Siwasaki } 27388420Siwasaki if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 274128227Snjl printf("%s: Acquire %d, units %d, pending %d, sem %p, thread %d\n", 275128227Snjl __func__, Units, as->as_units, as->as_pendings, as, 276128227Snjl AcpiOsGetThreadId()); 27788420Siwasaki } 27888420Siwasaki } 27988420Siwasaki 280128227Snjl if (result == AE_TIME) 28188420Siwasaki as->as_timeouts++; 282128227Snjl else 28388420Siwasaki as->as_timeouts = 0; 28488420Siwasaki 285105278Sjhb AS_UNLOCK(as); 286128227Snjl return_ACPI_STATUS (result); 28771876Smsmith#else 288128227Snjl return_ACPI_STATUS (AE_OK); 289128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 29067760Smsmith} 29167760Smsmith 29267760SmsmithACPI_STATUS 293167915SjkimAcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units) 29467760Smsmith{ 29571876Smsmith#ifndef ACPI_NO_SEMAPHORES 29667760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 29767760Smsmith 29896926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 29971876Smsmith 30067760Smsmith if (as == NULL) 30171876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 30267760Smsmith 303105278Sjhb AS_LOCK(as); 30488420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 30588420Siwasaki "return %d units to semaphore %p (has %d)\n", 30688420Siwasaki Units, as, as->as_units)); 30799492Siwasaki if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 30880071Smsmith as->as_units += Units; 30980071Smsmith if (as->as_units > as->as_maxunits) 31080071Smsmith as->as_units = as->as_maxunits; 31180071Smsmith } 31288420Siwasaki 31388420Siwasaki if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 31488420Siwasaki printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 31588420Siwasaki __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 31688420Siwasaki } 31788420Siwasaki 31867760Smsmith wakeup(as); 319105278Sjhb AS_UNLOCK(as); 320128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 321128227Snjl 322128227Snjl return_ACPI_STATUS (AE_OK); 32367760Smsmith} 324117530Snjl 325167908Snjl/* Combined mutex + mutex name storage since the latter must persist. */ 326167910Sjkimstruct acpi_spinlock { 327167910Sjkim struct mtx lock; 328167910Sjkim char name[32]; 329167908Snjl}; 330167908Snjl 331117530SnjlACPI_STATUS 332167910SjkimAcpiOsCreateLock (ACPI_SPINLOCK *OutHandle) 333117530Snjl{ 334167910Sjkim struct acpi_spinlock *h; 335117530Snjl 336117530Snjl if (OutHandle == NULL) 337117530Snjl return (AE_BAD_PARAMETER); 338167918Sjkim h = malloc(sizeof(*h), M_ACPISEM, M_NOWAIT | M_ZERO); 339167910Sjkim if (h == NULL) 340117530Snjl return (AE_NO_MEMORY); 341117530Snjl 342167908Snjl /* Build a unique name based on the address of the handle. */ 343167910Sjkim if (OutHandle == &AcpiGbl_GpeLock) 344167910Sjkim snprintf(h->name, sizeof(h->name), "acpi subsystem GPE lock"); 345167918Sjkim else if (OutHandle == &AcpiGbl_HardwareLock) 346167910Sjkim snprintf(h->name, sizeof(h->name), "acpi subsystem HW lock"); 347167910Sjkim else 348167910Sjkim snprintf(h->name, sizeof(h->name), "acpi subsys %p", OutHandle); 349177934Stakawata mtx_init(&h->lock, h->name, NULL, MTX_DEF|MTX_RECURSE); 350167910Sjkim *OutHandle = (ACPI_SPINLOCK)h; 351117530Snjl return (AE_OK); 352117530Snjl} 353117530Snjl 354117530Snjlvoid 355167910SjkimAcpiOsDeleteLock (ACPI_SPINLOCK Handle) 356117530Snjl{ 357167910Sjkim struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 358117530Snjl 359117530Snjl if (Handle == NULL) 360117530Snjl return; 361167910Sjkim mtx_destroy(&h->lock); 362167911Sjkim free(h, M_ACPISEM); 363117530Snjl} 364117530Snjl 365117530Snjl/* 366117530Snjl * The Flags parameter seems to state whether or not caller is an ISR 367117530Snjl * (and thus can't block) but since we have ithreads, we don't worry 368117530Snjl * about potentially blocking. 369117530Snjl */ 370151948SjkimACPI_NATIVE_UINT 371167910SjkimAcpiOsAcquireLock (ACPI_SPINLOCK Handle) 372117530Snjl{ 373167910Sjkim struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 374117530Snjl 375117530Snjl if (Handle == NULL) 376151948Sjkim return (0); 377167910Sjkim mtx_lock(&h->lock); 378151948Sjkim return (0); 379117530Snjl} 380117530Snjl 381117530Snjlvoid 382167910SjkimAcpiOsReleaseLock (ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags) 383117530Snjl{ 384167910Sjkim struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 385117530Snjl 386117530Snjl if (Handle == NULL) 387128227Snjl return; 388167910Sjkim mtx_unlock(&h->lock); 389117530Snjl} 390128979Snjl 391128979Snjl/* Section 5.2.9.1: global lock acquire/release functions */ 392128979Snjl#define GL_ACQUIRED (-1) 393128979Snjl#define GL_BUSY 0 394128979Snjl#define GL_BIT_PENDING 0x1 395128979Snjl#define GL_BIT_OWNED 0x2 396128979Snjl#define GL_BIT_MASK (GL_BIT_PENDING | GL_BIT_OWNED) 397128979Snjl 398128979Snjl/* 399128979Snjl * Acquire the global lock. If busy, set the pending bit. The caller 400128979Snjl * will wait for notification from the BIOS that the lock is available 401128979Snjl * and then attempt to acquire it again. 402128979Snjl */ 403128979Snjlint 404128979Snjlacpi_acquire_global_lock(uint32_t *lock) 405128979Snjl{ 406128979Snjl uint32_t new, old; 407128979Snjl 408128979Snjl do { 409128979Snjl old = *lock; 410128981Snjl new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) | 411128981Snjl ((old >> 1) & GL_BIT_PENDING); 412128979Snjl } while (atomic_cmpset_acq_int(lock, old, new) == 0); 413128979Snjl 414128979Snjl return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY); 415128979Snjl} 416128979Snjl 417128979Snjl/* 418128979Snjl * Release the global lock, returning whether there is a waiter pending. 419128979Snjl * If the BIOS set the pending bit, OSPM must notify the BIOS when it 420128979Snjl * releases the lock. 421128979Snjl */ 422128979Snjlint 423128979Snjlacpi_release_global_lock(uint32_t *lock) 424128979Snjl{ 425128979Snjl uint32_t new, old; 426128979Snjl 427128979Snjl do { 428128979Snjl old = *lock; 429128979Snjl new = old & ~GL_BIT_MASK; 430128979Snjl } while (atomic_cmpset_rel_int(lock, old, new) == 0); 431128979Snjl 432128979Snjl return (old & GL_BIT_PENDING); 433128979Snjl} 434