OsdSynch.c revision 88420
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 88420 2001-12-22 16:05:41Z iwasaki $ 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> 3874914Sjhb#include <sys/lock.h> 3967760Smsmith#include <sys/malloc.h> 4067760Smsmith#include <sys/mutex.h> 4188420Siwasaki#include <sys/sysctl.h> 4267760Smsmith 4377432Smsmith#define _COMPONENT ACPI_OS_SERVICES 4471876SmsmithMODULE_NAME("SYNCH") 4571876Smsmith 4669776Smsmithstatic MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 4767760Smsmith 4867760Smsmith/* 4967760Smsmith * Simple counting semaphore implemented using a mutex. (Subsequently used 5067760Smsmith * in the OSI code to implement a mutex. Go figure.) 5167760Smsmith */ 5267760Smsmithstruct acpi_semaphore { 5367760Smsmith struct mtx as_mtx; 5467760Smsmith UINT32 as_units; 5567760Smsmith UINT32 as_maxunits; 5688420Siwasaki UINT32 as_pendings; 5788420Siwasaki UINT32 as_resetting; 5888420Siwasaki UINT32 as_timeouts; 5967760Smsmith}; 6067760Smsmith 6188420Siwasaki#ifndef ACPI_NO_SEMAPHORES 6288420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING 6388420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING 4 6488420Siwasaki#endif 6588420Siwasakistatic int acpi_semaphore_debug = 0; 6688420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 6788420SiwasakiSYSCTL_INT(_debug, OID_AUTO, acpi_semaphore_debug, CTLFLAG_RW, 6888420Siwasaki &acpi_semaphore_debug, 0, ""); 6988420Siwasaki#endif 7088420Siwasaki 7167760SmsmithACPI_STATUS 7267760SmsmithAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle) 7367760Smsmith{ 7471876Smsmith#ifndef ACPI_NO_SEMAPHORES 7567760Smsmith struct acpi_semaphore *as; 7667760Smsmith 7777432Smsmith FUNCTION_TRACE(__func__); 7871876Smsmith 7967760Smsmith if (OutHandle == NULL) 8067760Smsmith return(AE_BAD_PARAMETER); 8167760Smsmith if (InitialUnits > MaxUnits) 8271876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 8367760Smsmith 8467760Smsmith if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL) 8571876Smsmith return_ACPI_STATUS(AE_NO_MEMORY); 8667760Smsmith 8788420Siwasaki bzero(as, sizeof(*as)); 8867760Smsmith mtx_init(&as->as_mtx, "ACPI semaphore", MTX_DEF); 8967760Smsmith as->as_units = InitialUnits; 9067760Smsmith as->as_maxunits = MaxUnits; 9188420Siwasaki as->as_pendings = as->as_resetting = as->as_timeouts = 0; 9267760Smsmith 9388420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 9488420Siwasaki "created semaphore %p max %d, initial %d\n", 9588420Siwasaki as, InitialUnits, MaxUnits)); 9671876Smsmith 9767760Smsmith *OutHandle = (ACPI_HANDLE)as; 9871876Smsmith return_ACPI_STATUS(AE_OK); 9971876Smsmith#else 10071876Smsmith *OutHandle = (ACPI_HANDLE)OutHandle; 10167760Smsmith return(AE_OK); 10271876Smsmith#endif 10367760Smsmith} 10467760Smsmith 10567760SmsmithACPI_STATUS 10667760SmsmithAcpiOsDeleteSemaphore (ACPI_HANDLE Handle) 10767760Smsmith{ 10871876Smsmith#ifndef ACPI_NO_SEMAPHORES 10971359Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 11071876Smsmith 11177432Smsmith FUNCTION_TRACE(__func__); 11271876Smsmith 11388420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 11471359Smsmith mtx_destroy(&as->as_mtx); 11567760Smsmith free(Handle, M_ACPISEM); 11671876Smsmith return_ACPI_STATUS(AE_OK); 11771876Smsmith#else 11867760Smsmith return(AE_OK); 11971876Smsmith#endif 12067760Smsmith} 12167760Smsmith 12267760Smsmith/* 12367760Smsmith * This implementation has a bug, in that it has to stall for the entire 12467760Smsmith * timeout before it will return AE_TIME. A better implementation would 12567760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up. 12667760Smsmith */ 12767760SmsmithACPI_STATUS 12867760SmsmithAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT32 Timeout) 12967760Smsmith{ 13071876Smsmith#ifndef ACPI_NO_SEMAPHORES 13167760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 13271876Smsmith ACPI_STATUS result; 13371876Smsmith int rv, tmo; 13488420Siwasaki struct timeval timeouttv, currenttv, timelefttv; 13567760Smsmith 13677432Smsmith FUNCTION_TRACE(__func__); 13771876Smsmith 13867760Smsmith if (as == NULL) 13971876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 14067760Smsmith 14188420Siwasaki if (cold) 14288420Siwasaki return_ACPI_STATUS(AE_OK); 14388420Siwasaki 14488420Siwasaki#if 0 14588420Siwasaki if (as->as_units < Units && as->as_timeouts > 10) { 14688420Siwasaki printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 14788420Siwasaki mtx_lock(&as->as_mtx); 14888420Siwasaki as->as_units = as->as_maxunits; 14988420Siwasaki if (as->as_pendings) 15088420Siwasaki as->as_resetting = 1; 15188420Siwasaki as->as_timeouts = 0; 15288420Siwasaki wakeup(as); 15388420Siwasaki mtx_unlock(&as->as_mtx); 15488420Siwasaki return_ACPI_STATUS(AE_TIME); 15588420Siwasaki } 15688420Siwasaki 15788420Siwasaki if (as->as_resetting) { 15888420Siwasaki return_ACPI_STATUS(AE_TIME); 15988420Siwasaki } 16088420Siwasaki#endif 16188420Siwasaki 16288420Siwasaki /* a timeout of WAIT_FOREVER means "forever" */ 16388420Siwasaki if (Timeout == WAIT_FOREVER) { 16471876Smsmith tmo = 0; 16588420Siwasaki timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 16688420Siwasaki timeouttv.tv_usec = 0; 16771876Smsmith } else { 16871876Smsmith /* compute timeout using microseconds per tick */ 16977432Smsmith tmo = (Timeout * 1000) / (1000000 / hz); 17071876Smsmith if (tmo <= 0) 17171876Smsmith tmo = 1; 17288420Siwasaki timeouttv.tv_sec = Timeout / 1000; 17388420Siwasaki timeouttv.tv_usec = (Timeout % 1000) * 1000; 17471876Smsmith } 17571876Smsmith 17688420Siwasaki /* calculate timeout value in timeval */ 17788420Siwasaki getmicrotime(¤ttv); 17888420Siwasaki timevaladd(&timeouttv, ¤ttv); 17988420Siwasaki 18072200Sbmilekic mtx_lock(&as->as_mtx); 18188420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 18288420Siwasaki "get %d units from semaphore %p (has %d), timeout %d\n", 18388420Siwasaki Units, as, as->as_units, Timeout)); 18467760Smsmith for (;;) { 18588420Siwasaki if (as->as_units == ACPI_NO_UNIT_LIMIT) { 18680071Smsmith result = AE_OK; 18780071Smsmith break; 18880071Smsmith } 18967760Smsmith if (as->as_units >= Units) { 19067760Smsmith as->as_units -= Units; 19167760Smsmith result = AE_OK; 19267760Smsmith break; 19367760Smsmith } 19488420Siwasaki 19588420Siwasaki /* limit number of pending treads */ 19688420Siwasaki if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 19767760Smsmith result = AE_TIME; 19867760Smsmith break; 19967760Smsmith } 20088420Siwasaki 20188420Siwasaki /* if timeout values of zero is specified, return immediately */ 20288420Siwasaki if (Timeout == 0) { 20388420Siwasaki result = AE_TIME; 20488420Siwasaki break; 20588420Siwasaki } 20688420Siwasaki 20788420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 20888420Siwasaki "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 20988420Siwasaki as, &as->as_mtx, PCATCH, tmo)); 21088420Siwasaki 21188420Siwasaki as->as_pendings++; 21288420Siwasaki 21388420Siwasaki if (acpi_semaphore_debug) { 21488420Siwasaki printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 21588420Siwasaki __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 21688420Siwasaki } 21788420Siwasaki 21888420Siwasaki rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 21988420Siwasaki 22088420Siwasaki as->as_pendings--; 22188420Siwasaki 22288420Siwasaki#if 0 22388420Siwasaki if (as->as_resetting) { 22488420Siwasaki /* semaphore reset, return immediately */ 22588420Siwasaki if (as->as_pendings == 0) { 22688420Siwasaki as->as_resetting = 0; 22788420Siwasaki } 22888420Siwasaki result = AE_TIME; 22988420Siwasaki break; 23088420Siwasaki } 23188420Siwasaki#endif 23288420Siwasaki 23388420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 23471876Smsmith if (rv == EWOULDBLOCK) { 23567760Smsmith result = AE_TIME; 23667760Smsmith break; 23767760Smsmith } 23888420Siwasaki 23988420Siwasaki /* check if we already awaited enough */ 24088420Siwasaki timelefttv = timeouttv; 24188420Siwasaki getmicrotime(¤ttv); 24288420Siwasaki timevalsub(&timelefttv, ¤ttv); 24388420Siwasaki if (timelefttv.tv_sec < 0) { 24488420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as)); 24588420Siwasaki result = AE_TIME; 24688420Siwasaki break; 24788420Siwasaki } 24888420Siwasaki 24988420Siwasaki /* adjust timeout for the next sleep */ 25088420Siwasaki tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz); 25188420Siwasaki if (tmo <= 0) 25288420Siwasaki tmo = 1; 25388420Siwasaki 25488420Siwasaki if (acpi_semaphore_debug) { 25588420Siwasaki printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n", 25688420Siwasaki __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId()); 25788420Siwasaki } 25867760Smsmith } 25988420Siwasaki 26088420Siwasaki if (acpi_semaphore_debug) { 26188420Siwasaki if (result == AE_TIME && Timeout > 0) { 26288420Siwasaki printf("%s: Timeout %d, pending %d, semaphore %p\n", 26388420Siwasaki __func__, Timeout, as->as_pendings, as); 26488420Siwasaki } 26588420Siwasaki if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 26688420Siwasaki printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n", 26788420Siwasaki __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 26888420Siwasaki } 26988420Siwasaki } 27088420Siwasaki 27188420Siwasaki if (result == AE_TIME) { 27288420Siwasaki as->as_timeouts++; 27388420Siwasaki } else { 27488420Siwasaki as->as_timeouts = 0; 27588420Siwasaki } 27688420Siwasaki 27772200Sbmilekic mtx_unlock(&as->as_mtx); 27867760Smsmith 27971876Smsmith return_ACPI_STATUS(result); 28071876Smsmith#else 28171876Smsmith return(AE_OK); 28271876Smsmith#endif 28367760Smsmith} 28467760Smsmith 28567760SmsmithACPI_STATUS 28667760SmsmithAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units) 28767760Smsmith{ 28871876Smsmith#ifndef ACPI_NO_SEMAPHORES 28967760Smsmith struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 29067760Smsmith 29177432Smsmith FUNCTION_TRACE(__func__); 29271876Smsmith 29367760Smsmith if (as == NULL) 29471876Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 29567760Smsmith 29672200Sbmilekic mtx_lock(&as->as_mtx); 29788420Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 29888420Siwasaki "return %d units to semaphore %p (has %d)\n", 29988420Siwasaki Units, as, as->as_units)); 30080071Smsmith if (as->as_units != ACPI_NO_UNIT_LIMIT) { 30180071Smsmith as->as_units += Units; 30280071Smsmith if (as->as_units > as->as_maxunits) 30380071Smsmith as->as_units = as->as_maxunits; 30480071Smsmith } 30588420Siwasaki 30688420Siwasaki if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 30788420Siwasaki printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 30888420Siwasaki __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 30988420Siwasaki } 31088420Siwasaki 31167760Smsmith wakeup(as); 31272200Sbmilekic mtx_unlock(&as->as_mtx); 31371876Smsmith return_ACPI_STATUS(AE_OK); 31471876Smsmith#else 31567760Smsmith return(AE_OK); 31671876Smsmith#endif 31767760Smsmith} 318