OsdSynch.c revision 120494
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 120494 2003-09-26 21:22:10Z njl $
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>
38105278Sjhb#include <sys/malloc.h>
39105278Sjhb#include <sys/sysctl.h>
40105278Sjhb#if __FreeBSD_version >= 500000
4174914Sjhb#include <sys/lock.h>
4267760Smsmith#include <sys/mutex.h>
43105278Sjhb#endif
4467760Smsmith
4577432Smsmith#define _COMPONENT	ACPI_OS_SERVICES
4691128SmsmithACPI_MODULE_NAME("SYNCH")
4771876Smsmith
4869776Smsmithstatic MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
4967760Smsmith
50105278Sjhb#if __FreeBSD_version < 500000
51105278Sjhb# define AS_LOCK(as)		s = splhigh()
52105278Sjhb# define AS_UNLOCK(as)		splx(s)
53105278Sjhb# define AS_LOCK_DECL		int s
54105278Sjhb# define msleep(a, b, c, d, e)	tsleep(a, c, d, e)
55105278Sjhb#else
56105278Sjhb# define AS_LOCK(as)		mtx_lock(&(as)->as_mtx)
57105278Sjhb# define AS_UNLOCK(as)		mtx_unlock(&(as)->as_mtx)
58105278Sjhb# define AS_LOCK_DECL
59105278Sjhb#endif
60105278Sjhb
6167760Smsmith/*
6267760Smsmith * Simple counting semaphore implemented using a mutex. (Subsequently used
6367760Smsmith * in the OSI code to implement a mutex.  Go figure.)
6467760Smsmith */
6567760Smsmithstruct acpi_semaphore {
66105278Sjhb#if __FreeBSD_version >= 500000
6767760Smsmith    struct mtx	as_mtx;
68105278Sjhb#endif
6967760Smsmith    UINT32	as_units;
7067760Smsmith    UINT32	as_maxunits;
7188420Siwasaki    UINT32	as_pendings;
7288420Siwasaki    UINT32	as_resetting;
7388420Siwasaki    UINT32	as_timeouts;
7467760Smsmith};
7567760Smsmith
7688420Siwasaki#ifndef ACPI_NO_SEMAPHORES
7788420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING
7888420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING	4
7988420Siwasaki#endif
8088420Siwasakistatic int	acpi_semaphore_debug = 0;
8188420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug);
82120494SnjlSYSCTL_DECL(_debug_acpi);
83120494SnjlSYSCTL_INT(_debug_acpi, OID_AUTO, semaphore_debug, CTLFLAG_RW,
84120494Snjl	   &acpi_semaphore_debug, 0, "Enable ACPI semaphore debug messages");
8588420Siwasaki#endif
8688420Siwasaki
8767760SmsmithACPI_STATUS
8867760SmsmithAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle)
8967760Smsmith{
9071876Smsmith#ifndef ACPI_NO_SEMAPHORES
9167760Smsmith    struct acpi_semaphore	*as;
9267760Smsmith
9396926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
9471876Smsmith
9567760Smsmith    if (OutHandle == NULL)
9667760Smsmith	return(AE_BAD_PARAMETER);
9767760Smsmith    if (InitialUnits > MaxUnits)
9871876Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
9967760Smsmith
10067760Smsmith    if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL)
10171876Smsmith	return_ACPI_STATUS(AE_NO_MEMORY);
10267760Smsmith
10388420Siwasaki    bzero(as, sizeof(*as));
104105278Sjhb#if __FreeBSD_version >= 500000
10593818Sjhb    mtx_init(&as->as_mtx, "ACPI semaphore", NULL, MTX_DEF);
106105278Sjhb#endif
10767760Smsmith    as->as_units = InitialUnits;
10867760Smsmith    as->as_maxunits = MaxUnits;
10988420Siwasaki    as->as_pendings = as->as_resetting = as->as_timeouts = 0;
11067760Smsmith
11188420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
11288420Siwasaki	"created semaphore %p max %d, initial %d\n",
11388420Siwasaki	as, InitialUnits, MaxUnits));
11471876Smsmith
11567760Smsmith    *OutHandle = (ACPI_HANDLE)as;
11671876Smsmith    return_ACPI_STATUS(AE_OK);
11771876Smsmith#else
11871876Smsmith    *OutHandle = (ACPI_HANDLE)OutHandle;
11967760Smsmith    return(AE_OK);
12071876Smsmith#endif
12167760Smsmith}
12267760Smsmith
12367760SmsmithACPI_STATUS
12467760SmsmithAcpiOsDeleteSemaphore (ACPI_HANDLE Handle)
12567760Smsmith{
12671876Smsmith#ifndef ACPI_NO_SEMAPHORES
12771359Smsmith    struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
12871876Smsmith
12996926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
13071876Smsmith
13188420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as));
132105278Sjhb#if __FreeBSD_version >= 500000
13371359Smsmith    mtx_destroy(&as->as_mtx);
134105278Sjhb#endif
13567760Smsmith    free(Handle, M_ACPISEM);
13671876Smsmith    return_ACPI_STATUS(AE_OK);
13771876Smsmith#else
13867760Smsmith    return(AE_OK);
13971876Smsmith#endif
14067760Smsmith}
14167760Smsmith
14267760Smsmith/*
14367760Smsmith * This implementation has a bug, in that it has to stall for the entire
14467760Smsmith * timeout before it will return AE_TIME.  A better implementation would
14567760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up.
14667760Smsmith */
14767760SmsmithACPI_STATUS
148107328SiwasakiAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT16 Timeout)
14967760Smsmith{
15071876Smsmith#ifndef ACPI_NO_SEMAPHORES
15167760Smsmith    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
15271876Smsmith    ACPI_STATUS			result;
15371876Smsmith    int				rv, tmo;
15488420Siwasaki    struct timeval		timeouttv, currenttv, timelefttv;
155105278Sjhb    AS_LOCK_DECL;
15667760Smsmith
15796926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
15871876Smsmith
15967760Smsmith    if (as == NULL)
16071876Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
16167760Smsmith
16288420Siwasaki    if (cold)
16388420Siwasaki	return_ACPI_STATUS(AE_OK);
16488420Siwasaki
16588420Siwasaki#if 0
16688420Siwasaki    if (as->as_units < Units && as->as_timeouts > 10) {
16788420Siwasaki	printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as);
168105278Sjhb	AS_LOCK(as);
16988420Siwasaki	as->as_units = as->as_maxunits;
17088420Siwasaki	if (as->as_pendings)
17188420Siwasaki	    as->as_resetting = 1;
17288420Siwasaki	as->as_timeouts = 0;
17388420Siwasaki	wakeup(as);
174105278Sjhb	AS_UNLOCK(as);
17588420Siwasaki	return_ACPI_STATUS(AE_TIME);
17688420Siwasaki    }
17788420Siwasaki
17888420Siwasaki    if (as->as_resetting) {
17988420Siwasaki	return_ACPI_STATUS(AE_TIME);
18088420Siwasaki    }
18188420Siwasaki#endif
18288420Siwasaki
183107328Siwasaki    /* a timeout of ACPI_WAIT_FOREVER means "forever" */
184107328Siwasaki    if (Timeout == ACPI_WAIT_FOREVER) {
18571876Smsmith	tmo = 0;
18688420Siwasaki	timeouttv.tv_sec = ((0xffff/1000) + 1);	/* cf. ACPI spec */
18788420Siwasaki	timeouttv.tv_usec = 0;
18871876Smsmith    } else {
18971876Smsmith	/* compute timeout using microseconds per tick */
19077432Smsmith	tmo = (Timeout * 1000) / (1000000 / hz);
19171876Smsmith	if (tmo <= 0)
19271876Smsmith	    tmo = 1;
19388420Siwasaki	timeouttv.tv_sec  = Timeout / 1000;
19488420Siwasaki	timeouttv.tv_usec = (Timeout % 1000) * 1000;
19571876Smsmith    }
19671876Smsmith
19788420Siwasaki    /* calculate timeout value in timeval */
19888420Siwasaki    getmicrotime(&currenttv);
19988420Siwasaki    timevaladd(&timeouttv, &currenttv);
20088420Siwasaki
201105278Sjhb    AS_LOCK(as);
20288420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
20388420Siwasaki	"get %d units from semaphore %p (has %d), timeout %d\n",
20488420Siwasaki	Units, as, as->as_units, Timeout));
20567760Smsmith    for (;;) {
20699492Siwasaki	if (as->as_maxunits == ACPI_NO_UNIT_LIMIT) {
20780071Smsmith	    result = AE_OK;
20880071Smsmith	    break;
20980071Smsmith	}
21067760Smsmith	if (as->as_units >= Units) {
21167760Smsmith	    as->as_units -= Units;
21267760Smsmith	    result = AE_OK;
21367760Smsmith	    break;
21467760Smsmith	}
21588420Siwasaki
21688420Siwasaki	/* limit number of pending treads */
21788420Siwasaki	if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) {
21867760Smsmith	    result = AE_TIME;
21967760Smsmith	    break;
22067760Smsmith	}
22188420Siwasaki
22288420Siwasaki	/* if timeout values of zero is specified, return immediately */
22388420Siwasaki	if (Timeout == 0) {
22488420Siwasaki	    result = AE_TIME;
22588420Siwasaki	    break;
22688420Siwasaki	}
22788420Siwasaki
228105278Sjhb#if __FreeBSD_version >= 500000
22988420Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
23088420Siwasaki	    "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n",
23188420Siwasaki	    as, &as->as_mtx, PCATCH, tmo));
232105278Sjhb#endif
23388420Siwasaki
23488420Siwasaki	as->as_pendings++;
23588420Siwasaki
23688420Siwasaki	if (acpi_semaphore_debug) {
23788420Siwasaki	    printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n",
23888420Siwasaki		__func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId());
23988420Siwasaki	}
24088420Siwasaki
24188420Siwasaki	rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo);
24288420Siwasaki
24388420Siwasaki	as->as_pendings--;
24488420Siwasaki
24588420Siwasaki#if 0
24688420Siwasaki	if (as->as_resetting) {
24788420Siwasaki	    /* semaphore reset, return immediately */
24888420Siwasaki	    if (as->as_pendings == 0) {
24988420Siwasaki		as->as_resetting = 0;
25088420Siwasaki	    }
25188420Siwasaki	    result = AE_TIME;
25288420Siwasaki	    break;
25388420Siwasaki	}
25488420Siwasaki#endif
25588420Siwasaki
25688420Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv));
25771876Smsmith	if (rv == EWOULDBLOCK) {
25867760Smsmith	    result = AE_TIME;
25967760Smsmith	    break;
26067760Smsmith	}
26188420Siwasaki
26288420Siwasaki	/* check if we already awaited enough */
26388420Siwasaki	timelefttv = timeouttv;
26488420Siwasaki	getmicrotime(&currenttv);
26588420Siwasaki	timevalsub(&timelefttv, &currenttv);
26688420Siwasaki	if (timelefttv.tv_sec < 0) {
26788420Siwasaki	    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as));
26888420Siwasaki	    result = AE_TIME;
26988420Siwasaki	    break;
27088420Siwasaki	}
27188420Siwasaki
27288420Siwasaki	/* adjust timeout for the next sleep */
27388420Siwasaki	tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz);
27488420Siwasaki	if (tmo <= 0)
27588420Siwasaki	    tmo = 1;
27688420Siwasaki
27788420Siwasaki	if (acpi_semaphore_debug) {
27888420Siwasaki	    printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n",
27988420Siwasaki		__func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId());
28088420Siwasaki	}
28167760Smsmith    }
28288420Siwasaki
28388420Siwasaki    if (acpi_semaphore_debug) {
28488420Siwasaki	if (result == AE_TIME && Timeout > 0) {
28588420Siwasaki	    printf("%s: Timeout %d, pending %d, semaphore %p\n",
28688420Siwasaki		__func__, Timeout, as->as_pendings, as);
28788420Siwasaki	}
28888420Siwasaki	if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) {
28988420Siwasaki	    printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n",
29088420Siwasaki		__func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
29188420Siwasaki	}
29288420Siwasaki    }
29388420Siwasaki
29488420Siwasaki    if (result == AE_TIME) {
29588420Siwasaki	as->as_timeouts++;
29688420Siwasaki    } else {
29788420Siwasaki	as->as_timeouts = 0;
29888420Siwasaki    }
29988420Siwasaki
300105278Sjhb    AS_UNLOCK(as);
30167760Smsmith
30271876Smsmith    return_ACPI_STATUS(result);
30371876Smsmith#else
30471876Smsmith    return(AE_OK);
30571876Smsmith#endif
30667760Smsmith}
30767760Smsmith
30867760SmsmithACPI_STATUS
30967760SmsmithAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units)
31067760Smsmith{
31171876Smsmith#ifndef ACPI_NO_SEMAPHORES
31267760Smsmith    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
313105278Sjhb    AS_LOCK_DECL;
31467760Smsmith
31596926Speter    ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
31671876Smsmith
31767760Smsmith    if (as == NULL)
31871876Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
31967760Smsmith
320105278Sjhb    AS_LOCK(as);
32188420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
32288420Siwasaki	"return %d units to semaphore %p (has %d)\n",
32388420Siwasaki	Units, as, as->as_units));
32499492Siwasaki    if (as->as_maxunits != ACPI_NO_UNIT_LIMIT) {
32580071Smsmith	as->as_units += Units;
32680071Smsmith	if (as->as_units > as->as_maxunits)
32780071Smsmith	    as->as_units = as->as_maxunits;
32880071Smsmith    }
32988420Siwasaki
33088420Siwasaki    if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) {
33188420Siwasaki	printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n",
33288420Siwasaki	    __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
33388420Siwasaki    }
33488420Siwasaki
33567760Smsmith    wakeup(as);
336105278Sjhb    AS_UNLOCK(as);
33771876Smsmith    return_ACPI_STATUS(AE_OK);
33871876Smsmith#else
33967760Smsmith    return(AE_OK);
34071876Smsmith#endif
34167760Smsmith}
342117530Snjl
343117530SnjlACPI_STATUS
344117530SnjlAcpiOsCreateLock (ACPI_HANDLE *OutHandle)
345117530Snjl{
346117530Snjl    struct mtx *m;
347117530Snjl
348117530Snjl    if (OutHandle == NULL)
349117530Snjl	return (AE_BAD_PARAMETER);
350117530Snjl    MALLOC(m, struct mtx *, sizeof(*m), M_ACPISEM, M_NOWAIT | M_ZERO);
351117530Snjl    if (m == NULL)
352117530Snjl	return (AE_NO_MEMORY);
353117530Snjl
354117530Snjl    mtx_init(m, "acpica subsystem lock", NULL, MTX_DEF);
355117530Snjl    *OutHandle = (ACPI_HANDLE)m;
356117530Snjl    return (AE_OK);
357117530Snjl}
358117530Snjl
359117530Snjlvoid
360117530SnjlAcpiOsDeleteLock (ACPI_HANDLE Handle)
361117530Snjl{
362117530Snjl    struct mtx *m = (struct mtx *)Handle;
363117530Snjl
364117530Snjl    if (Handle == NULL)
365117530Snjl        return;
366117530Snjl    mtx_destroy(m);
367117530Snjl}
368117530Snjl
369117530Snjl/*
370117530Snjl * The Flags parameter seems to state whether or not caller is an ISR
371117530Snjl * (and thus can't block) but since we have ithreads, we don't worry
372117530Snjl * about potentially blocking.
373117530Snjl */
374117530Snjlvoid
375117530SnjlAcpiOsAcquireLock (ACPI_HANDLE Handle, UINT32 Flags)
376117530Snjl{
377117530Snjl    struct mtx *m = (struct mtx *)Handle;
378117530Snjl
379117530Snjl    if (Handle == NULL)
380117530Snjl        return;
381117530Snjl    mtx_lock(m);
382117530Snjl}
383117530Snjl
384117530Snjlvoid
385117530SnjlAcpiOsReleaseLock (ACPI_HANDLE Handle, UINT32 Flags)
386117530Snjl{
387117530Snjl    struct mtx *m = (struct mtx *)Handle;
388117530Snjl
389117530Snjl    if (Handle == NULL)
390117530Snjl        return;
391117530Snjl    mtx_unlock(m);
392117530Snjl}
393