OsdSynch.c revision 107328
1169691Skan/*- 2169691Skan * Copyright (c) 2000 Michael Smith 3169691Skan * Copyright (c) 2000 BSDi 4169691Skan * All rights reserved. 5169691Skan * 6169691Skan * Redistribution and use in source and binary forms, with or without 7169691Skan * modification, are permitted provided that the following conditions 8169691Skan * are met: 9169691Skan * 1. Redistributions of source code must retain the above copyright 10169691Skan * notice, this list of conditions and the following disclaimer. 11169691Skan * 2. Redistributions in binary form must reproduce the above copyright 12169691Skan * notice, this list of conditions and the following disclaimer in the 13169691Skan * documentation and/or other materials provided with the distribution. 14169691Skan * 15169691Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16169691Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17169691Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18169691Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19169691Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20169691Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21169691Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22169691Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23169691Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24169691Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25169691Skan * SUCH DAMAGE. 26169691Skan * 27169691Skan * $FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 107328 2002-11-27 18:09:20Z iwasaki $ 28169691Skan */ 29169691Skan 30169691Skan/* 31169691Skan * 6.1 : Mutual Exclusion and Synchronisation 32169691Skan */ 33169691Skan 34169691Skan#include "acpi.h" 35169691Skan 36169691Skan#include "opt_acpi.h" 37169691Skan#include <sys/kernel.h> 38169691Skan#include <sys/malloc.h> 39169691Skan#include <sys/sysctl.h> 40169691Skan#if __FreeBSD_version >= 500000 41169691Skan#include <sys/lock.h> 42169691Skan#include <sys/mutex.h> 43169691Skan#endif 44169691Skan 45169691Skan#define _COMPONENT ACPI_OS_SERVICES 46169691SkanACPI_MODULE_NAME("SYNCH") 47169691Skan 48169691Skanstatic MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 49169691Skan 50169691Skan#if __FreeBSD_version < 500000 51169691Skan# define AS_LOCK(as) s = splhigh() 52169691Skan# define AS_UNLOCK(as) splx(s) 53169691Skan# define AS_LOCK_DECL int s 54169691Skan# define msleep(a, b, c, d, e) tsleep(a, c, d, e) 55169691Skan#else 56169691Skan# define AS_LOCK(as) mtx_lock(&(as)->as_mtx) 57169691Skan# define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx) 58169691Skan# define AS_LOCK_DECL 59169691Skan#endif 60169691Skan 61169691Skan/* 62169691Skan * Simple counting semaphore implemented using a mutex. (Subsequently used 63169691Skan * in the OSI code to implement a mutex. Go figure.) 64169691Skan */ 65169691Skanstruct acpi_semaphore { 66169691Skan#if __FreeBSD_version >= 500000 67169691Skan struct mtx as_mtx; 68169691Skan#endif 69169691Skan UINT32 as_units; 70169691Skan UINT32 as_maxunits; 71169691Skan UINT32 as_pendings; 72169691Skan UINT32 as_resetting; 73169691Skan UINT32 as_timeouts; 74169691Skan}; 75169691Skan 76169691Skan#ifndef ACPI_NO_SEMAPHORES 77169691Skan#ifndef ACPI_SEMAPHORES_MAX_PENDING 78169691Skan#define ACPI_SEMAPHORES_MAX_PENDING 4 79169691Skan#endif 80169691Skanstatic int acpi_semaphore_debug = 0; 81169691SkanTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 82169691SkanSYSCTL_INT(_debug, OID_AUTO, acpi_semaphore_debug, CTLFLAG_RW, 83169691Skan &acpi_semaphore_debug, 0, ""); 84169691Skan#endif 85169691Skan 86169691SkanACPI_STATUS 87169691SkanAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle) 88169691Skan{ 89169691Skan#ifndef ACPI_NO_SEMAPHORES 90169691Skan struct acpi_semaphore *as; 91169691Skan 92169691Skan ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 93169691Skan 94169691Skan if (OutHandle == NULL) 95169691Skan return(AE_BAD_PARAMETER); 96169691Skan if (InitialUnits > MaxUnits) 97169691Skan return_ACPI_STATUS(AE_BAD_PARAMETER); 98169691Skan 99169691Skan if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL) 100169691Skan return_ACPI_STATUS(AE_NO_MEMORY); 101169691Skan 102169691Skan bzero(as, sizeof(*as)); 103169691Skan#if __FreeBSD_version >= 500000 104169691Skan mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 105169691Skan#endif 106169691Skan as->as_units = InitialUnits; 107169691Skan as->as_maxunits = MaxUnits; 108169691Skan as->as_pendings = as->as_resetting = as->as_timeouts = 0; 109169691Skan 110169691Skan ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 111169691Skan "created semaphore %p max %d, initial %d\n", 112169691Skan as, InitialUnits, MaxUnits)); 113169691Skan 114169691Skan *OutHandle = (ACPI_HANDLE)as; 115169691Skan return_ACPI_STATUS(AE_OK); 116169691Skan#else 117169691Skan *OutHandle = (ACPI_HANDLE)OutHandle; 118169691Skan return(AE_OK); 119169691Skan#endif 120169691Skan} 121169691Skan 122169691SkanACPI_STATUS 123169691SkanAcpiOsDeleteSemaphore (ACPI_HANDLE Handle) 124169691Skan{ 125169691Skan#ifndef ACPI_NO_SEMAPHORES 126169691Skan struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 127169691Skan 128169691Skan ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 129169691Skan 130169691Skan ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 131169691Skan#if __FreeBSD_version >= 500000 132169691Skan mtx_destroy(&as->as_mtx); 133169691Skan#endif 134169691Skan free(Handle, M_ACPISEM); 135169691Skan return_ACPI_STATUS(AE_OK); 136169691Skan#else 137169691Skan return(AE_OK); 138169691Skan#endif 139169691Skan} 140169691Skan 141169691Skan/* 142169691Skan * This implementation has a bug, in that it has to stall for the entire 143169691Skan * timeout before it will return AE_TIME. A better implementation would 144169691Skan * use getmicrotime() to correctly adjust the timeout after being woken up. 145169691Skan */ 146169691SkanACPI_STATUS 147169691SkanAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout) 148169691Skan{ 149169691Skan#ifndef ACPI_NO_SEMAPHORES 150169691Skan struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 151169691Skan ACPI_STATUS result; 152169691Skan int rv, tmo; 153169691Skan struct timeval timeouttv, currenttv, timelefttv; 154169691Skan AS_LOCK_DECL; 155169691Skan 156169691Skan ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 157169691Skan 158169691Skan if (as == NULL) 159169691Skan return_ACPI_STATUS(AE_BAD_PARAMETER); 160169691Skan 161169691Skan if (cold) 162169691Skan return_ACPI_STATUS(AE_OK); 163169691Skan 164169691Skan#if 0 165169691Skan if (as->as_units < Units && as->as_timeouts > 10) { 166169691Skan printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 167169691Skan AS_LOCK(as); 168169691Skan as->as_units = as->as_maxunits; 169169691Skan if (as->as_pendings) 170169691Skan as->as_resetting = 1; 171169691Skan as->as_timeouts = 0; 172169691Skan wakeup(as); 173169691Skan AS_UNLOCK(as); 174169691Skan return_ACPI_STATUS(AE_TIME); 175169691Skan } 176169691Skan 177169691Skan if (as->as_resetting) { 178169691Skan return_ACPI_STATUS(AE_TIME); 179169691Skan } 180169691Skan#endif 181169691Skan 182169691Skan /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 183169691Skan if (Timeout == ACPI_WAIT_FOREVER) { 184169691Skan tmo = 0; 185169691Skan timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 186169691Skan timeouttv.tv_usec = 0; 187169691Skan } else { 188169691Skan /* compute timeout using microseconds per tick */ 189169691Skan tmo = (Timeout * 1000) / (1000000 / hz); 190169691Skan if (tmo <= 0) 191169691Skan tmo = 1; 192169691Skan timeouttv.tv_sec = Timeout / 1000; 193169691Skan timeouttv.tv_usec = (Timeout % 1000) * 1000; 194169691Skan } 195169691Skan 196169691Skan /* calculate timeout value in timeval */ 197169691Skan getmicrotime(¤ttv); 198169691Skan timevaladd(&timeouttv, ¤ttv); 199169691Skan 200169691Skan AS_LOCK(as); 201169691Skan ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 202169691Skan "get %d units from semaphore %p (has %d), timeout %d\n", 203169691Skan Units, as, as->as_units, Timeout)); 204169691Skan for (;;) { 205169691Skan if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 206169691Skan result = AE_OK; 207169691Skan break; 208169691Skan } 209169691Skan if (as->as_units >= Units) { 210169691Skan as->as_units -= Units; 211169691Skan result = AE_OK; 212169691Skan break; 213169691Skan } 214169691Skan 215169691Skan /* limit number of pending treads */ 216169691Skan if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 217169691Skan result = AE_TIME; 218169691Skan break; 219169691Skan } 220169691Skan 221169691Skan /* if timeout values of zero is specified, return immediately */ 222169691Skan if (Timeout == 0) { 223169691Skan result = AE_TIME; 224169691Skan break; 225169691Skan } 226169691Skan 227169691Skan#if __FreeBSD_version >= 500000 228169691Skan ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 229169691Skan "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 230169691Skan as, &as->as_mtx, PCATCH, tmo)); 231169691Skan#endif 232169691Skan 233169691Skan as->as_pendings++; 234169691Skan 235169691Skan if (acpi_semaphore_debug) { 236169691Skan printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 237169691Skan __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 238169691Skan } 239169691Skan 240169691Skan rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 241169691Skan 242169691Skan as->as_pendings--; 243169691Skan 244169691Skan#if 0 245169691Skan if (as->as_resetting) { 246169691Skan /* semaphore reset, return immediately */ 247169691Skan if (as->as_pendings == 0) { 248169691Skan as->as_resetting = 0; 249169691Skan } 250169691Skan result = AE_TIME; 251169691Skan break; 252169691Skan } 253169691Skan#endif 254169691Skan 255169691Skan ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 256169691Skan if (rv == EWOULDBLOCK) { 257169691Skan result = AE_TIME; 258169691Skan break; 259169691Skan } 260169691Skan 261169691Skan /* check if we already awaited enough */ 262169691Skan timelefttv = timeouttv; 263169691Skan getmicrotime(¤ttv); 264169691Skan timevalsub(&timelefttv, ¤ttv); 265169691Skan if (timelefttv.tv_sec < 0) { 266169691Skan ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as)); 267169691Skan result = AE_TIME; 268169691Skan break; 269169691Skan } 270169691Skan 271169691Skan /* adjust timeout for the next sleep */ 272169691Skan tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz); 273169691Skan if (tmo <= 0) 274169691Skan tmo = 1; 275169691Skan 276169691Skan if (acpi_semaphore_debug) { 277169691Skan printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n", 278169691Skan __func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId()); 279169691Skan } 280169691Skan } 281169691Skan 282169691Skan if (acpi_semaphore_debug) { 283169691Skan if (result == AE_TIME && Timeout > 0) { 284169691Skan printf("%s: Timeout %d, pending %d, semaphore %p\n", 285169691Skan __func__, Timeout, as->as_pendings, as); 286169691Skan } 287169691Skan if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 288169691Skan printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n", 289169691Skan __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 290 } 291 } 292 293 if (result == AE_TIME) { 294 as->as_timeouts++; 295 } else { 296 as->as_timeouts = 0; 297 } 298 299 AS_UNLOCK(as); 300 301 return_ACPI_STATUS(result); 302#else 303 return(AE_OK); 304#endif 305} 306 307ACPI_STATUS 308AcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units) 309{ 310#ifndef ACPI_NO_SEMAPHORES 311 struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 312 AS_LOCK_DECL; 313 314 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 315 316 if (as == NULL) 317 return_ACPI_STATUS(AE_BAD_PARAMETER); 318 319 AS_LOCK(as); 320 ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 321 "return %d units to semaphore %p (has %d)\n", 322 Units, as, as->as_units)); 323 if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 324 as->as_units += Units; 325 if (as->as_units > as->as_maxunits) 326 as->as_units = as->as_maxunits; 327 } 328 329 if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 330 printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 331 __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 332 } 333 334 wakeup(as); 335 AS_UNLOCK(as); 336 return_ACPI_STATUS(AE_OK); 337#else 338 return(AE_OK); 339#endif 340} 341