OsdSynch.c revision 177934
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
2867760Smsmith/*
2967760Smsmith * 6.1 : Mutual Exclusion and Synchronisation
3067760Smsmith */
3167760Smsmith
32148318Snjl#include <sys/cdefs.h>
33148318Snjl__FBSDID("$FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 177934 2008-04-05 14:21:01Z takawata $");
34148318Snjl
35150003Sobrien#include <contrib/dev/acpica/acpi.h>
3667760Smsmith
3788420Siwasaki#include "opt_acpi.h"
3867760Smsmith#include <sys/kernel.h>
39105278Sjhb#include <sys/malloc.h>
40105278Sjhb#include <sys/sysctl.h>
4174914Sjhb#include <sys/lock.h>
4267760Smsmith#include <sys/mutex.h>
4367760Smsmith
4477432Smsmith#define _COMPONENT	ACPI_OS_SERVICES
4591128SmsmithACPI_MODULE_NAME("SYNCH")
4671876Smsmith
47128227SnjlMALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
4867760Smsmith
49130695Snjl#define AS_LOCK(as)	mtx_lock(&(as)->as_mtx)
50130695Snjl#define AS_UNLOCK(as)	mtx_unlock(&(as)->as_mtx)
51105278Sjhb
5267760Smsmith/*
53128227Snjl * Simple counting semaphore implemented using a mutex.  (Subsequently used
5467760Smsmith * in the OSI code to implement a mutex.  Go figure.)
5567760Smsmith */
5667760Smsmithstruct acpi_semaphore {
5767760Smsmith    struct mtx	as_mtx;
5867760Smsmith    UINT32	as_units;
5967760Smsmith    UINT32	as_maxunits;
6088420Siwasaki    UINT32	as_pendings;
6188420Siwasaki    UINT32	as_resetting;
6288420Siwasaki    UINT32	as_timeouts;
6367760Smsmith};
6467760Smsmith
65167814Sjkim/* Default number of maximum pending threads. */
6688420Siwasaki#ifndef ACPI_NO_SEMAPHORES
6788420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING
6888420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING	4
6988420Siwasaki#endif
70167814Sjkim
7188420Siwasakistatic int	acpi_semaphore_debug = 0;
7288420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug);
73120494SnjlSYSCTL_DECL(_debug_acpi);
74120494SnjlSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW,
75120494Snjl	   &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages");
76128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
7788420Siwasaki
7867760SmsmithACPI_STATUS
79128227SnjlAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits,
80167915Sjkim    ACPI_SEMAPHORE *OutHandle)
8167760Smsmith{
8271876Smsmith#ifndef ACPI_NO_SEMAPHORES
8367760Smsmith    struct acpi_semaphore	*as;
8467760Smsmith
8596926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
8671876Smsmith
8767760Smsmith    if (OutHandle == NULL)
88128227Snjl	return_ACPI_STATUS (AE_BAD_PARAMETER);
8967760Smsmith    if (InitialUnits > MaxUnits)
90128227Snjl	return_ACPI_STATUS (AE_BAD_PARAMETER);
9167760Smsmith
92128227Snjl    if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT | M_ZERO)) == NULL)
93128227Snjl	return_ACPI_STATUS (AE_NO_MEMORY);
9467760Smsmith
9593818Sjhb    mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF);
9667760Smsmith    as->as_units = InitialUnits;
9767760Smsmith    as->as_maxunits = MaxUnits;
9888420Siwasaki    as->as_pendings = as->as_resetting = as->as_timeouts = 0;
9967760Smsmith
10088420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
10188420Siwasaki	"created semaphore %p max %d, initial %d\n",
10288420Siwasaki	as, InitialUnits, MaxUnits));
10371876Smsmith
10467760Smsmith    *OutHandle = (ACPI_HANDLE)as;
10571876Smsmith#else
10671876Smsmith    *OutHandle = (ACPI_HANDLE)OutHandle;
107128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
108128227Snjl
109128227Snjl    return_ACPI_STATUS (AE_OK);
11067760Smsmith}
11167760Smsmith
11267760SmsmithACPI_STATUS
113167915SjkimAcpiOsDeleteSemaphore(ACPI_SEMAPHORE Handle)
11467760Smsmith{
11571876Smsmith#ifndef ACPI_NO_SEMAPHORES
11671359Smsmith    struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
11771876Smsmith
11896926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
11971876Smsmith
12088420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as));
12171359Smsmith    mtx_destroy(&as->as_mtx);
12267760Smsmith    free(Handle, M_ACPISEM);
123128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
124128227Snjl
125128227Snjl    return_ACPI_STATUS (AE_OK);
12667760Smsmith}
12767760Smsmith
12867760Smsmith/*
12967760Smsmith * This implementation has a bug, in that it has to stall for the entire
13067760Smsmith * timeout before it will return AE_TIME.  A better implementation would
13167760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up.
13267760Smsmith */
13367760SmsmithACPI_STATUS
134167915SjkimAcpiOsWaitSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units, UINT16 Timeout)
13567760Smsmith{
13671876Smsmith#ifndef ACPI_NO_SEMAPHORES
137128227Snjl    ACPI_STATUS			result;
13867760Smsmith    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
13971876Smsmith    int				rv, tmo;
14088420Siwasaki    struct timeval		timeouttv, currenttv, timelefttv;
14167760Smsmith
14296926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
14371876Smsmith
14467760Smsmith    if (as == NULL)
145128227Snjl	return_ACPI_STATUS (AE_BAD_PARAMETER);
14667760Smsmith
14788420Siwasaki    if (cold)
148128227Snjl	return_ACPI_STATUS (AE_OK);
14988420Siwasaki
15088420Siwasaki#if 0
15188420Siwasaki    if (as->as_units < Units && as->as_timeouts > 10) {
15288420Siwasaki	printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as);
153105278Sjhb	AS_LOCK(as);
15488420Siwasaki	as->as_units = as->as_maxunits;
15588420Siwasaki	if (as->as_pendings)
15688420Siwasaki	    as->as_resetting = 1;
15788420Siwasaki	as->as_timeouts = 0;
15888420Siwasaki	wakeup(as);
159105278Sjhb	AS_UNLOCK(as);
160128227Snjl	return_ACPI_STATUS (AE_TIME);
16188420Siwasaki    }
16288420Siwasaki
163128227Snjl    if (as->as_resetting)
164128227Snjl	return_ACPI_STATUS (AE_TIME);
16588420Siwasaki#endif
16688420Siwasaki
167107328Siwasaki    /* a timeout of ACPI_WAIT_FOREVER means "forever" */
168107328Siwasaki    if (Timeout == ACPI_WAIT_FOREVER) {
16971876Smsmith	tmo = 0;
17088420Siwasaki	timeouttv.tv_sec = ((0xffff/1000) + 1);	/* cf. ACPI spec */
17188420Siwasaki	timeouttv.tv_usec = 0;
17271876Smsmith    } else {
17371876Smsmith	/* compute timeout using microseconds per tick */
17477432Smsmith	tmo = (Timeout * 1000) / (1000000 / hz);
17571876Smsmith	if (tmo <= 0)
17671876Smsmith	    tmo = 1;
17788420Siwasaki	timeouttv.tv_sec  = Timeout / 1000;
17888420Siwasaki	timeouttv.tv_usec = (Timeout % 1000) * 1000;
17971876Smsmith    }
18071876Smsmith
18188420Siwasaki    /* calculate timeout value in timeval */
18288420Siwasaki    getmicrotime(&currenttv);
18388420Siwasaki    timevaladd(&timeouttv, &currenttv);
18488420Siwasaki
185105278Sjhb    AS_LOCK(as);
18688420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
18788420Siwasaki	"get %d units from semaphore %p (has %d), timeout %d\n",
18888420Siwasaki	Units, as, as->as_units, Timeout));
18967760Smsmith    for (;;) {
19099492Siwasaki	if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) {
19180071Smsmith	    result = AE_OK;
19280071Smsmith	    break;
19380071Smsmith	}
19467760Smsmith	if (as->as_units >= Units) {
19567760Smsmith	    as->as_units -= Units;
19667760Smsmith	    result = AE_OK;
19767760Smsmith	    break;
19867760Smsmith	}
19988420Siwasaki
200167814Sjkim	/* limit number of pending threads */
20188420Siwasaki	if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) {
20267760Smsmith	    result = AE_TIME;
20367760Smsmith	    break;
20467760Smsmith	}
20588420Siwasaki
20688420Siwasaki	/* if timeout values of zero is specified, return immediately */
20788420Siwasaki	if (Timeout == 0) {
20888420Siwasaki	    result = AE_TIME;
20988420Siwasaki	    break;
21088420Siwasaki	}
21188420Siwasaki
21288420Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
21388420Siwasaki	    "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n",
21488420Siwasaki	    as, &as->as_mtx, PCATCH, tmo));
21588420Siwasaki
21688420Siwasaki	as->as_pendings++;
21788420Siwasaki
21888420Siwasaki	if (acpi_semaphore_debug) {
21988420Siwasaki	    printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n",
22088420Siwasaki		__func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId());
22188420Siwasaki	}
22288420Siwasaki
22388420Siwasaki	rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo);
22488420Siwasaki
22588420Siwasaki	as->as_pendings--;
22688420Siwasaki
22788420Siwasaki#if 0
22888420Siwasaki	if (as->as_resetting) {
22988420Siwasaki	    /* semaphore reset, return immediately */
23088420Siwasaki	    if (as->as_pendings == 0) {
23188420Siwasaki		as->as_resetting = 0;
23288420Siwasaki	    }
23388420Siwasaki	    result = AE_TIME;
23488420Siwasaki	    break;
23588420Siwasaki	}
23688420Siwasaki#endif
23788420Siwasaki
23888420Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv));
23971876Smsmith	if (rv == EWOULDBLOCK) {
24067760Smsmith	    result = AE_TIME;
24167760Smsmith	    break;
24267760Smsmith	}
24388420Siwasaki
24488420Siwasaki	/* check if we already awaited enough */
24588420Siwasaki	timelefttv = timeouttv;
24688420Siwasaki	getmicrotime(&currenttv);
24788420Siwasaki	timevalsub(&timelefttv, &currenttv);
24888420Siwasaki	if (timelefttv.tv_sec < 0) {
249128227Snjl	    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n",
250128227Snjl		as));
25188420Siwasaki	    result = AE_TIME;
25288420Siwasaki	    break;
25388420Siwasaki	}
25488420Siwasaki
25588420Siwasaki	/* adjust timeout for the next sleep */
256128227Snjl	tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) /
257128227Snjl	    (1000000 / hz);
25888420Siwasaki	if (tmo <= 0)
25988420Siwasaki	    tmo = 1;
26088420Siwasaki
26188420Siwasaki	if (acpi_semaphore_debug) {
262153706Strhodes	    printf("%s: Wakeup timeleft(%jd, %lu), tmo %u, sem %p, thread %d\n",
263153706Strhodes		__func__, (intmax_t)timelefttv.tv_sec, timelefttv.tv_usec, tmo, as,
264128227Snjl		AcpiOsGetThreadId());
26588420Siwasaki	}
26667760Smsmith    }
26788420Siwasaki
26888420Siwasaki    if (acpi_semaphore_debug) {
26988420Siwasaki	if (result == AE_TIME && Timeout > 0) {
27088420Siwasaki	    printf("%s: Timeout %d, pending %d, semaphore %p\n",
27188420Siwasaki		__func__, Timeout, as->as_pendings, as);
27288420Siwasaki	}
27388420Siwasaki	if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) {
274128227Snjl	    printf("%s: Acquire %d, units %d, pending %d, sem %p, thread %d\n",
275128227Snjl		__func__, Units, as->as_units, as->as_pendings, as,
276128227Snjl		AcpiOsGetThreadId());
27788420Siwasaki	}
27888420Siwasaki    }
27988420Siwasaki
280128227Snjl    if (result == AE_TIME)
28188420Siwasaki	as->as_timeouts++;
282128227Snjl    else
28388420Siwasaki	as->as_timeouts = 0;
28488420Siwasaki
285105278Sjhb    AS_UNLOCK(as);
286128227Snjl    return_ACPI_STATUS (result);
28771876Smsmith#else
288128227Snjl    return_ACPI_STATUS (AE_OK);
289128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
29067760Smsmith}
29167760Smsmith
29267760SmsmithACPI_STATUS
293167915SjkimAcpiOsSignalSemaphore(ACPI_SEMAPHORE Handle, UINT32 Units)
29467760Smsmith{
29571876Smsmith#ifndef ACPI_NO_SEMAPHORES
29667760Smsmith    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
29767760Smsmith
29896926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
29971876Smsmith
30067760Smsmith    if (as == NULL)
30171876Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
30267760Smsmith
303105278Sjhb    AS_LOCK(as);
30488420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
30588420Siwasaki	"return %d units to semaphore %p (has %d)\n",
30688420Siwasaki	Units, as, as->as_units));
30799492Siwasaki    if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) {
30880071Smsmith	as->as_units += Units;
30980071Smsmith	if (as->as_units > as->as_maxunits)
31080071Smsmith	    as->as_units = as->as_maxunits;
31180071Smsmith    }
31288420Siwasaki
31388420Siwasaki    if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) {
31488420Siwasaki	printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n",
31588420Siwasaki	    __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
31688420Siwasaki    }
31788420Siwasaki
31867760Smsmith    wakeup(as);
319105278Sjhb    AS_UNLOCK(as);
320128227Snjl#endif /* !ACPI_NO_SEMAPHORES */
321128227Snjl
322128227Snjl    return_ACPI_STATUS (AE_OK);
32367760Smsmith}
324117530Snjl
325167908Snjl/* Combined mutex + mutex name storage since the latter must persist. */
326167910Sjkimstruct acpi_spinlock {
327167910Sjkim    struct mtx	lock;
328167910Sjkim    char	name[32];
329167908Snjl};
330167908Snjl
331117530SnjlACPI_STATUS
332167910SjkimAcpiOsCreateLock (ACPI_SPINLOCK *OutHandle)
333117530Snjl{
334167910Sjkim    struct acpi_spinlock *h;
335117530Snjl
336117530Snjl    if (OutHandle == NULL)
337117530Snjl	return (AE_BAD_PARAMETER);
338167918Sjkim    h = malloc(sizeof(*h), M_ACPISEM, M_NOWAIT | M_ZERO);
339167910Sjkim    if (h == NULL)
340117530Snjl	return (AE_NO_MEMORY);
341117530Snjl
342167908Snjl    /* Build a unique name based on the address of the handle. */
343167910Sjkim    if (OutHandle == &AcpiGbl_GpeLock)
344167910Sjkim	snprintf(h->name, sizeof(h->name), "acpi subsystem GPE lock");
345167918Sjkim    else if (OutHandle == &AcpiGbl_HardwareLock)
346167910Sjkim	snprintf(h->name, sizeof(h->name), "acpi subsystem HW lock");
347167910Sjkim    else
348167910Sjkim	snprintf(h->name, sizeof(h->name), "acpi subsys %p", OutHandle);
349177934Stakawata    mtx_init(&h->lock, h->name, NULL, MTX_DEF|MTX_RECURSE);
350167910Sjkim    *OutHandle = (ACPI_SPINLOCK)h;
351117530Snjl    return (AE_OK);
352117530Snjl}
353117530Snjl
354117530Snjlvoid
355167910SjkimAcpiOsDeleteLock (ACPI_SPINLOCK Handle)
356117530Snjl{
357167910Sjkim    struct acpi_spinlock *h = (struct acpi_spinlock *)Handle;
358117530Snjl
359117530Snjl    if (Handle == NULL)
360117530Snjl        return;
361167910Sjkim    mtx_destroy(&h->lock);
362167911Sjkim    free(h, M_ACPISEM);
363117530Snjl}
364117530Snjl
365117530Snjl/*
366117530Snjl * The Flags parameter seems to state whether or not caller is an ISR
367117530Snjl * (and thus can't block) but since we have ithreads, we don't worry
368117530Snjl * about potentially blocking.
369117530Snjl */
370151948SjkimACPI_NATIVE_UINT
371167910SjkimAcpiOsAcquireLock (ACPI_SPINLOCK Handle)
372117530Snjl{
373167910Sjkim    struct acpi_spinlock *h = (struct acpi_spinlock *)Handle;
374117530Snjl
375117530Snjl    if (Handle == NULL)
376151948Sjkim	return (0);
377167910Sjkim    mtx_lock(&h->lock);
378151948Sjkim    return (0);
379117530Snjl}
380117530Snjl
381117530Snjlvoid
382167910SjkimAcpiOsReleaseLock (ACPI_SPINLOCK Handle, ACPI_CPU_FLAGS Flags)
383117530Snjl{
384167910Sjkim    struct acpi_spinlock *h = (struct acpi_spinlock *)Handle;
385117530Snjl
386117530Snjl    if (Handle == NULL)
387128227Snjl	return;
388167910Sjkim    mtx_unlock(&h->lock);
389117530Snjl}
390128979Snjl
391128979Snjl/* Section 5.2.9.1:  global lock acquire/release functions */
392128979Snjl#define GL_ACQUIRED	(-1)
393128979Snjl#define GL_BUSY		0
394128979Snjl#define GL_BIT_PENDING	0x1
395128979Snjl#define GL_BIT_OWNED	0x2
396128979Snjl#define GL_BIT_MASK	(GL_BIT_PENDING | GL_BIT_OWNED)
397128979Snjl
398128979Snjl/*
399128979Snjl * Acquire the global lock.  If busy, set the pending bit.  The caller
400128979Snjl * will wait for notification from the BIOS that the lock is available
401128979Snjl * and then attempt to acquire it again.
402128979Snjl */
403128979Snjlint
404128979Snjlacpi_acquire_global_lock(uint32_t *lock)
405128979Snjl{
406128979Snjl	uint32_t new, old;
407128979Snjl
408128979Snjl	do {
409128979Snjl		old = *lock;
410128981Snjl		new = ((old & ~GL_BIT_MASK) | GL_BIT_OWNED) |
411128981Snjl			((old >> 1) & GL_BIT_PENDING);
412128979Snjl	} while (atomic_cmpset_acq_int(lock, old, new) == 0);
413128979Snjl
414128979Snjl	return ((new < GL_BIT_MASK) ? GL_ACQUIRED : GL_BUSY);
415128979Snjl}
416128979Snjl
417128979Snjl/*
418128979Snjl * Release the global lock, returning whether there is a waiter pending.
419128979Snjl * If the BIOS set the pending bit, OSPM must notify the BIOS when it
420128979Snjl * releases the lock.
421128979Snjl */
422128979Snjlint
423128979Snjlacpi_release_global_lock(uint32_t *lock)
424128979Snjl{
425128979Snjl	uint32_t new, old;
426128979Snjl
427128979Snjl	do {
428128979Snjl		old = *lock;
429128979Snjl		new = old & ~GL_BIT_MASK;
430128979Snjl	} while (atomic_cmpset_rel_int(lock, old, new) == 0);
431128979Snjl
432128979Snjl	return (old & GL_BIT_PENDING);
433128979Snjl}
434