OsdSynch.c revision 193530
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 193530 2009-06-05 18:44:36Z jkim $"); 34148318Snjl 35193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 36193530Sjkim#include <contrib/dev/acpica/include/accommon.h> 3767760Smsmith 3888420Siwasaki#include "opt_acpi.h" 3967760Smsmith#include <sys/kernel.h> 40105278Sjhb#include <sys/malloc.h> 41105278Sjhb#include <sys/sysctl.h> 4274914Sjhb#include <sys/lock.h> 4367760Smsmith#include <sys/mutex.h> 4467760Smsmith 4577432Smsmith#define _COMPONENT ACPI_OS_SERVICES 4691128SmsmithACPI_MODULE_NAME("SYNCH") 4771876Smsmith 48128227SnjlMALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 4967760Smsmith 50130695Snjl#define AS_LOCK(as) mtx_lock(&(as)->as_mtx) 51130695Snjl#define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx) 52105278Sjhb 5367760Smsmith/* 54128227Snjl * Simple counting semaphore implemented using a mutex. (Subsequently used 5567760Smsmith * in the OSI code to implement a mutex. Go figure.) 5667760Smsmith */ 5767760Smsmithstruct acpi_semaphore { 5867760Smsmith struct mtx as_mtx; 5967760Smsmith UINT32 as_units; 6067760Smsmith UINT32 as_maxunits; 6188420Siwasaki UINT32 as_pendings; 6288420Siwasaki UINT32 as_resetting; 6388420Siwasaki UINT32 as_timeouts; 6467760Smsmith}; 6567760Smsmith 66167814Sjkim/* Default number of maximum pending threads. */ 6788420Siwasaki#ifndef ACPI_NO_SEMAPHORES 6888420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING 6988420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING 4 7088420Siwasaki#endif 71167814Sjkim 7288420Siwasakistatic int acpi_semaphore_debug = 0; 7388420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 74120494SnjlSYSCTL_DECL(_debug_acpi); 75120494SnjlSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW, 76120494Snjl &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages"); 77128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 7888420Siwasaki 7967760SmsmithACPI_STATUS 80128227SnjlAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, 81167915Sjkim ACPI_SEMAPHORE *OutHandle) 8267760Smsmith{ 8371876Smsmith#ifndef ACPI_NO_SEMAPHORES 8467760Smsmith struct acpi_semaphore *as; 8567760Smsmith 8696926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 8771876Smsmith 8867760Smsmith if (OutHandle == NULL) 89128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 9067760Smsmith if (InitialUnits > MaxUnits) 91128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 9267760Smsmith 93128227Snjl if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL) 94128227Snjl return_ACPI_STATUS (AE_NO_MEMORY); 9567760Smsmith 9693818Sjhb mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 9767760Smsmith as->as_units = InitialUnits; 9867760Smsmith as->as_maxunits = MaxUnits; 9988420Siwasaki as->as_pendings = as->as_resetting = as->as_timeouts = 0; 10067760Smsmith 10188420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 10288420Siwasaki "created semaphore %p max %d, initial %d\n", 10388420Siwasaki as, InitialUnits, MaxUnits)); 10471876Smsmith 10567760Smsmith *OutHandle = (ACPI_HANDLE)as; 10671876Smsmith#else 10771876Smsmith *OutHandle = (ACPI_HANDLE)OutHandle; 108128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 109128227Snjl 110128227Snjl return_ACPI_STATUS (AE_OK); 11167760Smsmith} 11267760Smsmith 11367760SmsmithACPI_STATUS 114167915SjkimAcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle) 11567760Smsmith{ 11671876Smsmith#ifndef ACPI_NO_SEMAPHORES 11771359Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 11871876Smsmith 11996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 12071876Smsmith 12188420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 12271359Smsmith mtx_destroy(&as->as_mtx); 12367760Smsmith free(Handle, M_ACPISEM); 124128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 125128227Snjl 126128227Snjl return_ACPI_STATUS (AE_OK); 12767760Smsmith} 12867760Smsmith 12967760Smsmith/* 13067760Smsmith * This implementation has a bug, in that it has to stall for the entire 13167760Smsmith * timeout before it will return AE_TIME. A better implementation would 13267760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up. 13367760Smsmith */ 13467760SmsmithACPI_STATUS 135167915SjkimAcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout) 13667760Smsmith{ 13771876Smsmith#ifndef ACPI_NO_SEMAPHORES 138128227Snjl ACPI_STATUS result; 13967760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 14071876Smsmith int rv, tmo; 14188420Siwasaki struct timeval timeouttv, currenttv, timelefttv; 14267760Smsmith 14396926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 14471876Smsmith 14567760Smsmith if (as == NULL) 146128227Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 14767760Smsmith 14888420Siwasaki if (cold) 149128227Snjl return_ACPI_STATUS (AE_OK); 15088420Siwasaki 15188420Siwasaki#if 0 15288420Siwasaki if (as->as_units < Units && as->as_timeouts > 10) { 15388420Siwasaki printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 154105278Sjhb AS_LOCK(as); 15588420Siwasaki as->as_units = as->as_maxunits; 15688420Siwasaki if (as->as_pendings) 15788420Siwasaki as->as_resetting = 1; 15888420Siwasaki as->as_timeouts = 0; 15988420Siwasaki wakeup(as); 160105278Sjhb AS_UNLOCK(as); 161128227Snjl return_ACPI_STATUS (AE_TIME); 16288420Siwasaki } 16388420Siwasaki 164128227Snjl if (as->as_resetting) 165128227Snjl return_ACPI_STATUS (AE_TIME); 16688420Siwasaki#endif 16788420Siwasaki 168107328Siwasaki /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 169107328Siwasaki if (Timeout == ACPI_WAIT_FOREVER) { 17071876Smsmith tmo = 0; 17188420Siwasaki timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 17288420Siwasaki timeouttv.tv_usec = 0; 17371876Smsmith } else { 17471876Smsmith /* compute timeout using microseconds per tick */ 17577432Smsmith tmo = (Timeout * 1000) / (1000000 / hz); 17671876Smsmith if (tmo <= 0) 17771876Smsmith tmo = 1; 17888420Siwasaki timeouttv.tv_sec = Timeout / 1000; 17988420Siwasaki timeouttv.tv_usec = (Timeout % 1000) * 1000; 18071876Smsmith } 18171876Smsmith 18288420Siwasaki /* calculate timeout value in timeval */ 18388420Siwasaki getmicrotime(¤ttv); 18488420Siwasaki timevaladd(&timeouttv, ¤ttv); 18588420Siwasaki 186105278Sjhb AS_LOCK(as); 18788420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 18888420Siwasaki "get %d units from semaphore %p (has %d), timeout %d\n", 18988420Siwasaki Units, as, as->as_units, Timeout)); 19067760Smsmith for (;;) { 19199492Siwasaki if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 19280071Smsmith result = AE_OK; 19380071Smsmith break; 19480071Smsmith } 19567760Smsmith if (as->as_units >= Units) { 19667760Smsmith as->as_units -= Units; 19767760Smsmith result = AE_OK; 19867760Smsmith break; 19967760Smsmith } 20088420Siwasaki 201167814Sjkim /* limit number of pending threads */ 20288420Siwasaki if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 20367760Smsmith result = AE_TIME; 20467760Smsmith break; 20567760Smsmith } 20688420Siwasaki 20788420Siwasaki /* if timeout values of zero is specified, return immediately */ 20888420Siwasaki if (Timeout == 0) { 20988420Siwasaki result = AE_TIME; 21088420Siwasaki break; 21188420Siwasaki } 21288420Siwasaki 21388420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 21488420Siwasaki "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 21588420Siwasaki as, &as->as_mtx, PCATCH, tmo)); 21688420Siwasaki 21788420Siwasaki as->as_pendings++; 21888420Siwasaki 21988420Siwasaki if (acpi_semaphore_debug) { 22088420Siwasaki printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 22188420Siwasaki __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 22288420Siwasaki } 22388420Siwasaki 22488420Siwasaki rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 22588420Siwasaki 22688420Siwasaki as->as_pendings--; 22788420Siwasaki 22888420Siwasaki#if 0 22988420Siwasaki if (as->as_resetting) { 23088420Siwasaki /* semaphore reset, return immediately */ 23188420Siwasaki if (as->as_pendings == 0) { 23288420Siwasaki as->as_resetting = 0; 23388420Siwasaki } 23488420Siwasaki result = AE_TIME; 23588420Siwasaki break; 23688420Siwasaki } 23788420Siwasaki#endif 23888420Siwasaki 23988420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 24071876Smsmith if (rv == EWOULDBLOCK) { 24167760Smsmith result = AE_TIME; 24267760Smsmith break; 24367760Smsmith } 24488420Siwasaki 24588420Siwasaki /* check if we already awaited enough */ 24688420Siwasaki timelefttv = timeouttv; 24788420Siwasaki getmicrotime(¤ttv); 24888420Siwasaki timevalsub(&timelefttv, ¤ttv); 24988420Siwasaki if (timelefttv.tv_sec < 0) { 250128227Snjl ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", 251128227Snjl as)); 25288420Siwasaki result = AE_TIME; 25388420Siwasaki break; 25488420Siwasaki } 25588420Siwasaki 25688420Siwasaki /* adjust timeout for the next sleep */ 257128227Snjl tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / 258128227Snjl (1000000 / hz); 25988420Siwasaki if (tmo <= 0) 26088420Siwasaki tmo = 1; 26188420Siwasaki 26288420Siwasaki if (acpi_semaphore_debug) { 263153706Strhodes printf("%s: Wakeup timeleft(%jd, %lu), tmo %u, sem %p, thread %d\n", 264153706Strhodes __func__, (intmax_t)timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, 265128227Snjl AcpiOsGetThreadId()); 26688420Siwasaki } 26767760Smsmith } 26888420Siwasaki 26988420Siwasaki if (acpi_semaphore_debug) { 27088420Siwasaki if (result == AE_TIME && Timeout > 0) { 27188420Siwasaki printf("%s: Timeout %d, pending %d, semaphore %p\n", 27288420Siwasaki __func__, Timeout, as->as_pendings, as); 27388420Siwasaki } 27488420Siwasaki if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 275128227Snjl printf("%s: Acquire %d, units %d, pending %d, sem %p, thread %d\n", 276128227Snjl __func__, Units, as->as_units, as->as_pendings, as, 277128227Snjl AcpiOsGetThreadId()); 27888420Siwasaki } 27988420Siwasaki } 28088420Siwasaki 281128227Snjl if (result == AE_TIME) 28288420Siwasaki as->as_timeouts++; 283128227Snjl else 28488420Siwasaki as->as_timeouts = 0; 28588420Siwasaki 286105278Sjhb AS_UNLOCK(as); 287128227Snjl return_ACPI_STATUS (result); 28871876Smsmith#else 289128227Snjl return_ACPI_STATUS (AE_OK); 290128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 29167760Smsmith} 29267760Smsmith 29367760SmsmithACPI_STATUS 294167915SjkimAcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units) 29567760Smsmith{ 29671876Smsmith#ifndef ACPI_NO_SEMAPHORES 29767760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 29867760Smsmith 29996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 30071876Smsmith 30167760Smsmith if (as == NULL) 30271876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 30367760Smsmith 304105278Sjhb AS_LOCK(as); 30588420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 30688420Siwasaki "return %d units to semaphore %p (has %d)\n", 30788420Siwasaki Units, as, as->as_units)); 30899492Siwasaki if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 30980071Smsmith as->as_units += Units; 31080071Smsmith if (as->as_units > as->as_maxunits) 31180071Smsmith as->as_units = as->as_maxunits; 31280071Smsmith } 31388420Siwasaki 31488420Siwasaki if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 31588420Siwasaki printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 31688420Siwasaki __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 31788420Siwasaki } 31888420Siwasaki 31967760Smsmith wakeup(as); 320105278Sjhb AS_UNLOCK(as); 321128227Snjl#endif /* !ACPI_NO_SEMAPHORES */ 322128227Snjl 323128227Snjl return_ACPI_STATUS (AE_OK); 32467760Smsmith} 325117530Snjl 326167908Snjl/* Combined mutex + mutex name storage since the latter must persist. */ 327167910Sjkimstruct acpi_spinlock { 328167910Sjkim struct mtx lock; 329167910Sjkim char name[32]; 330167908Snjl}; 331167908Snjl 332117530SnjlACPI_STATUS 333167910SjkimAcpiOsCreateLock (ACPI_SPINLOCK *OutHandle) 334117530Snjl{ 335167910Sjkim struct acpi_spinlock *h; 336117530Snjl 337117530Snjl if (OutHandle == NULL) 338117530Snjl return (AE_BAD_PARAMETER); 339167918Sjkim h = malloc(sizeof(*h), M_ACPISEM, M_NOWAIT | M_ZERO); 340167910Sjkim if (h == NULL) 341117530Snjl return (AE_NO_MEMORY); 342117530Snjl 343167908Snjl /* Build a unique name based on the address of the handle. */ 344167910Sjkim if (OutHandle == &AcpiGbl_GpeLock) 345167910Sjkim snprintf(h->name, sizeof(h->name), "acpi subsystem GPE lock"); 346167918Sjkim else if (OutHandle == &AcpiGbl_HardwareLock) 347167910Sjkim snprintf(h->name, sizeof(h->name), "acpi subsystem HW lock"); 348167910Sjkim else 349167910Sjkim snprintf(h->name, sizeof(h->name), "acpi subsys %p", OutHandle); 350177934Stakawata mtx_init(&h->lock, h->name, NULL, MTX_DEF|MTX_RECURSE); 351167910Sjkim *OutHandle = (ACPI_SPINLOCK)h; 352117530Snjl return (AE_OK); 353117530Snjl} 354117530Snjl 355117530Snjlvoid 356167910SjkimAcpiOsDeleteLock (ACPI_SPINLOCK Handle) 357117530Snjl{ 358167910Sjkim struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 359117530Snjl 360117530Snjl if (Handle == NULL) 361117530Snjl return; 362167910Sjkim mtx_destroy(&h->lock); 363167911Sjkim free(h, M_ACPISEM); 364117530Snjl} 365117530Snjl 366117530Snjl/* 367117530Snjl * The Flags parameter seems to state whether or not caller is an ISR 368117530Snjl * (and thus can't block) but since we have ithreads, we don't worry 369117530Snjl * about potentially blocking. 370117530Snjl */ 371193530SjkimACPI_CPU_FLAGS 372167910SjkimAcpiOsAcquireLock (ACPI_SPINLOCK Handle) 373117530Snjl{ 374167910Sjkim struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 375117530Snjl 376117530Snjl if (Handle == NULL) 377151948Sjkim return (0); 378167910Sjkim mtx_lock(&h->lock); 379151948Sjkim return (0); 380117530Snjl} 381117530Snjl 382117530Snjlvoid 383167910SjkimAcpiOsReleaseLock (ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags) 384117530Snjl{ 385167910Sjkim struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 386117530Snjl 387117530Snjl if (Handle == NULL) 388128227Snjl return; 389167910Sjkim mtx_unlock(&h->lock); 390117530Snjl} 391128979Snjl 392128979Snjl/* Section 5.2.9.1: global lock acquire/release functions */ 393128979Snjl#define GL_ACQUIRED (-1) 394128979Snjl#define GL_BUSY 0 395128979Snjl#define GL_BIT_PENDING 0x1 396128979Snjl#define GL_BIT_OWNED 0x2 397128979Snjl#define GL_BIT_MASK (GL_BIT_PENDING | GL_BIT_OWNED) 398128979Snjl 399128979Snjl/* 400128979Snjl * Acquire the global lock. If busy, set the pending bit. The caller 401128979Snjl * will wait for notification from the BIOS that the lock is available 402128979Snjl * and then attempt to acquire it again. 403128979Snjl */ 404128979Snjlint 405128979Snjlacpi_acquire_global_lock(uint32_t *lock) 406128979Snjl{ 407128979Snjl uint32_t new, old; 408128979Snjl 409128979Snjl do { 410128979Snjl old = *lock; 411128981Snjl new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) | 412128981Snjl ((old >> 1) & GL_BIT_PENDING); 413128979Snjl } while (atomic_cmpset_acq_int(lock, old, new) == 0); 414128979Snjl 415128979Snjl return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY); 416128979Snjl} 417128979Snjl 418128979Snjl/* 419128979Snjl * Release the global lock, returning whether there is a waiter pending. 420128979Snjl * If the BIOS set the pending bit, OSPM must notify the BIOS when it 421128979Snjl * releases the lock. 422128979Snjl */ 423128979Snjlint 424128979Snjlacpi_release_global_lock(uint32_t *lock) 425128979Snjl{ 426128979Snjl uint32_t new, old; 427128979Snjl 428128979Snjl do { 429128979Snjl old = *lock; 430128979Snjl new = old & ~GL_BIT_MASK; 431128979Snjl } while (atomic_cmpset_rel_int(lock, old, new) == 0); 432128979Snjl 433128979Snjl return (old & GL_BIT_PENDING); 434128979Snjl} 435