OsdSynch.c revision 120494
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 * $FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 120494 2003-09-26 21:22:10Z njl $ 2867760Smsmith */ 2967760Smsmith 3067760Smsmith/* 3167760Smsmith * 6.1 : Mutual Exclusion and Synchronisation 3267760Smsmith */ 3367760Smsmith 3467760Smsmith#include "acpi.h" 3567760Smsmith 3688420Siwasaki#include "opt_acpi.h" 3767760Smsmith#include <sys/kernel.h> 38105278Sjhb#include <sys/malloc.h> 39105278Sjhb#include <sys/sysctl.h> 40105278Sjhb#if __FreeBSD_version >= 500000 4174914Sjhb#include <sys/lock.h> 4267760Smsmith#include <sys/mutex.h> 43105278Sjhb#endif 4467760Smsmith 4577432Smsmith#define _COMPONENT ACPI_OS_SERVICES 4691128SmsmithACPI_MODULE_NAME("SYNCH") 4771876Smsmith 4869776Smsmithstatic MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 4967760Smsmith 50105278Sjhb#if __FreeBSD_version < 500000 51105278Sjhb# define AS_LOCK(as) s = splhigh() 52105278Sjhb# define AS_UNLOCK(as) splx(s) 53105278Sjhb# define AS_LOCK_DECL int s 54105278Sjhb# define msleep(a, b, c, d, e) tsleep(a, c, d, e) 55105278Sjhb#else 56105278Sjhb# define AS_LOCK(as) mtx_lock(&(as)->as_mtx) 57105278Sjhb# define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx) 58105278Sjhb# define AS_LOCK_DECL 59105278Sjhb#endif 60105278Sjhb 6167760Smsmith/* 6267760Smsmith * Simple counting semaphore implemented using a mutex. (Subsequently used 6367760Smsmith * in the OSI code to implement a mutex. Go figure.) 6467760Smsmith */ 6567760Smsmithstruct acpi_semaphore { 66105278Sjhb#if __FreeBSD_version >= 500000 6767760Smsmith struct mtx as_mtx; 68105278Sjhb#endif 6967760Smsmith UINT32 as_units; 7067760Smsmith UINT32 as_maxunits; 7188420Siwasaki UINT32 as_pendings; 7288420Siwasaki UINT32 as_resetting; 7388420Siwasaki UINT32 as_timeouts; 7467760Smsmith}; 7567760Smsmith 7688420Siwasaki#ifndef ACPI_NO_SEMAPHORES 7788420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING 7888420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING 4 7988420Siwasaki#endif 8088420Siwasakistatic int acpi_semaphore_debug = 0; 8188420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 82120494SnjlSYSCTL_DECL(_debug_acpi); 83120494SnjlSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW, 84120494Snjl &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages"); 8588420Siwasaki#endif 8688420Siwasaki 8767760SmsmithACPI_STATUS 8867760SmsmithAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle) 8967760Smsmith{ 9071876Smsmith#ifndef ACPI_NO_SEMAPHORES 9167760Smsmith struct acpi_semaphore *as; 9267760Smsmith 9396926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 9471876Smsmith 9567760Smsmith if (OutHandle == NULL) 9667760Smsmith return(AE_BAD_PARAMETER); 9767760Smsmith if (InitialUnits > MaxUnits) 9871876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 9967760Smsmith 10067760Smsmith if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL) 10171876Smsmith return_ACPI_STATUS(AE_NO_MEMORY); 10267760Smsmith 10388420Siwasaki bzero(as, sizeof(*as)); 104105278Sjhb#if __FreeBSD_version >= 500000 10593818Sjhb mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 106105278Sjhb#endif 10767760Smsmith as->as_units = InitialUnits; 10867760Smsmith as->as_maxunits = MaxUnits; 10988420Siwasaki as->as_pendings = as->as_resetting = as->as_timeouts = 0; 11067760Smsmith 11188420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 11288420Siwasaki "created semaphore %p max %d, initial %d\n", 11388420Siwasaki as, InitialUnits, MaxUnits)); 11471876Smsmith 11567760Smsmith *OutHandle = (ACPI_HANDLE)as; 11671876Smsmith return_ACPI_STATUS(AE_OK); 11771876Smsmith#else 11871876Smsmith *OutHandle = (ACPI_HANDLE)OutHandle; 11967760Smsmith return(AE_OK); 12071876Smsmith#endif 12167760Smsmith} 12267760Smsmith 12367760SmsmithACPI_STATUS 12467760SmsmithAcpiOsDeleteSemaphore (ACPI_HANDLE Handle) 12567760Smsmith{ 12671876Smsmith#ifndef ACPI_NO_SEMAPHORES 12771359Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 12871876Smsmith 12996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 13071876Smsmith 13188420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 132105278Sjhb#if __FreeBSD_version >= 500000 13371359Smsmith mtx_destroy(&as->as_mtx); 134105278Sjhb#endif 13567760Smsmith free(Handle, M_ACPISEM); 13671876Smsmith return_ACPI_STATUS(AE_OK); 13771876Smsmith#else 13867760Smsmith return(AE_OK); 13971876Smsmith#endif 14067760Smsmith} 14167760Smsmith 14267760Smsmith/* 14367760Smsmith * This implementation has a bug, in that it has to stall for the entire 14467760Smsmith * timeout before it will return AE_TIME. A better implementation would 14567760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up. 14667760Smsmith */ 14767760SmsmithACPI_STATUS 148107328SiwasakiAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout) 14967760Smsmith{ 15071876Smsmith#ifndef ACPI_NO_SEMAPHORES 15167760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 15271876Smsmith ACPI_STATUS result; 15371876Smsmith int rv, tmo; 15488420Siwasaki struct timeval timeouttv, currenttv, timelefttv; 155105278Sjhb AS_LOCK_DECL; 15667760Smsmith 15796926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 15871876Smsmith 15967760Smsmith if (as == NULL) 16071876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 16167760Smsmith 16288420Siwasaki if (cold) 16388420Siwasaki return_ACPI_STATUS(AE_OK); 16488420Siwasaki 16588420Siwasaki#if 0 16688420Siwasaki if (as->as_units < Units && as->as_timeouts > 10) { 16788420Siwasaki printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 168105278Sjhb AS_LOCK(as); 16988420Siwasaki as->as_units = as->as_maxunits; 17088420Siwasaki if (as->as_pendings) 17188420Siwasaki as->as_resetting = 1; 17288420Siwasaki as->as_timeouts = 0; 17388420Siwasaki wakeup(as); 174105278Sjhb AS_UNLOCK(as); 17588420Siwasaki return_ACPI_STATUS(AE_TIME); 17688420Siwasaki } 17788420Siwasaki 17888420Siwasaki if (as->as_resetting) { 17988420Siwasaki return_ACPI_STATUS(AE_TIME); 18088420Siwasaki } 18188420Siwasaki#endif 18288420Siwasaki 183107328Siwasaki /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 184107328Siwasaki if (Timeout == ACPI_WAIT_FOREVER) { 18571876Smsmith tmo = 0; 18688420Siwasaki timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 18788420Siwasaki timeouttv.tv_usec = 0; 18871876Smsmith } else { 18971876Smsmith /* compute timeout using microseconds per tick */ 19077432Smsmith tmo = (Timeout * 1000) / (1000000 / hz); 19171876Smsmith if (tmo <= 0) 19271876Smsmith tmo = 1; 19388420Siwasaki timeouttv.tv_sec = Timeout / 1000; 19488420Siwasaki timeouttv.tv_usec = (Timeout % 1000) * 1000; 19571876Smsmith } 19671876Smsmith 19788420Siwasaki /* calculate timeout value in timeval */ 19888420Siwasaki getmicrotime(¤ttv); 19988420Siwasaki timevaladd(&timeouttv, ¤ttv); 20088420Siwasaki 201105278Sjhb AS_LOCK(as); 20288420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 20388420Siwasaki "get %d units from semaphore %p (has %d), timeout %d\n", 20488420Siwasaki Units, as, as->as_units, Timeout)); 20567760Smsmith for (;;) { 20699492Siwasaki if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 20780071Smsmith result = AE_OK; 20880071Smsmith break; 20980071Smsmith } 21067760Smsmith if (as->as_units >= Units) { 21167760Smsmith as->as_units -= Units; 21267760Smsmith result = AE_OK; 21367760Smsmith break; 21467760Smsmith } 21588420Siwasaki 21688420Siwasaki /* limit number of pending treads */ 21788420Siwasaki if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 21867760Smsmith result = AE_TIME; 21967760Smsmith break; 22067760Smsmith } 22188420Siwasaki 22288420Siwasaki /* if timeout values of zero is specified, return immediately */ 22388420Siwasaki if (Timeout == 0) { 22488420Siwasaki result = AE_TIME; 22588420Siwasaki break; 22688420Siwasaki } 22788420Siwasaki 228105278Sjhb#if __FreeBSD_version >= 500000 22988420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 23088420Siwasaki "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 23188420Siwasaki as, &as->as_mtx, PCATCH, tmo)); 232105278Sjhb#endif 23388420Siwasaki 23488420Siwasaki as->as_pendings++; 23588420Siwasaki 23688420Siwasaki if (acpi_semaphore_debug) { 23788420Siwasaki printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 23888420Siwasaki __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 23988420Siwasaki } 24088420Siwasaki 24188420Siwasaki rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 24288420Siwasaki 24388420Siwasaki as->as_pendings--; 24488420Siwasaki 24588420Siwasaki#if 0 24688420Siwasaki if (as->as_resetting) { 24788420Siwasaki /* semaphore reset, return immediately */ 24888420Siwasaki if (as->as_pendings == 0) { 24988420Siwasaki as->as_resetting = 0; 25088420Siwasaki } 25188420Siwasaki result = AE_TIME; 25288420Siwasaki break; 25388420Siwasaki } 25488420Siwasaki#endif 25588420Siwasaki 25688420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 25771876Smsmith if (rv == EWOULDBLOCK) { 25867760Smsmith result = AE_TIME; 25967760Smsmith break; 26067760Smsmith } 26188420Siwasaki 26288420Siwasaki /* check if we already awaited enough */ 26388420Siwasaki timelefttv = timeouttv; 26488420Siwasaki getmicrotime(¤ttv); 26588420Siwasaki timevalsub(&timelefttv, ¤ttv); 26688420Siwasaki if (timelefttv.tv_sec < 0) { 26788420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as)); 26888420Siwasaki result = AE_TIME; 26988420Siwasaki break; 27088420Siwasaki } 27188420Siwasaki 27288420Siwasaki /* adjust timeout for the next sleep */ 27388420Siwasaki tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz); 27488420Siwasaki if (tmo <= 0) 27588420Siwasaki tmo = 1; 27688420Siwasaki 27788420Siwasaki if (acpi_semaphore_debug) { 27888420Siwasaki printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n", 27988420Siwasaki __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId()); 28088420Siwasaki } 28167760Smsmith } 28288420Siwasaki 28388420Siwasaki if (acpi_semaphore_debug) { 28488420Siwasaki if (result == AE_TIME && Timeout > 0) { 28588420Siwasaki printf("%s: Timeout %d, pending %d, semaphore %p\n", 28688420Siwasaki __func__, Timeout, as->as_pendings, as); 28788420Siwasaki } 28888420Siwasaki if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 28988420Siwasaki printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n", 29088420Siwasaki __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 29188420Siwasaki } 29288420Siwasaki } 29388420Siwasaki 29488420Siwasaki if (result == AE_TIME) { 29588420Siwasaki as->as_timeouts++; 29688420Siwasaki } else { 29788420Siwasaki as->as_timeouts = 0; 29888420Siwasaki } 29988420Siwasaki 300105278Sjhb AS_UNLOCK(as); 30167760Smsmith 30271876Smsmith return_ACPI_STATUS(result); 30371876Smsmith#else 30471876Smsmith return(AE_OK); 30571876Smsmith#endif 30667760Smsmith} 30767760Smsmith 30867760SmsmithACPI_STATUS 30967760SmsmithAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units) 31067760Smsmith{ 31171876Smsmith#ifndef ACPI_NO_SEMAPHORES 31267760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 313105278Sjhb AS_LOCK_DECL; 31467760Smsmith 31596926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 31671876Smsmith 31767760Smsmith if (as == NULL) 31871876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 31967760Smsmith 320105278Sjhb AS_LOCK(as); 32188420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 32288420Siwasaki "return %d units to semaphore %p (has %d)\n", 32388420Siwasaki Units, as, as->as_units)); 32499492Siwasaki if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 32580071Smsmith as->as_units += Units; 32680071Smsmith if (as->as_units > as->as_maxunits) 32780071Smsmith as->as_units = as->as_maxunits; 32880071Smsmith } 32988420Siwasaki 33088420Siwasaki if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 33188420Siwasaki printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 33288420Siwasaki __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 33388420Siwasaki } 33488420Siwasaki 33567760Smsmith wakeup(as); 336105278Sjhb AS_UNLOCK(as); 33771876Smsmith return_ACPI_STATUS(AE_OK); 33871876Smsmith#else 33967760Smsmith return(AE_OK); 34071876Smsmith#endif 34167760Smsmith} 342117530Snjl 343117530SnjlACPI_STATUS 344117530SnjlAcpiOsCreateLock (ACPI_HANDLE *OutHandle) 345117530Snjl{ 346117530Snjl struct mtx *m; 347117530Snjl 348117530Snjl if (OutHandle == NULL) 349117530Snjl return (AE_BAD_PARAMETER); 350117530Snjl MALLOC(m, struct mtx *, sizeof(*m), M_ACPISEM, M_NOWAIT | M_ZERO); 351117530Snjl if (m == NULL) 352117530Snjl return (AE_NO_MEMORY); 353117530Snjl 354117530Snjl mtx_init(m, "acpica subsystem lock", NULL, MTX_DEF); 355117530Snjl *OutHandle = (ACPI_HANDLE)m; 356117530Snjl return (AE_OK); 357117530Snjl} 358117530Snjl 359117530Snjlvoid 360117530SnjlAcpiOsDeleteLock (ACPI_HANDLE Handle) 361117530Snjl{ 362117530Snjl struct mtx *m = (struct mtx *)Handle; 363117530Snjl 364117530Snjl if (Handle == NULL) 365117530Snjl return; 366117530Snjl mtx_destroy(m); 367117530Snjl} 368117530Snjl 369117530Snjl/* 370117530Snjl * The Flags parameter seems to state whether or not caller is an ISR 371117530Snjl * (and thus can't block) but since we have ithreads, we don't worry 372117530Snjl * about potentially blocking. 373117530Snjl */ 374117530Snjlvoid 375117530SnjlAcpiOsAcquireLock (ACPI_HANDLE Handle, UINT32 Flags) 376117530Snjl{ 377117530Snjl struct mtx *m = (struct mtx *)Handle; 378117530Snjl 379117530Snjl if (Handle == NULL) 380117530Snjl return; 381117530Snjl mtx_lock(m); 382117530Snjl} 383117530Snjl 384117530Snjlvoid 385117530SnjlAcpiOsReleaseLock (ACPI_HANDLE Handle, UINT32 Flags) 386117530Snjl{ 387117530Snjl struct mtx *m = (struct mtx *)Handle; 388117530Snjl 389117530Snjl if (Handle == NULL) 390117530Snjl return; 391117530Snjl mtx_unlock(m); 392117530Snjl} 393