OsdSynch.c revision 91128
1131903Smarcel/*-
2131903Smarcel * Copyright (c) 2000 Michael Smith
3131903Smarcel * Copyright (c) 2000 BSDi
4131903Smarcel * All rights reserved.
5131903Smarcel *
6131903Smarcel * Redistribution and use in source and binary forms, with or without
7131903Smarcel * modification, are permitted provided that the following conditions
8131903Smarcel * are met:
9131903Smarcel * 1. Redistributions of source code must retain the above copyright
10131903Smarcel *    notice, this list of conditions and the following disclaimer.
11131903Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12131903Smarcel *    notice, this list of conditions and the following disclaimer in the
13131903Smarcel *    documentation and/or other materials provided with the distribution.
14131903Smarcel *
15131903Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16131903Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17131903Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18131903Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19131903Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20131903Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21131903Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22131903Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23131903Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24131903Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25131903Smarcel * SUCH DAMAGE.
26131903Smarcel *
27131903Smarcel *	$FreeBSD: head/sys/dev/acpica/Osd/OsdSynch.c 91128 2002-02-23 05:31:38Z msmith $
28131903Smarcel */
29131903Smarcel
30131903Smarcel/*
31131903Smarcel * 6.1 : Mutual Exclusion and Synchronisation
32131903Smarcel */
33131903Smarcel
34131903Smarcel#include "acpi.h"
35131903Smarcel
36131903Smarcel#include "opt_acpi.h"
37131903Smarcel#include <sys/kernel.h>
38131903Smarcel#include <sys/lock.h>
39131903Smarcel#include <sys/malloc.h>
40131903Smarcel#include <sys/mutex.h>
41131903Smarcel#include <sys/sysctl.h>
42131903Smarcel
43131903Smarcel#define _COMPONENT	ACPI_OS_SERVICES
44131903SmarcelACPI_MODULE_NAME("SYNCH")
45131903Smarcel
46131903Smarcelstatic MALLOC_DEFINE(M_ACPISEM, "acpisem", "ACPI semaphore");
47131903Smarcel
48131903Smarcel/*
49131903Smarcel * Simple counting semaphore implemented using a mutex. (Subsequently used
50131903Smarcel * in the OSI code to implement a mutex.  Go figure.)
51131903Smarcel */
52131903Smarcelstruct acpi_semaphore {
53131903Smarcel    struct mtx	as_mtx;
54131903Smarcel    UINT32	as_units;
55131903Smarcel    UINT32	as_maxunits;
56131903Smarcel    UINT32	as_pendings;
57131903Smarcel    UINT32	as_resetting;
58131903Smarcel    UINT32	as_timeouts;
59131903Smarcel};
60131903Smarcel
61131903Smarcel#ifndef ACPI_NO_SEMAPHORES
62131903Smarcel#ifndef ACPI_SEMAPHORES_MAX_PENDING
63131903Smarcel#define ACPI_SEMAPHORES_MAX_PENDING	4
64131903Smarcel#endif
65131903Smarcelstatic int	acpi_semaphore_debug = 0;
66131903SmarcelTUNABLE_INT("debug.acpi_semaphore_debug", &acpi_semaphore_debug);
67131903SmarcelSYSCTL_INT(_debug, OID_AUTO, acpi_semaphore_debug, CTLFLAG_RW,
68131903Smarcel    &acpi_semaphore_debug, 0, "");
69133737Srwatson#endif
70133737Srwatson
71133737SrwatsonACPI_STATUS
72133737SrwatsonAcpiOsCreateSemaphore(UINT32 MaxUnits, UINT32 InitialUnits, ACPI_HANDLE *OutHandle)
73133737Srwatson{
74133737Srwatson#ifndef ACPI_NO_SEMAPHORES
75133737Srwatson    struct acpi_semaphore	*as;
76133737Srwatson
77133737Srwatson    ACPI_FUNCTION_TRACE(__func__);
78133737Srwatson
79134162Srwatson    if (OutHandle == NULL)
80133737Srwatson	return(AE_BAD_PARAMETER);
81133737Srwatson    if (InitialUnits > MaxUnits)
82131903Smarcel	return_ACPI_STATUS(AE_BAD_PARAMETER);
83131903Smarcel
84131903Smarcel    if ((as = malloc(sizeof(*as), M_ACPISEM, M_NOWAIT)) == NULL)
85131903Smarcel	return_ACPI_STATUS(AE_NO_MEMORY);
86131903Smarcel
87131903Smarcel    bzero(as, sizeof(*as));
88131903Smarcel    mtx_init(&as->as_mtx, "ACPI semaphore", MTX_DEF);
89131903Smarcel    as->as_units = InitialUnits;
90131903Smarcel    as->as_maxunits = MaxUnits;
91131903Smarcel    as->as_pendings = as->as_resetting = as->as_timeouts = 0;
92131903Smarcel
93131903Smarcel    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
94131903Smarcel	"created semaphore %p max %d, initial %d\n",
95131903Smarcel	as, InitialUnits, MaxUnits));
96131903Smarcel
97131903Smarcel    *OutHandle = (ACPI_HANDLE)as;
98131903Smarcel    return_ACPI_STATUS(AE_OK);
99131903Smarcel#else
100131903Smarcel    *OutHandle = (ACPI_HANDLE)OutHandle;
101131903Smarcel    return(AE_OK);
102131903Smarcel#endif
103131903Smarcel}
104131903Smarcel
105131903SmarcelACPI_STATUS
106131903SmarcelAcpiOsDeleteSemaphore (ACPI_HANDLE Handle)
107131903Smarcel{
108131903Smarcel#ifndef ACPI_NO_SEMAPHORES
109131903Smarcel    struct acpi_semaphore *as = (struct acpi_semaphore *)Handle;
110131903Smarcel
111131903Smarcel    ACPI_FUNCTION_TRACE(__func__);
112131903Smarcel
113131903Smarcel    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "destroyed semaphore %p\n", as));
114131903Smarcel    mtx_destroy(&as->as_mtx);
115131903Smarcel    free(Handle, M_ACPISEM);
116131903Smarcel    return_ACPI_STATUS(AE_OK);
117131903Smarcel#else
118131903Smarcel    return(AE_OK);
119131982Smarcel#endif
120131982Smarcel}
121131982Smarcel
122131982Smarcel/*
123131982Smarcel * This implementation has a bug, in that it has to stall for the entire
124131903Smarcel * timeout before it will return AE_TIME.  A better implementation would
125131903Smarcel * use getmicrotime() to correctly adjust the timeout after being woken up.
126131903Smarcel */
127131903SmarcelACPI_STATUS
128131903SmarcelAcpiOsWaitSemaphore(ACPI_HANDLE Handle, UINT32 Units, UINT32 Timeout)
129132001Smarcel{
130131903Smarcel#ifndef ACPI_NO_SEMAPHORES
131131903Smarcel    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
132131903Smarcel    ACPI_STATUS			result;
133131903Smarcel    int				rv, tmo;
134131903Smarcel    struct timeval		timeouttv, currenttv, timelefttv;
135131903Smarcel
136131903Smarcel    ACPI_FUNCTION_TRACE(__func__);
137131903Smarcel
138131903Smarcel    if (as == NULL)
139131903Smarcel	return_ACPI_STATUS(AE_BAD_PARAMETER);
140131903Smarcel
141131903Smarcel    if (cold)
142131903Smarcel	return_ACPI_STATUS(AE_OK);
143131903Smarcel
144131903Smarcel#if 0
145131903Smarcel    if (as->as_units < Units && as->as_timeouts > 10) {
146131903Smarcel	printf("%s: semaphore %p too many timeouts, resetting\n", __func__, as);
147131903Smarcel	mtx_lock(&as->as_mtx);
148131903Smarcel	as->as_units = as->as_maxunits;
149131903Smarcel	if (as->as_pendings)
150131903Smarcel	    as->as_resetting = 1;
151131903Smarcel	as->as_timeouts = 0;
152131903Smarcel	wakeup(as);
153131903Smarcel	mtx_unlock(&as->as_mtx);
154131903Smarcel	return_ACPI_STATUS(AE_TIME);
155131903Smarcel    }
156131903Smarcel
157131903Smarcel    if (as->as_resetting) {
158131903Smarcel	return_ACPI_STATUS(AE_TIME);
159131903Smarcel    }
160131903Smarcel#endif
161131903Smarcel
162131903Smarcel    /* a timeout of WAIT_FOREVER means "forever" */
163131903Smarcel    if (Timeout == WAIT_FOREVER) {
164131903Smarcel	tmo = 0;
165131903Smarcel	timeouttv.tv_sec = ((0xffff/1000) + 1);	/* cf. ACPI spec */
166131903Smarcel	timeouttv.tv_usec = 0;
167131903Smarcel    } else {
168131903Smarcel	/* compute timeout using microseconds per tick */
169131903Smarcel	tmo = (Timeout * 1000) / (1000000 / hz);
170131903Smarcel	if (tmo <= 0)
171131903Smarcel	    tmo = 1;
172131903Smarcel	timeouttv.tv_sec  = Timeout / 1000;
173131903Smarcel	timeouttv.tv_usec = (Timeout % 1000) * 1000;
174131903Smarcel    }
175131903Smarcel
176131903Smarcel    /* calculate timeout value in timeval */
177131903Smarcel    getmicrotime(&currenttv);
178131903Smarcel    timevaladd(&timeouttv, &currenttv);
179131903Smarcel
180131903Smarcel    mtx_lock(&as->as_mtx);
181131903Smarcel    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
182131903Smarcel	"get %d units from semaphore %p (has %d), timeout %d\n",
183131903Smarcel	Units, as, as->as_units, Timeout));
184131903Smarcel    for (;;) {
185131903Smarcel	if (as->as_units == ACPI_NO_UNIT_LIMIT) {
186131903Smarcel	    result = AE_OK;
187131903Smarcel	    break;
188131903Smarcel	}
189131903Smarcel	if (as->as_units >= Units) {
190131903Smarcel	    as->as_units -= Units;
191131903Smarcel	    result = AE_OK;
192131903Smarcel	    break;
193131903Smarcel	}
194131903Smarcel
195131903Smarcel	/* limit number of pending treads */
196131903Smarcel	if (as->as_pendings >= ACPI_SEMAPHORES_MAX_PENDING) {
197131903Smarcel	    result = AE_TIME;
198131903Smarcel	    break;
199131903Smarcel	}
200131903Smarcel
201131903Smarcel	/* if timeout values of zero is specified, return immediately */
202131903Smarcel	if (Timeout == 0) {
203131903Smarcel	    result = AE_TIME;
204131903Smarcel	    break;
205131903Smarcel	}
206131903Smarcel
207132001Smarcel	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
208132001Smarcel	    "semaphore blocked, calling msleep(%p, %p, %d, \"acsem\", %d)\n",
209132001Smarcel	    as, &as->as_mtx, PCATCH, tmo));
210132001Smarcel
211132001Smarcel	as->as_pendings++;
212132001Smarcel
213132001Smarcel	if (acpi_semaphore_debug) {
214132001Smarcel	    printf("%s: Sleep %d, pending %d, semaphore %p, thread %d\n",
215132001Smarcel		__func__, Timeout, as->as_pendings, as, AcpiOsGetThreadId());
216132001Smarcel	}
217132001Smarcel
218132001Smarcel	rv = msleep(as, &as->as_mtx, PCATCH, "acsem", tmo);
219132001Smarcel
220132001Smarcel	as->as_pendings--;
221132001Smarcel
222132001Smarcel#if 0
223132001Smarcel	if (as->as_resetting) {
224132001Smarcel	    /* semaphore reset, return immediately */
225132001Smarcel	    if (as->as_pendings == 0) {
226131903Smarcel		as->as_resetting = 0;
227131903Smarcel	    }
228131903Smarcel	    result = AE_TIME;
229131903Smarcel	    break;
230131903Smarcel	}
231131903Smarcel#endif
232131903Smarcel
233131903Smarcel	ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "msleep(%d) returned %d\n", tmo, rv));
234131903Smarcel	if (rv == EWOULDBLOCK) {
235131903Smarcel	    result = AE_TIME;
236131903Smarcel	    break;
237131903Smarcel	}
238131903Smarcel
239131903Smarcel	/* check if we already awaited enough */
240131903Smarcel	timelefttv = timeouttv;
241131903Smarcel	getmicrotime(&currenttv);
242131903Smarcel	timevalsub(&timelefttv, &currenttv);
243131903Smarcel	if (timelefttv.tv_sec < 0) {
244131903Smarcel	    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "await semaphore %p timeout\n", as));
245131903Smarcel	    result = AE_TIME;
246131903Smarcel	    break;
247131903Smarcel	}
248131903Smarcel
249131903Smarcel	/* adjust timeout for the next sleep */
250131903Smarcel	tmo = (timelefttv.tv_sec * 1000000 + timelefttv.tv_usec) / (1000000 / hz);
251131903Smarcel	if (tmo <= 0)
252131903Smarcel	    tmo = 1;
253131903Smarcel
254131903Smarcel	if (acpi_semaphore_debug) {
255131903Smarcel	    printf("%s: Wakeup timeleft(%lu, %lu), tmo %u, semaphore %p, thread %d\n",
256131903Smarcel		__func__, timelefttv.tv_sec, timelefttv.tv_usec, tmo, as, AcpiOsGetThreadId());
257131903Smarcel	}
258131903Smarcel    }
259131903Smarcel
260131903Smarcel    if (acpi_semaphore_debug) {
261131903Smarcel	if (result == AE_TIME && Timeout > 0) {
262131903Smarcel	    printf("%s: Timeout %d, pending %d, semaphore %p\n",
263131903Smarcel		__func__, Timeout, as->as_pendings, as);
264131903Smarcel	}
265131903Smarcel	if (result == AE_OK && (as->as_timeouts > 0 || as->as_pendings > 0)) {
266131903Smarcel	    printf("%s: Acquire %d, units %d, pending %d, semaphore %p, thread %d\n",
267131903Smarcel		__func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
268131903Smarcel	}
269131903Smarcel    }
270131903Smarcel
271131903Smarcel    if (result == AE_TIME) {
272131903Smarcel	as->as_timeouts++;
273131903Smarcel    } else {
274131903Smarcel	as->as_timeouts = 0;
275131903Smarcel    }
276131903Smarcel
277131903Smarcel    mtx_unlock(&as->as_mtx);
278131903Smarcel
279131903Smarcel    return_ACPI_STATUS(result);
280131903Smarcel#else
281131903Smarcel    return(AE_OK);
282131903Smarcel#endif
283131903Smarcel}
284131903Smarcel
285131903SmarcelACPI_STATUS
286131903SmarcelAcpiOsSignalSemaphore(ACPI_HANDLE Handle, UINT32 Units)
287131903Smarcel{
288131903Smarcel#ifndef ACPI_NO_SEMAPHORES
289131903Smarcel    struct acpi_semaphore	*as = (struct acpi_semaphore *)Handle;
290131903Smarcel
291131903Smarcel    ACPI_FUNCTION_TRACE(__func__);
292131903Smarcel
293131903Smarcel    if (as == NULL)
294131903Smarcel	return_ACPI_STATUS(AE_BAD_PARAMETER);
295131903Smarcel
296131903Smarcel    mtx_lock(&as->as_mtx);
297131903Smarcel    ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
298131903Smarcel	"return %d units to semaphore %p (has %d)\n",
299131903Smarcel	Units, as, as->as_units));
300131903Smarcel    if (as->as_units != ACPI_NO_UNIT_LIMIT) {
301131903Smarcel	as->as_units += Units;
302131903Smarcel	if (as->as_units > as->as_maxunits)
303131903Smarcel	    as->as_units = as->as_maxunits;
304131903Smarcel    }
305131903Smarcel
306131903Smarcel    if (acpi_semaphore_debug && (as->as_timeouts > 0 || as->as_pendings > 0)) {
307131903Smarcel	printf("%s: Release %d, units %d, pending %d, semaphore %p, thread %d\n",
308131903Smarcel	    __func__, Units, as->as_units, as->as_pendings, as, AcpiOsGetThreadId());
309131903Smarcel    }
310131903Smarcel
311131903Smarcel    wakeup(as);
312131903Smarcel    mtx_unlock(&as->as_mtx);
313131903Smarcel    return_ACPI_STATUS(AE_OK);
314131903Smarcel#else
315131903Smarcel    return(AE_OK);
316131903Smarcel#endif
317131903Smarcel}
318131903Smarcel