OsdSynch.c revision 151948
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 151948 2005-11-01 22:44:08Z jkim $"); 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 6588420Siwasaki#ifndef ACPI_NO_SEMAPHORES 6688420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING 6788420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING 4 6888420Siwasaki#endif 6988420Siwasakistatic int acpi_semaphore_debug = 0; 7088420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 71120494SnjlSYSCTL_DECL(_debug_acpi); 72120494SnjlSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW, 73120494Snjl &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages"); 74128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 7588420Siwasaki 7667760SmsmithACPI_STATUS 77128227SnjlAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, 78128227Snjl ACPI_HANDLE *OutHandle) 7967760Smsmith{ 8071876Smsmith#ifndef ACPI_NO_SEMAPHORES 8167760Smsmith struct acpi_semaphore *as; 8267760Smsmith 8396926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 8471876Smsmith 8567760Smsmith if (OutHandle == NULL) 86128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 8767760Smsmith if (InitialUnits > MaxUnits) 88128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 8967760Smsmith 90128227Snjl if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL) 91128227Snjl return_ACPI_STATUS (AE_NO_MEMORY); 9267760Smsmith 9393818Sjhb mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 9467760Smsmith as->as_units = InitialUnits; 9567760Smsmith as->as_maxunits = MaxUnits; 9688420Siwasaki as->as_pendings = as->as_resetting = as->as_timeouts = 0; 9767760Smsmith 9888420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 9988420Siwasaki "created semaphore %p max %d, initial %d\n", 10088420Siwasaki as, InitialUnits, MaxUnits)); 10171876Smsmith 10267760Smsmith *OutHandle = (ACPI_HANDLE)as; 10371876Smsmith#else 10471876Smsmith *OutHandle = (ACPI_HANDLE)OutHandle; 105128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 106128227Snjl 107128227Snjl return_ACPI_STATUS (AE_OK); 10867760Smsmith} 10967760Smsmith 11067760SmsmithACPI_STATUS 111128227SnjlAcpiOsDeleteSemaphore(ACPI_HANDLE Handle) 11267760Smsmith{ 11371876Smsmith#ifndef ACPI_NO_SEMAPHORES 11471359Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 11571876Smsmith 11696926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 11771876Smsmith 11888420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 11971359Smsmith mtx_destroy(&as->as_mtx); 12067760Smsmith free(Handle, M_ACPISEM); 121128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 122128227Snjl 123128227Snjl return_ACPI_STATUS (AE_OK); 12467760Smsmith} 12567760Smsmith 12667760Smsmith/* 12767760Smsmith * This implementation has a bug, in that it has to stall for the entire 12867760Smsmith * timeout before it will return AE_TIME. A better implementation would 12967760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up. 13067760Smsmith */ 13167760SmsmithACPI_STATUS 132107328SiwasakiAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout) 13367760Smsmith{ 13471876Smsmith#ifndef ACPI_NO_SEMAPHORES 135128227Snjl ACPI_STATUS result; 13667760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 13771876Smsmith int rv, tmo; 13888420Siwasaki struct timeval timeouttv, currenttv, timelefttv; 13967760Smsmith 14096926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 14171876Smsmith 14267760Smsmith if (as == NULL) 143128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 14467760Smsmith 14588420Siwasaki if (cold) 146128227Snjl return_ACPI_STATUS (AE_OK); 14788420Siwasaki 14888420Siwasaki#if 0 14988420Siwasaki if (as->as_units < Units && as->as_timeouts > 10) { 15088420Siwasaki printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 151105278Sjhb AS_LOCK(as); 15288420Siwasaki as->as_units = as->as_maxunits; 15388420Siwasaki if (as->as_pendings) 15488420Siwasaki as->as_resetting = 1; 15588420Siwasaki as->as_timeouts = 0; 15688420Siwasaki wakeup(as); 157105278Sjhb AS_UNLOCK(as); 158128227Snjl return_ACPI_STATUS (AE_TIME); 15988420Siwasaki } 16088420Siwasaki 161128227Snjl if (as->as_resetting) 162128227Snjl return_ACPI_STATUS (AE_TIME); 16388420Siwasaki#endif 16488420Siwasaki 165107328Siwasaki /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 166107328Siwasaki if (Timeout == ACPI_WAIT_FOREVER) { 16771876Smsmith tmo = 0; 16888420Siwasaki timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 16988420Siwasaki timeouttv.tv_usec = 0; 17071876Smsmith } else { 17171876Smsmith /* compute timeout using microseconds per tick */ 17277432Smsmith tmo = (Timeout * 1000) / (1000000 / hz); 17371876Smsmith if (tmo <= 0) 17471876Smsmith tmo = 1; 17588420Siwasaki timeouttv.tv_sec = Timeout / 1000; 17688420Siwasaki timeouttv.tv_usec = (Timeout % 1000) * 1000; 17771876Smsmith } 17871876Smsmith 17988420Siwasaki /* calculate timeout value in timeval */ 18088420Siwasaki getmicrotime(¤ttv); 18188420Siwasaki timevaladd(&timeouttv, ¤ttv); 18288420Siwasaki 183105278Sjhb AS_LOCK(as); 18488420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 18588420Siwasaki "get %d units from semaphore %p (has %d), timeout %d\n", 18688420Siwasaki Units, as, as->as_units, Timeout)); 18767760Smsmith for (;;) { 18899492Siwasaki if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 18980071Smsmith result = AE_OK; 19080071Smsmith break; 19180071Smsmith } 19267760Smsmith if (as->as_units >= Units) { 19367760Smsmith as->as_units -= Units; 19467760Smsmith result = AE_OK; 19567760Smsmith break; 19667760Smsmith } 19788420Siwasaki 19888420Siwasaki /* limit number of pending treads */ 19988420Siwasaki if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 20067760Smsmith result = AE_TIME; 20167760Smsmith break; 20267760Smsmith } 20388420Siwasaki 20488420Siwasaki /* if timeout values of zero is specified, return immediately */ 20588420Siwasaki if (Timeout == 0) { 20688420Siwasaki result = AE_TIME; 20788420Siwasaki break; 20888420Siwasaki } 20988420Siwasaki 21088420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 21188420Siwasaki "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 21288420Siwasaki as, &as->as_mtx, PCATCH, tmo)); 21388420Siwasaki 21488420Siwasaki as->as_pendings++; 21588420Siwasaki 21688420Siwasaki if (acpi_semaphore_debug) { 21788420Siwasaki printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 21888420Siwasaki __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 21988420Siwasaki } 22088420Siwasaki 22188420Siwasaki rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 22288420Siwasaki 22388420Siwasaki as->as_pendings--; 22488420Siwasaki 22588420Siwasaki#if 0 22688420Siwasaki if (as->as_resetting) { 22788420Siwasaki /* semaphore reset, return immediately */ 22888420Siwasaki if (as->as_pendings == 0) { 22988420Siwasaki as->as_resetting = 0; 23088420Siwasaki } 23188420Siwasaki result = AE_TIME; 23288420Siwasaki break; 23388420Siwasaki } 23488420Siwasaki#endif 23588420Siwasaki 23688420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 23771876Smsmith if (rv == EWOULDBLOCK) { 23867760Smsmith result = AE_TIME; 23967760Smsmith break; 24067760Smsmith } 24188420Siwasaki 24288420Siwasaki /* check if we already awaited enough */ 24388420Siwasaki timelefttv = timeouttv; 24488420Siwasaki getmicrotime(¤ttv); 24588420Siwasaki timevalsub(&timelefttv, ¤ttv); 24688420Siwasaki if (timelefttv.tv_sec < 0) { 247128227Snjl ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", 248128227Snjl as)); 24988420Siwasaki result = AE_TIME; 25088420Siwasaki break; 25188420Siwasaki } 25288420Siwasaki 25388420Siwasaki /* adjust timeout for the next sleep */ 254128227Snjl tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / 255128227Snjl (1000000 / hz); 25688420Siwasaki if (tmo <= 0) 25788420Siwasaki tmo = 1; 25888420Siwasaki 25988420Siwasaki if (acpi_semaphore_debug) { 260128227Snjl printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, sem %p, thread %d\n", 261128227Snjl __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, 262128227Snjl AcpiOsGetThreadId()); 26388420Siwasaki } 26467760Smsmith } 26588420Siwasaki 26688420Siwasaki if (acpi_semaphore_debug) { 26788420Siwasaki if (result == AE_TIME && Timeout > 0) { 26888420Siwasaki printf("%s: Timeout %d, pending %d, semaphore %p\n", 26988420Siwasaki __func__, Timeout, as->as_pendings, as); 27088420Siwasaki } 27188420Siwasaki if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 272128227Snjl printf("%s: Acquire %d, units %d, pending %d, sem %p, thread %d\n", 273128227Snjl __func__, Units, as->as_units, as->as_pendings, as, 274128227Snjl AcpiOsGetThreadId()); 27588420Siwasaki } 27688420Siwasaki } 27788420Siwasaki 278128227Snjl if (result == AE_TIME) 27988420Siwasaki as->as_timeouts++; 280128227Snjl else 28188420Siwasaki as->as_timeouts = 0; 28288420Siwasaki 283105278Sjhb AS_UNLOCK(as); 284128227Snjl return_ACPI_STATUS (result); 28571876Smsmith#else 286128227Snjl return_ACPI_STATUS (AE_OK); 287128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 28867760Smsmith} 28967760Smsmith 29067760SmsmithACPI_STATUS 29167760SmsmithAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units) 29267760Smsmith{ 29371876Smsmith#ifndef ACPI_NO_SEMAPHORES 29467760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 29567760Smsmith 29696926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 29771876Smsmith 29867760Smsmith if (as == NULL) 29971876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 30067760Smsmith 301105278Sjhb AS_LOCK(as); 30288420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 30388420Siwasaki "return %d units to semaphore %p (has %d)\n", 30488420Siwasaki Units, as, as->as_units)); 30599492Siwasaki if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 30680071Smsmith as->as_units += Units; 30780071Smsmith if (as->as_units > as->as_maxunits) 30880071Smsmith as->as_units = as->as_maxunits; 30980071Smsmith } 31088420Siwasaki 31188420Siwasaki if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 31288420Siwasaki printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 31388420Siwasaki __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 31488420Siwasaki } 31588420Siwasaki 31667760Smsmith wakeup(as); 317105278Sjhb AS_UNLOCK(as); 318128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 319128227Snjl 320128227Snjl return_ACPI_STATUS (AE_OK); 32167760Smsmith} 322117530Snjl 323117530SnjlACPI_STATUS 324117530SnjlAcpiOsCreateLock (ACPI_HANDLE *OutHandle) 325117530Snjl{ 326117530Snjl struct mtx *m; 327117530Snjl 328117530Snjl if (OutHandle == NULL) 329117530Snjl return (AE_BAD_PARAMETER); 330128227Snjl m = malloc(sizeof(*m), M_ACPISEM, M_NOWAIT | M_ZERO); 331117530Snjl if (m == NULL) 332117530Snjl return (AE_NO_MEMORY); 333117530Snjl 334117530Snjl mtx_init(m, "acpica subsystem lock", NULL, MTX_DEF); 335117530Snjl *OutHandle = (ACPI_HANDLE)m; 336117530Snjl return (AE_OK); 337117530Snjl} 338117530Snjl 339117530Snjlvoid 340117530SnjlAcpiOsDeleteLock (ACPI_HANDLE Handle) 341117530Snjl{ 342117530Snjl struct mtx *m = (struct mtx *)Handle; 343117530Snjl 344117530Snjl if (Handle == NULL) 345117530Snjl return; 346117530Snjl mtx_destroy(m); 347117530Snjl} 348117530Snjl 349117530Snjl/* 350117530Snjl * The Flags parameter seems to state whether or not caller is an ISR 351117530Snjl * (and thus can't block) but since we have ithreads, we don't worry 352117530Snjl * about potentially blocking. 353117530Snjl */ 354151948SjkimACPI_NATIVE_UINT 355151948SjkimAcpiOsAcquireLock (ACPI_HANDLE Handle) 356117530Snjl{ 357117530Snjl struct mtx *m = (struct mtx *)Handle; 358117530Snjl 359117530Snjl if (Handle == NULL) 360151948Sjkim return (0); 361117530Snjl mtx_lock(m); 362151948Sjkim return (0); 363117530Snjl} 364117530Snjl 365117530Snjlvoid 366151948SjkimAcpiOsReleaseLock (ACPI_HANDLE Handle, ACPI_NATIVE_UINT Flags) 367117530Snjl{ 368117530Snjl struct mtx *m = (struct mtx *)Handle; 369117530Snjl 370117530Snjl if (Handle == NULL) 371128227Snjl return; 372117530Snjl mtx_unlock(m); 373117530Snjl} 374128979Snjl 375128979Snjl/* Section 5.2.9.1: global lock acquire/release functions */ 376128979Snjl#define GL_ACQUIRED (-1) 377128979Snjl#define GL_BUSY 0 378128979Snjl#define GL_BIT_PENDING 0x1 379128979Snjl#define GL_BIT_OWNED 0x2 380128979Snjl#define GL_BIT_MASK (GL_BIT_PENDING | GL_BIT_OWNED) 381128979Snjl 382128979Snjl/* 383128979Snjl * Acquire the global lock. If busy, set the pending bit. The caller 384128979Snjl * will wait for notification from the BIOS that the lock is available 385128979Snjl * and then attempt to acquire it again. 386128979Snjl */ 387128979Snjlint 388128979Snjlacpi_acquire_global_lock(uint32_t *lock) 389128979Snjl{ 390128979Snjl uint32_t new, old; 391128979Snjl 392128979Snjl do { 393128979Snjl old = *lock; 394128981Snjl new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) | 395128981Snjl ((old >> 1) & GL_BIT_PENDING); 396128979Snjl } while (atomic_cmpset_acq_int(lock, old, new) == 0); 397128979Snjl 398128979Snjl return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY); 399128979Snjl} 400128979Snjl 401128979Snjl/* 402128979Snjl * Release the global lock, returning whether there is a waiter pending. 403128979Snjl * If the BIOS set the pending bit, OSPM must notify the BIOS when it 404128979Snjl * releases the lock. 405128979Snjl */ 406128979Snjlint 407128979Snjlacpi_release_global_lock(uint32_t *lock) 408128979Snjl{ 409128979Snjl uint32_t new, old; 410128979Snjl 411128979Snjl do { 412128979Snjl old = *lock; 413128979Snjl new = old & ~GL_BIT_MASK; 414128979Snjl } while (atomic_cmpset_rel_int(lock, old, new) == 0); 415128979Snjl 416128979Snjl return (old & GL_BIT_PENDING); 417128979Snjl} 418