OsdSynch.c revision 91128
1131903Smarcel/*- 2131903Smarcel * Copyright (c) 2000 Michael Smith 3131903Smarcel * Copyright (c) 2000 BSDi 4131903Smarcel * All rights reserved. 5131903Smarcel * 6131903Smarcel * Redistribution and use in source and binary forms, with or without 7131903Smarcel * modification, are permitted provided that the following conditions 8131903Smarcel * are met: 9131903Smarcel * 1. Redistributions of source code must retain the above copyright 10131903Smarcel * notice, this list of conditions and the following disclaimer. 11131903Smarcel * 2. Redistributions in binary form must reproduce the above copyright 12131903Smarcel * notice, this list of conditions and the following disclaimer in the 13131903Smarcel * documentation and/or other materials provided with the distribution. 14131903Smarcel * 15131903Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16131903Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17131903Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18131903Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19131903Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20131903Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21131903Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22131903Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23131903Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24131903Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25131903Smarcel * SUCH DAMAGE. 26131903Smarcel * 27131903Smarcel * $FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 91128 2002-02-23 05:31:38Z msmith $ 28131903Smarcel */ 29131903Smarcel 30131903Smarcel/* 31131903Smarcel * 6.1 : Mutual Exclusion and Synchronisation 32131903Smarcel */ 33131903Smarcel 34131903Smarcel#include "acpi.h" 35131903Smarcel 36131903Smarcel#include "opt_acpi.h" 37131903Smarcel#include <sys/kernel.h> 38131903Smarcel#include <sys/lock.h> 39131903Smarcel#include <sys/malloc.h> 40131903Smarcel#include <sys/mutex.h> 41131903Smarcel#include <sys/sysctl.h> 42131903Smarcel 43131903Smarcel#define _COMPONENT ACPI_OS_SERVICES 44131903SmarcelACPI_MODULE_NAME("SYNCH") 45131903Smarcel 46131903Smarcelstatic MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 47131903Smarcel 48131903Smarcel/* 49131903Smarcel * Simple counting semaphore implemented using a mutex. (Subsequently used 50131903Smarcel * in the OSI code to implement a mutex. Go figure.) 51131903Smarcel */ 52131903Smarcelstruct acpi_semaphore { 53131903Smarcel struct mtx as_mtx; 54131903Smarcel UINT32 as_units; 55131903Smarcel UINT32 as_maxunits; 56131903Smarcel UINT32 as_pendings; 57131903Smarcel UINT32 as_resetting; 58131903Smarcel UINT32 as_timeouts; 59131903Smarcel}; 60131903Smarcel 61131903Smarcel#ifndef ACPI_NO_SEMAPHORES 62131903Smarcel#ifndef ACPI_SEMAPHORES_MAX_PENDING 63131903Smarcel#define ACPI_SEMAPHORES_MAX_PENDING 4 64131903Smarcel#endif 65131903Smarcelstatic int acpi_semaphore_debug = 0; 66131903SmarcelTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 67131903SmarcelSYSCTL_INT(_debug, OID_AUTO, acpi_semaphore_debug, CTLFLAG_RW, 68131903Smarcel &acpi_semaphore_debug, 0, ""); 69133737Srwatson#endif 70133737Srwatson 71133737SrwatsonACPI_STATUS 72133737SrwatsonAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle) 73133737Srwatson{ 74133737Srwatson#ifndef ACPI_NO_SEMAPHORES 75133737Srwatson struct acpi_semaphore *as; 76133737Srwatson 77133737Srwatson ACPI_FUNCTION_TRACE(__func__); 78133737Srwatson 79134162Srwatson if (OutHandle == NULL) 80133737Srwatson return(AE_BAD_PARAMETER); 81133737Srwatson if (InitialUnits > MaxUnits) 82131903Smarcel return_ACPI_STATUS(AE_BAD_PARAMETER); 83131903Smarcel 84131903Smarcel if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL) 85131903Smarcel return_ACPI_STATUS(AE_NO_MEMORY); 86131903Smarcel 87131903Smarcel bzero(as, sizeof(*as)); 88131903Smarcel mtx_init(&as->as_mtx, "ACPI semaphore", MTX_DEF); 89131903Smarcel as->as_units = InitialUnits; 90131903Smarcel as->as_maxunits = MaxUnits; 91131903Smarcel as->as_pendings = as->as_resetting = as->as_timeouts = 0; 92131903Smarcel 93131903Smarcel ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 94131903Smarcel "created semaphore %p max %d, initial %d\n", 95131903Smarcel as, InitialUnits, MaxUnits)); 96131903Smarcel 97131903Smarcel *OutHandle = (ACPI_HANDLE)as; 98131903Smarcel return_ACPI_STATUS(AE_OK); 99131903Smarcel#else 100131903Smarcel *OutHandle = (ACPI_HANDLE)OutHandle; 101131903Smarcel return(AE_OK); 102131903Smarcel#endif 103131903Smarcel} 104131903Smarcel 105131903SmarcelACPI_STATUS 106131903SmarcelAcpiOsDeleteSemaphore (ACPI_HANDLE Handle) 107131903Smarcel{ 108131903Smarcel#ifndef ACPI_NO_SEMAPHORES 109131903Smarcel struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 110131903Smarcel 111131903Smarcel ACPI_FUNCTION_TRACE(__func__); 112131903Smarcel 113131903Smarcel ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 114131903Smarcel mtx_destroy(&as->as_mtx); 115131903Smarcel free(Handle, M_ACPISEM); 116131903Smarcel return_ACPI_STATUS(AE_OK); 117131903Smarcel#else 118131903Smarcel return(AE_OK); 119131982Smarcel#endif 120131982Smarcel} 121131982Smarcel 122131982Smarcel/* 123131982Smarcel * This implementation has a bug, in that it has to stall for the entire 124131903Smarcel * timeout before it will return AE_TIME. A better implementation would 125131903Smarcel * use getmicrotime() to correctly adjust the timeout after being woken up. 126131903Smarcel */ 127131903SmarcelACPI_STATUS 128131903SmarcelAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT32 Timeout) 129132001Smarcel{ 130131903Smarcel#ifndef ACPI_NO_SEMAPHORES 131131903Smarcel struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 132131903Smarcel ACPI_STATUS result; 133131903Smarcel int rv, tmo; 134131903Smarcel struct timeval timeouttv, currenttv, timelefttv; 135131903Smarcel 136131903Smarcel ACPI_FUNCTION_TRACE(__func__); 137131903Smarcel 138131903Smarcel if (as == NULL) 139131903Smarcel return_ACPI_STATUS(AE_BAD_PARAMETER); 140131903Smarcel 141131903Smarcel if (cold) 142131903Smarcel return_ACPI_STATUS(AE_OK); 143131903Smarcel 144131903Smarcel#if 0 145131903Smarcel if (as->as_units < Units && as->as_timeouts > 10) { 146131903Smarcel printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 147131903Smarcel mtx_lock(&as->as_mtx); 148131903Smarcel as->as_units = as->as_maxunits; 149131903Smarcel if (as->as_pendings) 150131903Smarcel as->as_resetting = 1; 151131903Smarcel as->as_timeouts = 0; 152131903Smarcel wakeup(as); 153131903Smarcel mtx_unlock(&as->as_mtx); 154131903Smarcel return_ACPI_STATUS(AE_TIME); 155131903Smarcel } 156131903Smarcel 157131903Smarcel if (as->as_resetting) { 158131903Smarcel return_ACPI_STATUS(AE_TIME); 159131903Smarcel } 160131903Smarcel#endif 161131903Smarcel 162131903Smarcel /* a timeout of WAIT_FOREVER means "forever" */ 163131903Smarcel if (Timeout == WAIT_FOREVER) { 164131903Smarcel tmo = 0; 165131903Smarcel timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 166131903Smarcel timeouttv.tv_usec = 0; 167131903Smarcel } else { 168131903Smarcel /* compute timeout using microseconds per tick */ 169131903Smarcel tmo = (Timeout * 1000) / (1000000 / hz); 170131903Smarcel if (tmo <= 0) 171131903Smarcel tmo = 1; 172131903Smarcel timeouttv.tv_sec = Timeout / 1000; 173131903Smarcel timeouttv.tv_usec = (Timeout % 1000) * 1000; 174131903Smarcel } 175131903Smarcel 176131903Smarcel /* calculate timeout value in timeval */ 177131903Smarcel getmicrotime(¤ttv); 178131903Smarcel timevaladd(&timeouttv, ¤ttv); 179131903Smarcel 180131903Smarcel mtx_lock(&as->as_mtx); 181131903Smarcel ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 182131903Smarcel "get %d units from semaphore %p (has %d), timeout %d\n", 183131903Smarcel Units, as, as->as_units, Timeout)); 184131903Smarcel for (;;) { 185131903Smarcel if (as->as_units == ACPI_NO_UNIT_LIMIT) { 186131903Smarcel result = AE_OK; 187131903Smarcel break; 188131903Smarcel } 189131903Smarcel if (as->as_units >= Units) { 190131903Smarcel as->as_units -= Units; 191131903Smarcel result = AE_OK; 192131903Smarcel break; 193131903Smarcel } 194131903Smarcel 195131903Smarcel /* limit number of pending treads */ 196131903Smarcel if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 197131903Smarcel result = AE_TIME; 198131903Smarcel break; 199131903Smarcel } 200131903Smarcel 201131903Smarcel /* if timeout values of zero is specified, return immediately */ 202131903Smarcel if (Timeout == 0) { 203131903Smarcel result = AE_TIME; 204131903Smarcel break; 205131903Smarcel } 206131903Smarcel 207132001Smarcel ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 208132001Smarcel "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 209132001Smarcel as, &as->as_mtx, PCATCH, tmo)); 210132001Smarcel 211132001Smarcel as->as_pendings++; 212132001Smarcel 213132001Smarcel if (acpi_semaphore_debug) { 214132001Smarcel printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 215132001Smarcel __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 216132001Smarcel } 217132001Smarcel 218132001Smarcel rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 219132001Smarcel 220132001Smarcel as->as_pendings--; 221132001Smarcel 222132001Smarcel#if 0 223132001Smarcel if (as->as_resetting) { 224132001Smarcel /* semaphore reset, return immediately */ 225132001Smarcel if (as->as_pendings == 0) { 226131903Smarcel as->as_resetting = 0; 227131903Smarcel } 228131903Smarcel result = AE_TIME; 229131903Smarcel break; 230131903Smarcel } 231131903Smarcel#endif 232131903Smarcel 233131903Smarcel ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 234131903Smarcel if (rv == EWOULDBLOCK) { 235131903Smarcel result = AE_TIME; 236131903Smarcel break; 237131903Smarcel } 238131903Smarcel 239131903Smarcel /* check if we already awaited enough */ 240131903Smarcel timelefttv = timeouttv; 241131903Smarcel getmicrotime(¤ttv); 242131903Smarcel timevalsub(&timelefttv, ¤ttv); 243131903Smarcel if (timelefttv.tv_sec < 0) { 244131903Smarcel ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as)); 245131903Smarcel result = AE_TIME; 246131903Smarcel break; 247131903Smarcel } 248131903Smarcel 249131903Smarcel /* adjust timeout for the next sleep */ 250131903Smarcel tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz); 251131903Smarcel if (tmo <= 0) 252131903Smarcel tmo = 1; 253131903Smarcel 254131903Smarcel if (acpi_semaphore_debug) { 255131903Smarcel printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n", 256131903Smarcel __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId()); 257131903Smarcel } 258131903Smarcel } 259131903Smarcel 260131903Smarcel if (acpi_semaphore_debug) { 261131903Smarcel if (result == AE_TIME && Timeout > 0) { 262131903Smarcel printf("%s: Timeout %d, pending %d, semaphore %p\n", 263131903Smarcel __func__, Timeout, as->as_pendings, as); 264131903Smarcel } 265131903Smarcel if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 266131903Smarcel printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n", 267131903Smarcel __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 268131903Smarcel } 269131903Smarcel } 270131903Smarcel 271131903Smarcel if (result == AE_TIME) { 272131903Smarcel as->as_timeouts++; 273131903Smarcel } else { 274131903Smarcel as->as_timeouts = 0; 275131903Smarcel } 276131903Smarcel 277131903Smarcel mtx_unlock(&as->as_mtx); 278131903Smarcel 279131903Smarcel return_ACPI_STATUS(result); 280131903Smarcel#else 281131903Smarcel return(AE_OK); 282131903Smarcel#endif 283131903Smarcel} 284131903Smarcel 285131903SmarcelACPI_STATUS 286131903SmarcelAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units) 287131903Smarcel{ 288131903Smarcel#ifndef ACPI_NO_SEMAPHORES 289131903Smarcel struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 290131903Smarcel 291131903Smarcel ACPI_FUNCTION_TRACE(__func__); 292131903Smarcel 293131903Smarcel if (as == NULL) 294131903Smarcel return_ACPI_STATUS(AE_BAD_PARAMETER); 295131903Smarcel 296131903Smarcel mtx_lock(&as->as_mtx); 297131903Smarcel ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 298131903Smarcel "return %d units to semaphore %p (has %d)\n", 299131903Smarcel Units, as, as->as_units)); 300131903Smarcel if (as->as_units != ACPI_NO_UNIT_LIMIT) { 301131903Smarcel as->as_units += Units; 302131903Smarcel if (as->as_units > as->as_maxunits) 303131903Smarcel as->as_units = as->as_maxunits; 304131903Smarcel } 305131903Smarcel 306131903Smarcel if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 307131903Smarcel printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 308131903Smarcel __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 309131903Smarcel } 310131903Smarcel 311131903Smarcel wakeup(as); 312131903Smarcel mtx_unlock(&as->as_mtx); 313131903Smarcel return_ACPI_STATUS(AE_OK); 314131903Smarcel#else 315131903Smarcel return(AE_OK); 316131903Smarcel#endif 317131903Smarcel} 318131903Smarcel