OsdSynch.c revision 167910
1139823Simp/*- 2122922Sandre * Copyright (c) 2000 Michael Smith 3122922Sandre * Copyright (c) 2000 BSDi 4122922Sandre * All rights reserved. 5122922Sandre * 6122922Sandre * Redistribution and use in source and binary forms, with or without 7122922Sandre * modification, are permitted provided that the following conditions 8122922Sandre * are met: 9122922Sandre * 1. Redistributions of source code must retain the above copyright 10122922Sandre * notice, this list of conditions and the following disclaimer. 11122922Sandre * 2. Redistributions in binary form must reproduce the above copyright 12122922Sandre * notice, this list of conditions and the following disclaimer in the 13122922Sandre * documentation and/or other materials provided with the distribution. 14122922Sandre * 15122922Sandre * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16122922Sandre * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17122922Sandre * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18122922Sandre * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19122922Sandre * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20122922Sandre * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21122922Sandre * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22122922Sandre * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23122922Sandre * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24122922Sandre * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25122922Sandre * SUCH DAMAGE. 26122922Sandre */ 27122922Sandre 28122922Sandre/* 29122922Sandre * 6.1 : Mutual Exclusion and Synchronisation 30122922Sandre */ 31170030Srwatson 32170030Srwatson#include <sys/cdefs.h> 33170030Srwatson__FBSDID("$FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 167910 2007-03-26 21:23:23Z jkim $"); 34170030Srwatson 35170030Srwatson#include <contrib/dev/acpica/acpi.h> 36170030Srwatson 37170030Srwatson#include "opt_acpi.h" 38122922Sandre#include <sys/kernel.h> 39170030Srwatson#include <sys/malloc.h> 40182411Srpaulo#include <sys/sysctl.h> 41170030Srwatson#include <sys/lock.h> 42170030Srwatson#include <sys/mutex.h> 43170030Srwatson 44122922Sandre#define _COMPONENT ACPI_OS_SERVICES 45170030SrwatsonACPI_MODULE_NAME("SYNCH") 46170030Srwatson 47170030SrwatsonMALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore"); 48170030Srwatson 49170030Srwatson#define AS_LOCK(as) mtx_lock(&(as)->as_mtx) 50170030Srwatson#define AS_UNLOCK(as) mtx_unlock(&(as)->as_mtx) 51170030Srwatson 52170030Srwatson/* 53170030Srwatson * Simple counting semaphore implemented using a mutex. (Subsequently used 54170030Srwatson * in the OSI code to implement a mutex. Go figure.) 55170030Srwatson */ 56170030Srwatsonstruct acpi_semaphore { 57170030Srwatson struct mtx as_mtx; 58122922Sandre UINT32 as_units; 59122922Sandre UINT32 as_maxunits; 60122922Sandre UINT32 as_pendings; 61122922Sandre UINT32 as_resetting; 62122922Sandre UINT32 as_timeouts; 63122922Sandre}; 64122922Sandre 65172467Ssilby/* Default number of maximum pending threads. */ 66172467Ssilby#ifndef ACPI_NO_SEMAPHORES 67172467Ssilby#ifndef ACPI_SEMAPHORES_MAX_PENDING 68122922Sandre#define ACPI_SEMAPHORES_MAX_PENDING 4 69122922Sandre#endif 70122922Sandre 71122922Sandrestatic int acpi_semaphore_debug = 0; 72122922SandreTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug); 73122922SandreSYSCTL_DECL(_debug_acpi); 74122922SandreSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW, 75122922Sandre &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages"); 76122922Sandre#endif /* !ACPI_NO_SEMAPHORES */ 77122922Sandre 78122922SandreACPI_STATUS 79181803SbzAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, 80122922Sandre ACPI_HANDLE *OutHandle) 81122922Sandre{ 82194739Sbz#ifndef ACPI_NO_SEMAPHORES 83122922Sandre struct acpi_semaphore *as; 84122922Sandre 85122922Sandre ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 86122922Sandre 87122922Sandre if (OutHandle == NULL) 88122922Sandre return_ACPI_STATUS (AE_BAD_PARAMETER); 89122922Sandre if (InitialUnits > MaxUnits) 90122922Sandre return_ACPI_STATUS (AE_BAD_PARAMETER); 91122922Sandre 92122922Sandre if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL) 93122922Sandre return_ACPI_STATUS (AE_NO_MEMORY); 94122922Sandre 95122922Sandre mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF); 96185571Sbz as->as_units = InitialUnits; 97122922Sandre as->as_maxunits = MaxUnits; 98122922Sandre as->as_pendings = as->as_resetting = as->as_timeouts = 0; 99122922Sandre 100122922Sandre ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 101122922Sandre "created semaphore %p max %d, initial %d\n", 102122922Sandre as, InitialUnits, MaxUnits)); 103122922Sandre 104122922Sandre *OutHandle = (ACPI_HANDLE)as; 105122922Sandre#else 106122922Sandre *OutHandle = (ACPI_HANDLE)OutHandle; 107122922Sandre#endif /* !ACPI_NO_SEMAPHORES */ 108122922Sandre 109195699Srwatson return_ACPI_STATUS (AE_OK); 110195699Srwatson} 111122922Sandre 112195727SrwatsonACPI_STATUS 113195727SrwatsonAcpiOsDeleteSemaphore(ACPI_HANDLE Handle) 114195699Srwatson{ 115122922Sandre#ifndef ACPI_NO_SEMAPHORES 116122922Sandre struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 117122922Sandre 118122922Sandre ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 119122922Sandre 120182633Sbrooks ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as)); 121182633Sbrooks mtx_destroy(&as->as_mtx); 122122922Sandre free(Handle, M_ACPISEM); 123195699Srwatson#endif /* !ACPI_NO_SEMAPHORES */ 124195699Srwatson 125183550Szec return_ACPI_STATUS (AE_OK); 126122922Sandre} 127195699Srwatson 128195699Srwatson/* 129183550Szec * This implementation has a bug, in that it has to stall for the entire 130122922Sandre * timeout before it will return AE_TIME. A better implementation would 131195699Srwatson * use getmicrotime() to correctly adjust the timeout after being woken up. 132195699Srwatson */ 133183550SzecACPI_STATUS 134122922SandreAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout) 135195699Srwatson{ 136195699Srwatson#ifndef ACPI_NO_SEMAPHORES 137183550Szec ACPI_STATUS result; 138122922Sandre struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 139195699Srwatson int rv, tmo; 140195699Srwatson struct timeval timeouttv, currenttv, timelefttv; 141183550Szec 142122922Sandre ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 143195699Srwatson 144195699Srwatson if (as == NULL) 145195699Srwatson return_ACPI_STATUS (AE_BAD_PARAMETER); 146170434Syar 147195699Srwatson if (cold) 148195699Srwatson return_ACPI_STATUS (AE_OK); 149183550Szec 150122922Sandre#if 0 151122922Sandre if (as->as_units < Units && as->as_timeouts > 10) { 152167784Sandre printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as); 153167784Sandre AS_LOCK(as); 154122922Sandre as->as_units = as->as_maxunits; 155122922Sandre if (as->as_pendings) 156122922Sandre as->as_resetting = 1; 157122922Sandre as->as_timeouts = 0; 158122922Sandre wakeup(as); 159133874Srwatson AS_UNLOCK(as); 160181803Sbz return_ACPI_STATUS (AE_TIME); 161122922Sandre } 162122922Sandre 163133874Srwatson if (as->as_resetting) 164122922Sandre return_ACPI_STATUS (AE_TIME); 165122922Sandre#endif 166122922Sandre 167122922Sandre /* a timeout of ACPI_WAIT_FOREVER means "forever" */ 168181803Sbz if (Timeout == ACPI_WAIT_FOREVER) { 169122922Sandre tmo = 0; 170122922Sandre timeouttv.tv_sec = ((0xffff/1000) + 1); /* cf. ACPI spec */ 171122922Sandre timeouttv.tv_usec = 0; 172122922Sandre } else { 173122922Sandre /* compute timeout using microseconds per tick */ 174122922Sandre tmo = (Timeout * 1000) / (1000000 / hz); 175122922Sandre if (tmo <= 0) 176122922Sandre tmo = 1; 177122922Sandre timeouttv.tv_sec = Timeout / 1000; 178122922Sandre timeouttv.tv_usec = (Timeout % 1000) * 1000; 179170030Srwatson } 180122922Sandre 181181803Sbz /* calculate timeout value in timeval */ 182181803Sbz getmicrotime(¤ttv); 183181803Sbz timevaladd(&timeouttv, ¤ttv); 184181803Sbz 185181803Sbz AS_LOCK(as); 186181803Sbz ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 187181803Sbz "get %d units from semaphore %p (has %d), timeout %d\n", 188122922Sandre Units, as, as->as_units, Timeout)); 189133874Srwatson for (;;) { 190181803Sbz if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) { 191133874Srwatson result = AE_OK; 192181803Sbz break; 193133874Srwatson } 194181803Sbz if (as->as_units >= Units) { 195181803Sbz as->as_units -= Units; 196133874Srwatson result = AE_OK; 197181803Sbz break; 198133874Srwatson } 199181803Sbz 200122922Sandre /* limit number of pending threads */ 201122922Sandre if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) { 202170030Srwatson result = AE_TIME; 203122922Sandre break; 204181803Sbz } 205181803Sbz 206122922Sandre /* if timeout values of zero is specified, return immediately */ 207122922Sandre if (Timeout == 0) { 208122922Sandre result = AE_TIME; 209170030Srwatson break; 210122922Sandre } 211181803Sbz 212181803Sbz ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 213181803Sbz "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n", 214181803Sbz as, &as->as_mtx, PCATCH, tmo)); 215122922Sandre 216122922Sandre as->as_pendings++; 217122922Sandre 218122922Sandre if (acpi_semaphore_debug) { 219122922Sandre printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n", 220122922Sandre __func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId()); 221181887Sjulian } 222181888Sjulian 223181888Sjulian rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo); 224181803Sbz 225122922Sandre as->as_pendings--; 226122922Sandre 227122922Sandre#if 0 228122922Sandre if (as->as_resetting) { 229181803Sbz /* semaphore reset, return immediately */ 230181887Sjulian if (as->as_pendings == 0) { 231191816Szec as->as_resetting = 0; 232122922Sandre } 233122922Sandre result = AE_TIME; 234193731Szec break; 235193731Szec } 236193731Szec#endif 237193731Szec 238193731Szec ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv)); 239193731Szec if (rv == EWOULDBLOCK) { 240193731Szec result = AE_TIME; 241193731Szec break; 242193731Szec } 243193731Szec 244193731Szec /* check if we already awaited enough */ 245122922Sandre timelefttv = timeouttv; 246170030Srwatson getmicrotime(¤ttv); 247122922Sandre timevalsub(&timelefttv, ¤ttv); 248122922Sandre if (timelefttv.tv_sec < 0) { 249122922Sandre ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", 250122922Sandre as)); 251122922Sandre result = AE_TIME; 252122922Sandre break; 253122922Sandre } 254122922Sandre 255122922Sandre /* adjust timeout for the next sleep */ 256122922Sandre tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / 257122922Sandre (1000000 / hz); 258122922Sandre if (tmo <= 0) 259122922Sandre tmo = 1; 260122922Sandre 261122922Sandre if (acpi_semaphore_debug) { 262122922Sandre printf("%s: Wakeup timeleft(%jd, %lu), tmo %u, sem %p, thread %d\n", 263186222Sbz __func__, (intmax_t)timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, 264122922Sandre AcpiOsGetThreadId()); 265122922Sandre } 266122922Sandre } 267122922Sandre 268181803Sbz if (acpi_semaphore_debug) { 269122922Sandre if (result == AE_TIME && Timeout > 0) { 270122922Sandre printf("%s: Timeout %d, pending %d, semaphore %p\n", 271170030Srwatson __func__, Timeout, as->as_pendings, as); 272170030Srwatson } 273170030Srwatson if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) { 274122922Sandre printf("%s: Acquire %d, units %d, pending %d, sem %p, thread %d\n", 275122922Sandre __func__, Units, as->as_units, as->as_pendings, as, 276122922Sandre AcpiOsGetThreadId()); 277122922Sandre } 278170030Srwatson } 279122922Sandre 280122922Sandre if (result == AE_TIME) 281186222Sbz as->as_timeouts++; 282122922Sandre else 283122922Sandre as->as_timeouts = 0; 284122922Sandre 285122922Sandre AS_UNLOCK(as); 286122922Sandre return_ACPI_STATUS (result); 287122922Sandre#else 288122922Sandre return_ACPI_STATUS (AE_OK); 289122922Sandre#endif /* !ACPI_NO_SEMAPHORES */ 290122922Sandre} 291122922Sandre 292122922SandreACPI_STATUS 293170030SrwatsonAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units) 294122922Sandre{ 295122922Sandre#ifndef ACPI_NO_SEMAPHORES 296122922Sandre struct acpi_semaphore *as = (struct acpi_semaphore *)Handle; 297122922Sandre 298122922Sandre ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 299122922Sandre 300170030Srwatson if (as == NULL) 301170030Srwatson return_ACPI_STATUS(AE_BAD_PARAMETER); 302133874Srwatson 303122922Sandre AS_LOCK(as); 304122922Sandre ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, 305122922Sandre "return %d units to semaphore %p (has %d)\n", 306122922Sandre Units, as, as->as_units)); 307122922Sandre if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) { 308122922Sandre as->as_units += Units; 309122922Sandre if (as->as_units > as->as_maxunits) 310122922Sandre as->as_units = as->as_maxunits; 311122922Sandre } 312122922Sandre 313122922Sandre if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) { 314122922Sandre printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n", 315122922Sandre __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId()); 316170030Srwatson } 317122922Sandre 318186222Sbz wakeup(as); 319122922Sandre AS_UNLOCK(as); 320122922Sandre#endif /* !ACPI_NO_SEMAPHORES */ 321122922Sandre 322122922Sandre return_ACPI_STATUS (AE_OK); 323181803Sbz} 324122922Sandre 325122922Sandre/* Combined mutex + mutex name storage since the latter must persist. */ 326170030Srwatsonstruct acpi_spinlock { 327170030Srwatson struct mtx lock; 328170030Srwatson char name[32]; 329122922Sandre}; 330122922Sandre 331122922SandreACPI_STATUS 332122922SandreAcpiOsCreateLock (ACPI_SPINLOCK *OutHandle) 333170030Srwatson{ 334122922Sandre struct acpi_spinlock *h; 335181803Sbz 336181803Sbz if (OutHandle == NULL) 337122922Sandre return (AE_BAD_PARAMETER); 338122922Sandre h = malloc(sizeof(struct acpi_spinlock), M_ACPISEM, M_NOWAIT | M_ZERO); 339122922Sandre if (h == NULL) 340170030Srwatson return (AE_NO_MEMORY); 341170030Srwatson 342170030Srwatson /* Build a unique name based on the address of the handle. */ 343170030Srwatson if (OutHandle == &AcpiGbl_GpeLock) 344170405Sandre snprintf(h->name, sizeof(h->name), "acpi subsystem GPE lock"); 345170405Sandre if (OutHandle == &AcpiGbl_HardwareLock) 346122922Sandre snprintf(h->name, sizeof(h->name), "acpi subsystem HW lock"); 347170405Sandre else 348170405Sandre snprintf(h->name, sizeof(h->name), "acpi subsys %p", OutHandle); 349170405Sandre mtx_init(&h->lock, h->name, NULL, MTX_DEF); 350170405Sandre *OutHandle = (ACPI_SPINLOCK)h; 351122922Sandre return (AE_OK); 352181803Sbz} 353181803Sbz 354190948Srwatsonvoid 355123028SandreAcpiOsDeleteLock (ACPI_SPINLOCK Handle) 356181803Sbz{ 357122922Sandre struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 358122922Sandre 359122922Sandre if (Handle == NULL) 360170030Srwatson return; 361122922Sandre mtx_destroy(&h->lock); 362181803Sbz free(&h->lock, M_ACPISEM); 363122922Sandre} 364122922Sandre 365122922Sandre/* 366122922Sandre * The Flags parameter seems to state whether or not caller is an ISR 367122922Sandre * (and thus can't block) but since we have ithreads, we don't worry 368122922Sandre * about potentially blocking. 369122922Sandre */ 370170030SrwatsonACPI_NATIVE_UINT 371122922SandreAcpiOsAcquireLock (ACPI_SPINLOCK Handle) 372122922Sandre{ 373186222Sbz struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 374123113Sandre 375122922Sandre if (Handle == NULL) 376122922Sandre return (0); 377122922Sandre mtx_lock(&h->lock); 378181803Sbz return (0); 379122922Sandre} 380122922Sandre 381170030Srwatsonvoid 382122922SandreAcpiOsReleaseLock (ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags) 383122922Sandre{ 384181803Sbz struct acpi_spinlock *h = (struct acpi_spinlock *)Handle; 385181803Sbz 386190948Srwatson if (Handle == NULL) 387122922Sandre return; 388122922Sandre mtx_unlock(&h->lock); 389122922Sandre} 390122922Sandre 391122922Sandre/* Section 5.2.9.1: global lock acquire/release functions */ 392170030Srwatson#define GL_ACQUIRED (-1) 393170030Srwatson#define GL_BUSY 0 394170030Srwatson#define GL_BIT_PENDING 0x1 395122922Sandre#define GL_BIT_OWNED 0x2 396122922Sandre#define GL_BIT_MASK (GL_BIT_PENDING | GL_BIT_OWNED) 397122922Sandre 398122922Sandre/* 399122922Sandre * Acquire the global lock. If busy, set the pending bit. The caller 400122922Sandre * will wait for notification from the BIOS that the lock is available 401122922Sandre * and then attempt to acquire it again. 402170030Srwatson */ 403122922Sandreint 404122922Sandreacpi_acquire_global_lock(uint32_t *lock) 405122922Sandre{ 406122922Sandre uint32_t new, old; 407170030Srwatson 408122922Sandre do { 409122922Sandre old = *lock; 410122922Sandre new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) | 411122922Sandre ((old >> 1) & GL_BIT_PENDING); 412122922Sandre } while (atomic_cmpset_acq_int(lock, old, new) == 0); 413122922Sandre 414181803Sbz return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY); 415122922Sandre} 416122922Sandre 417122922Sandre/* 418122922Sandre * Release the global lock, returning whether there is a waiter pending. 419122922Sandre * If the BIOS set the pending bit, OSPM must notify the BIOS when it 420122922Sandre * releases the lock. 421122922Sandre */ 422122922Sandreint 423122922Sandreacpi_release_global_lock(uint32_t *lock) 424122922Sandre{ 425122922Sandre uint32_t new, old; 426170030Srwatson 427122922Sandre do { 428122922Sandre old = *lock; 429122922Sandre new = old & ~GL_BIT_MASK; 430122922Sandre } while (atomic_cmpset_rel_int(lock, old, new) == 0); 431122922Sandre 432170030Srwatson return (old & GL_BIT_PENDING); 433170030Srwatson} 434138409Srwatson