OsdSynch.c revision 88420
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 88420 2001-12-22 16:05:41Z iwasaki $
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>
3874914Sjhb#include <sys/lock.h>
3967760Smsmith#include <sys/malloc.h>
4067760Smsmith#include <sys/mutex.h>
4188420Siwasaki#include <sys/sysctl.h>
4267760Smsmith
4377432Smsmith#define _COMPONENT	ACPI_OS_SERVICES
4471876SmsmithMODULE_NAME("SYNCH")
4571876Smsmith
4669776Smsmithstatic MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
4767760Smsmith
4867760Smsmith/*
4967760Smsmith * Simple counting semaphore implemented using a mutex. (Subsequently used
5067760Smsmith * in the OSI code to implement a mutex.  Go figure.)
5167760Smsmith */
5267760Smsmithstruct acpi_semaphore {
5367760Smsmith    struct mtx	as_mtx;
5467760Smsmith    UINT32	as_units;
5567760Smsmith    UINT32	as_maxunits;
5688420Siwasaki    UINT32	as_pendings;
5788420Siwasaki    UINT32	as_resetting;
5888420Siwasaki    UINT32	as_timeouts;
5967760Smsmith};
6067760Smsmith
6188420Siwasaki#ifndef ACPI_NO_SEMAPHORES
6288420Siwasaki#ifndef ACPI_SEMAPHORES_MAX_PENDING
6388420Siwasaki#define ACPI_SEMAPHORES_MAX_PENDING	4
6488420Siwasaki#endif
6588420Siwasakistatic int	acpi_semaphore_debug = 0;
6688420SiwasakiTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug);
6788420SiwasakiSYSCTL_INT(_debug, OID_AUTO, acpi_semaphore_debug, CTLFLAG_RW,
6888420Siwasaki    &acpi_semaphore_debug, 0, "");
6988420Siwasaki#endif
7088420Siwasaki
7167760SmsmithACPI_STATUS
7267760SmsmithAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle)
7367760Smsmith{
7471876Smsmith#ifndef ACPI_NO_SEMAPHORES
7567760Smsmith    struct acpi_semaphore	*as;
7667760Smsmith
7777432Smsmith    FUNCTION_TRACE(__func__);
7871876Smsmith
7967760Smsmith    if (OutHandle == NULL)
8067760Smsmith	return(AE_BAD_PARAMETER);
8167760Smsmith    if (InitialUnits > MaxUnits)
8271876Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
8367760Smsmith
8467760Smsmith    if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL)
8571876Smsmith	return_ACPI_STATUS(AE_NO_MEMORY);
8667760Smsmith
8788420Siwasaki    bzero(as, sizeof(*as));
8867760Smsmith    mtx_init(&as->as_mtx, "ACPI semaphore", MTX_DEF);
8967760Smsmith    as->as_units = InitialUnits;
9067760Smsmith    as->as_maxunits = MaxUnits;
9188420Siwasaki    as->as_pendings = as->as_resetting = as->as_timeouts = 0;
9267760Smsmith
9388420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
9488420Siwasaki	"created semaphore %p max %d, initial %d\n",
9588420Siwasaki	as, InitialUnits, MaxUnits));
9671876Smsmith
9767760Smsmith    *OutHandle = (ACPI_HANDLE)as;
9871876Smsmith    return_ACPI_STATUS(AE_OK);
9971876Smsmith#else
10071876Smsmith    *OutHandle = (ACPI_HANDLE)OutHandle;
10167760Smsmith    return(AE_OK);
10271876Smsmith#endif
10367760Smsmith}
10467760Smsmith
10567760SmsmithACPI_STATUS
10667760SmsmithAcpiOsDeleteSemaphore (ACPI_HANDLE Handle)
10767760Smsmith{
10871876Smsmith#ifndef ACPI_NO_SEMAPHORES
10971359Smsmith    struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
11071876Smsmith
11177432Smsmith    FUNCTION_TRACE(__func__);
11271876Smsmith
11388420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as));
11471359Smsmith    mtx_destroy(&as->as_mtx);
11567760Smsmith    free(Handle, M_ACPISEM);
11671876Smsmith    return_ACPI_STATUS(AE_OK);
11771876Smsmith#else
11867760Smsmith    return(AE_OK);
11971876Smsmith#endif
12067760Smsmith}
12167760Smsmith
12267760Smsmith/*
12367760Smsmith * This implementation has a bug, in that it has to stall for the entire
12467760Smsmith * timeout before it will return AE_TIME.  A better implementation would
12567760Smsmith * use getmicrotime() to correctly adjust the timeout after being woken up.
12667760Smsmith */
12767760SmsmithACPI_STATUS
12867760SmsmithAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT32 Timeout)
12967760Smsmith{
13071876Smsmith#ifndef ACPI_NO_SEMAPHORES
13167760Smsmith    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
13271876Smsmith    ACPI_STATUS			result;
13371876Smsmith    int				rv, tmo;
13488420Siwasaki    struct timeval		timeouttv, currenttv, timelefttv;
13567760Smsmith
13677432Smsmith    FUNCTION_TRACE(__func__);
13771876Smsmith
13867760Smsmith    if (as == NULL)
13971876Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
14067760Smsmith
14188420Siwasaki    if (cold)
14288420Siwasaki	return_ACPI_STATUS(AE_OK);
14388420Siwasaki
14488420Siwasaki#if 0
14588420Siwasaki    if (as->as_units < Units && as->as_timeouts > 10) {
14688420Siwasaki	printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as);
14788420Siwasaki	mtx_lock(&as->as_mtx);
14888420Siwasaki	as->as_units = as->as_maxunits;
14988420Siwasaki	if (as->as_pendings)
15088420Siwasaki	    as->as_resetting = 1;
15188420Siwasaki	as->as_timeouts = 0;
15288420Siwasaki	wakeup(as);
15388420Siwasaki	mtx_unlock(&as->as_mtx);
15488420Siwasaki	return_ACPI_STATUS(AE_TIME);
15588420Siwasaki    }
15688420Siwasaki
15788420Siwasaki    if (as->as_resetting) {
15888420Siwasaki	return_ACPI_STATUS(AE_TIME);
15988420Siwasaki    }
16088420Siwasaki#endif
16188420Siwasaki
16288420Siwasaki    /* a timeout of WAIT_FOREVER means "forever" */
16388420Siwasaki    if (Timeout == WAIT_FOREVER) {
16471876Smsmith	tmo = 0;
16588420Siwasaki	timeouttv.tv_sec = ((0xffff/1000) + 1);	/* cf. ACPI spec */
16688420Siwasaki	timeouttv.tv_usec = 0;
16771876Smsmith    } else {
16871876Smsmith	/* compute timeout using microseconds per tick */
16977432Smsmith	tmo = (Timeout * 1000) / (1000000 / hz);
17071876Smsmith	if (tmo <= 0)
17171876Smsmith	    tmo = 1;
17288420Siwasaki	timeouttv.tv_sec  = Timeout / 1000;
17388420Siwasaki	timeouttv.tv_usec = (Timeout % 1000) * 1000;
17471876Smsmith    }
17571876Smsmith
17688420Siwasaki    /* calculate timeout value in timeval */
17788420Siwasaki    getmicrotime(&currenttv);
17888420Siwasaki    timevaladd(&timeouttv, &currenttv);
17988420Siwasaki
18072200Sbmilekic    mtx_lock(&as->as_mtx);
18188420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
18288420Siwasaki	"get %d units from semaphore %p (has %d), timeout %d\n",
18388420Siwasaki	Units, as, as->as_units, Timeout));
18467760Smsmith    for (;;) {
18588420Siwasaki	if (as->as_units == ACPI_NO_UNIT_LIMIT) {
18680071Smsmith	    result = AE_OK;
18780071Smsmith	    break;
18880071Smsmith	}
18967760Smsmith	if (as->as_units >= Units) {
19067760Smsmith	    as->as_units -= Units;
19167760Smsmith	    result = AE_OK;
19267760Smsmith	    break;
19367760Smsmith	}
19488420Siwasaki
19588420Siwasaki	/* limit number of pending treads */
19688420Siwasaki	if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) {
19767760Smsmith	    result = AE_TIME;
19867760Smsmith	    break;
19967760Smsmith	}
20088420Siwasaki
20188420Siwasaki	/* if timeout values of zero is specified, return immediately */
20288420Siwasaki	if (Timeout == 0) {
20388420Siwasaki	    result = AE_TIME;
20488420Siwasaki	    break;
20588420Siwasaki	}
20688420Siwasaki
20788420Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
20888420Siwasaki	    "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n",
20988420Siwasaki	    as, &as->as_mtx, PCATCH, tmo));
21088420Siwasaki
21188420Siwasaki	as->as_pendings++;
21288420Siwasaki
21388420Siwasaki	if (acpi_semaphore_debug) {
21488420Siwasaki	    printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n",
21588420Siwasaki		__func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId());
21688420Siwasaki	}
21788420Siwasaki
21888420Siwasaki	rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo);
21988420Siwasaki
22088420Siwasaki	as->as_pendings--;
22188420Siwasaki
22288420Siwasaki#if 0
22388420Siwasaki	if (as->as_resetting) {
22488420Siwasaki	    /* semaphore reset, return immediately */
22588420Siwasaki	    if (as->as_pendings == 0) {
22688420Siwasaki		as->as_resetting = 0;
22788420Siwasaki	    }
22888420Siwasaki	    result = AE_TIME;
22988420Siwasaki	    break;
23088420Siwasaki	}
23188420Siwasaki#endif
23288420Siwasaki
23388420Siwasaki	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv));
23471876Smsmith	if (rv == EWOULDBLOCK) {
23567760Smsmith	    result = AE_TIME;
23667760Smsmith	    break;
23767760Smsmith	}
23888420Siwasaki
23988420Siwasaki	/* check if we already awaited enough */
24088420Siwasaki	timelefttv = timeouttv;
24188420Siwasaki	getmicrotime(&currenttv);
24288420Siwasaki	timevalsub(&timelefttv, &currenttv);
24388420Siwasaki	if (timelefttv.tv_sec < 0) {
24488420Siwasaki	    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as));
24588420Siwasaki	    result = AE_TIME;
24688420Siwasaki	    break;
24788420Siwasaki	}
24888420Siwasaki
24988420Siwasaki	/* adjust timeout for the next sleep */
25088420Siwasaki	tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz);
25188420Siwasaki	if (tmo <= 0)
25288420Siwasaki	    tmo = 1;
25388420Siwasaki
25488420Siwasaki	if (acpi_semaphore_debug) {
25588420Siwasaki	    printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n",
25688420Siwasaki		__func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId());
25788420Siwasaki	}
25867760Smsmith    }
25988420Siwasaki
26088420Siwasaki    if (acpi_semaphore_debug) {
26188420Siwasaki	if (result == AE_TIME && Timeout > 0) {
26288420Siwasaki	    printf("%s: Timeout %d, pending %d, semaphore %p\n",
26388420Siwasaki		__func__, Timeout, as->as_pendings, as);
26488420Siwasaki	}
26588420Siwasaki	if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) {
26688420Siwasaki	    printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n",
26788420Siwasaki		__func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
26888420Siwasaki	}
26988420Siwasaki    }
27088420Siwasaki
27188420Siwasaki    if (result == AE_TIME) {
27288420Siwasaki	as->as_timeouts++;
27388420Siwasaki    } else {
27488420Siwasaki	as->as_timeouts = 0;
27588420Siwasaki    }
27688420Siwasaki
27772200Sbmilekic    mtx_unlock(&as->as_mtx);
27867760Smsmith
27971876Smsmith    return_ACPI_STATUS(result);
28071876Smsmith#else
28171876Smsmith    return(AE_OK);
28271876Smsmith#endif
28367760Smsmith}
28467760Smsmith
28567760SmsmithACPI_STATUS
28667760SmsmithAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units)
28767760Smsmith{
28871876Smsmith#ifndef ACPI_NO_SEMAPHORES
28967760Smsmith    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
29067760Smsmith
29177432Smsmith    FUNCTION_TRACE(__func__);
29271876Smsmith
29367760Smsmith    if (as == NULL)
29471876Smsmith	return_ACPI_STATUS(AE_BAD_PARAMETER);
29567760Smsmith
29672200Sbmilekic    mtx_lock(&as->as_mtx);
29788420Siwasaki    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
29888420Siwasaki	"return %d units to semaphore %p (has %d)\n",
29988420Siwasaki	Units, as, as->as_units));
30080071Smsmith    if (as->as_units != ACPI_NO_UNIT_LIMIT) {
30180071Smsmith	as->as_units += Units;
30280071Smsmith	if (as->as_units > as->as_maxunits)
30380071Smsmith	    as->as_units = as->as_maxunits;
30480071Smsmith    }
30588420Siwasaki
30688420Siwasaki    if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) {
30788420Siwasaki	printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n",
30888420Siwasaki	    __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
30988420Siwasaki    }
31088420Siwasaki
31167760Smsmith    wakeup(as);
31272200Sbmilekic    mtx_unlock(&as->as_mtx);
31371876Smsmith    return_ACPI_STATUS(AE_OK);
31471876Smsmith#else
31567760Smsmith    return(AE_OK);
31671876Smsmith#endif
31767760Smsmith}
318