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(&currenttv);
183181803Sbz    timevaladd(&timeouttv, &currenttv);
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(&currenttv);
247122922Sandre	timevalsub(&timelefttv, &currenttv);
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