exmutex.c revision 281075
1/******************************************************************************
2 *
3 * Module Name: exmutex - ASL Mutex Acquire/Release functions
4 *
5 *****************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2015, Intel Corp.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions, and the following disclaimer,
16 *    without modification.
17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18 *    substantially similar to the "NO WARRANTY" disclaimer below
19 *    ("Disclaimer") and any redistribution must be conditioned upon
20 *    including a substantially similar Disclaimer requirement for further
21 *    binary redistribution.
22 * 3. Neither the names of the above-listed copyright holders nor the names
23 *    of any contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * Alternatively, this software may be distributed under the terms of the
27 * GNU General Public License ("GPL") version 2 as published by the Free
28 * Software Foundation.
29 *
30 * NO WARRANTY
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41 * POSSIBILITY OF SUCH DAMAGES.
42 */
43
44#include <contrib/dev/acpica/include/acpi.h>
45#include <contrib/dev/acpica/include/accommon.h>
46#include <contrib/dev/acpica/include/acinterp.h>
47#include <contrib/dev/acpica/include/acevents.h>
48
49#define _COMPONENT          ACPI_EXECUTER
50        ACPI_MODULE_NAME    ("exmutex")
51
52/* Local prototypes */
53
54static void
55AcpiExLinkMutex (
56    ACPI_OPERAND_OBJECT     *ObjDesc,
57    ACPI_THREAD_STATE       *Thread);
58
59
60/*******************************************************************************
61 *
62 * FUNCTION:    AcpiExUnlinkMutex
63 *
64 * PARAMETERS:  ObjDesc             - The mutex to be unlinked
65 *
66 * RETURN:      None
67 *
68 * DESCRIPTION: Remove a mutex from the "AcquiredMutex" list
69 *
70 ******************************************************************************/
71
72void
73AcpiExUnlinkMutex (
74    ACPI_OPERAND_OBJECT     *ObjDesc)
75{
76    ACPI_THREAD_STATE       *Thread = ObjDesc->Mutex.OwnerThread;
77
78
79    if (!Thread)
80    {
81        return;
82    }
83
84    /* Doubly linked list */
85
86    if (ObjDesc->Mutex.Next)
87    {
88        (ObjDesc->Mutex.Next)->Mutex.Prev = ObjDesc->Mutex.Prev;
89    }
90
91    if (ObjDesc->Mutex.Prev)
92    {
93        (ObjDesc->Mutex.Prev)->Mutex.Next = ObjDesc->Mutex.Next;
94
95        /*
96         * Migrate the previous sync level associated with this mutex to
97         * the previous mutex on the list so that it may be preserved.
98         * This handles the case where several mutexes have been acquired
99         * at the same level, but are not released in opposite order.
100         */
101        (ObjDesc->Mutex.Prev)->Mutex.OriginalSyncLevel =
102            ObjDesc->Mutex.OriginalSyncLevel;
103    }
104    else
105    {
106        Thread->AcquiredMutexList = ObjDesc->Mutex.Next;
107    }
108}
109
110
111/*******************************************************************************
112 *
113 * FUNCTION:    AcpiExLinkMutex
114 *
115 * PARAMETERS:  ObjDesc             - The mutex to be linked
116 *              Thread              - Current executing thread object
117 *
118 * RETURN:      None
119 *
120 * DESCRIPTION: Add a mutex to the "AcquiredMutex" list for this walk
121 *
122 ******************************************************************************/
123
124static void
125AcpiExLinkMutex (
126    ACPI_OPERAND_OBJECT     *ObjDesc,
127    ACPI_THREAD_STATE       *Thread)
128{
129    ACPI_OPERAND_OBJECT     *ListHead;
130
131
132    ListHead = Thread->AcquiredMutexList;
133
134    /* This object will be the first object in the list */
135
136    ObjDesc->Mutex.Prev = NULL;
137    ObjDesc->Mutex.Next = ListHead;
138
139    /* Update old first object to point back to this object */
140
141    if (ListHead)
142    {
143        ListHead->Mutex.Prev = ObjDesc;
144    }
145
146    /* Update list head */
147
148    Thread->AcquiredMutexList = ObjDesc;
149}
150
151
152/*******************************************************************************
153 *
154 * FUNCTION:    AcpiExAcquireMutexObject
155 *
156 * PARAMETERS:  Timeout             - Timeout in milliseconds
157 *              ObjDesc             - Mutex object
158 *              ThreadId            - Current thread state
159 *
160 * RETURN:      Status
161 *
162 * DESCRIPTION: Acquire an AML mutex, low-level interface. Provides a common
163 *              path that supports multiple acquires by the same thread.
164 *
165 * MUTEX:       Interpreter must be locked
166 *
167 * NOTE: This interface is called from three places:
168 * 1) From AcpiExAcquireMutex, via an AML Acquire() operator
169 * 2) From AcpiExAcquireGlobalLock when an AML Field access requires the
170 *    global lock
171 * 3) From the external interface, AcpiAcquireGlobalLock
172 *
173 ******************************************************************************/
174
175ACPI_STATUS
176AcpiExAcquireMutexObject (
177    UINT16                  Timeout,
178    ACPI_OPERAND_OBJECT     *ObjDesc,
179    ACPI_THREAD_ID          ThreadId)
180{
181    ACPI_STATUS             Status;
182
183
184    ACPI_FUNCTION_TRACE_PTR (ExAcquireMutexObject, ObjDesc);
185
186
187    if (!ObjDesc)
188    {
189        return_ACPI_STATUS (AE_BAD_PARAMETER);
190    }
191
192    /* Support for multiple acquires by the owning thread */
193
194    if (ObjDesc->Mutex.ThreadId == ThreadId)
195    {
196        /*
197         * The mutex is already owned by this thread, just increment the
198         * acquisition depth
199         */
200        ObjDesc->Mutex.AcquisitionDepth++;
201        return_ACPI_STATUS (AE_OK);
202    }
203
204    /* Acquire the mutex, wait if necessary. Special case for Global Lock */
205
206    if (ObjDesc == AcpiGbl_GlobalLockMutex)
207    {
208        Status = AcpiEvAcquireGlobalLock (Timeout);
209    }
210    else
211    {
212        Status = AcpiExSystemWaitMutex (ObjDesc->Mutex.OsMutex,
213                    Timeout);
214    }
215
216    if (ACPI_FAILURE (Status))
217    {
218        /* Includes failure from a timeout on TimeDesc */
219
220        return_ACPI_STATUS (Status);
221    }
222
223    /* Acquired the mutex: update mutex object */
224
225    ObjDesc->Mutex.ThreadId = ThreadId;
226    ObjDesc->Mutex.AcquisitionDepth = 1;
227    ObjDesc->Mutex.OriginalSyncLevel = 0;
228    ObjDesc->Mutex.OwnerThread = NULL;      /* Used only for AML Acquire() */
229
230    return_ACPI_STATUS (AE_OK);
231}
232
233
234/*******************************************************************************
235 *
236 * FUNCTION:    AcpiExAcquireMutex
237 *
238 * PARAMETERS:  TimeDesc            - Timeout integer
239 *              ObjDesc             - Mutex object
240 *              WalkState           - Current method execution state
241 *
242 * RETURN:      Status
243 *
244 * DESCRIPTION: Acquire an AML mutex
245 *
246 ******************************************************************************/
247
248ACPI_STATUS
249AcpiExAcquireMutex (
250    ACPI_OPERAND_OBJECT     *TimeDesc,
251    ACPI_OPERAND_OBJECT     *ObjDesc,
252    ACPI_WALK_STATE         *WalkState)
253{
254    ACPI_STATUS             Status;
255
256
257    ACPI_FUNCTION_TRACE_PTR (ExAcquireMutex, ObjDesc);
258
259
260    if (!ObjDesc)
261    {
262        return_ACPI_STATUS (AE_BAD_PARAMETER);
263    }
264
265    /* Must have a valid thread state struct */
266
267    if (!WalkState->Thread)
268    {
269        ACPI_ERROR ((AE_INFO,
270            "Cannot acquire Mutex [%4.4s], null thread info",
271            AcpiUtGetNodeName (ObjDesc->Mutex.Node)));
272        return_ACPI_STATUS (AE_AML_INTERNAL);
273    }
274
275    /*
276     * Current sync level must be less than or equal to the sync level of the
277     * mutex. This mechanism provides some deadlock prevention
278     */
279    if (WalkState->Thread->CurrentSyncLevel > ObjDesc->Mutex.SyncLevel)
280    {
281        ACPI_ERROR ((AE_INFO,
282            "Cannot acquire Mutex [%4.4s], current SyncLevel is too large (%u)",
283            AcpiUtGetNodeName (ObjDesc->Mutex.Node),
284            WalkState->Thread->CurrentSyncLevel));
285        return_ACPI_STATUS (AE_AML_MUTEX_ORDER);
286    }
287
288    Status = AcpiExAcquireMutexObject ((UINT16) TimeDesc->Integer.Value,
289                ObjDesc, WalkState->Thread->ThreadId);
290    if (ACPI_SUCCESS (Status) && ObjDesc->Mutex.AcquisitionDepth == 1)
291    {
292        /* Save Thread object, original/current sync levels */
293
294        ObjDesc->Mutex.OwnerThread = WalkState->Thread;
295        ObjDesc->Mutex.OriginalSyncLevel = WalkState->Thread->CurrentSyncLevel;
296        WalkState->Thread->CurrentSyncLevel = ObjDesc->Mutex.SyncLevel;
297
298        /* Link the mutex to the current thread for force-unlock at method exit */
299
300        AcpiExLinkMutex (ObjDesc, WalkState->Thread);
301    }
302
303    return_ACPI_STATUS (Status);
304}
305
306
307/*******************************************************************************
308 *
309 * FUNCTION:    AcpiExReleaseMutexObject
310 *
311 * PARAMETERS:  ObjDesc             - The object descriptor for this op
312 *
313 * RETURN:      Status
314 *
315 * DESCRIPTION: Release a previously acquired Mutex, low level interface.
316 *              Provides a common path that supports multiple releases (after
317 *              previous multiple acquires) by the same thread.
318 *
319 * MUTEX:       Interpreter must be locked
320 *
321 * NOTE: This interface is called from three places:
322 * 1) From AcpiExReleaseMutex, via an AML Acquire() operator
323 * 2) From AcpiExReleaseGlobalLock when an AML Field access requires the
324 *    global lock
325 * 3) From the external interface, AcpiReleaseGlobalLock
326 *
327 ******************************************************************************/
328
329ACPI_STATUS
330AcpiExReleaseMutexObject (
331    ACPI_OPERAND_OBJECT     *ObjDesc)
332{
333    ACPI_STATUS             Status = AE_OK;
334
335
336    ACPI_FUNCTION_TRACE (ExReleaseMutexObject);
337
338
339    if (ObjDesc->Mutex.AcquisitionDepth == 0)
340    {
341        return_ACPI_STATUS (AE_NOT_ACQUIRED);
342    }
343
344    /* Match multiple Acquires with multiple Releases */
345
346    ObjDesc->Mutex.AcquisitionDepth--;
347    if (ObjDesc->Mutex.AcquisitionDepth != 0)
348    {
349        /* Just decrement the depth and return */
350
351        return_ACPI_STATUS (AE_OK);
352    }
353
354    if (ObjDesc->Mutex.OwnerThread)
355    {
356        /* Unlink the mutex from the owner's list */
357
358        AcpiExUnlinkMutex (ObjDesc);
359        ObjDesc->Mutex.OwnerThread = NULL;
360    }
361
362    /* Release the mutex, special case for Global Lock */
363
364    if (ObjDesc == AcpiGbl_GlobalLockMutex)
365    {
366        Status = AcpiEvReleaseGlobalLock ();
367    }
368    else
369    {
370        AcpiOsReleaseMutex (ObjDesc->Mutex.OsMutex);
371    }
372
373    /* Clear mutex info */
374
375    ObjDesc->Mutex.ThreadId = 0;
376    return_ACPI_STATUS (Status);
377}
378
379
380/*******************************************************************************
381 *
382 * FUNCTION:    AcpiExReleaseMutex
383 *
384 * PARAMETERS:  ObjDesc             - The object descriptor for this op
385 *              WalkState           - Current method execution state
386 *
387 * RETURN:      Status
388 *
389 * DESCRIPTION: Release a previously acquired Mutex.
390 *
391 ******************************************************************************/
392
393ACPI_STATUS
394AcpiExReleaseMutex (
395    ACPI_OPERAND_OBJECT     *ObjDesc,
396    ACPI_WALK_STATE         *WalkState)
397{
398    ACPI_STATUS             Status = AE_OK;
399    UINT8                   PreviousSyncLevel;
400    ACPI_THREAD_STATE       *OwnerThread;
401
402
403    ACPI_FUNCTION_TRACE (ExReleaseMutex);
404
405
406    if (!ObjDesc)
407    {
408        return_ACPI_STATUS (AE_BAD_PARAMETER);
409    }
410
411    OwnerThread = ObjDesc->Mutex.OwnerThread;
412
413    /* The mutex must have been previously acquired in order to release it */
414
415    if (!OwnerThread)
416    {
417        ACPI_ERROR ((AE_INFO,
418            "Cannot release Mutex [%4.4s], not acquired",
419            AcpiUtGetNodeName (ObjDesc->Mutex.Node)));
420        return_ACPI_STATUS (AE_AML_MUTEX_NOT_ACQUIRED);
421    }
422
423    /* Must have a valid thread ID */
424
425    if (!WalkState->Thread)
426    {
427        ACPI_ERROR ((AE_INFO,
428            "Cannot release Mutex [%4.4s], null thread info",
429            AcpiUtGetNodeName (ObjDesc->Mutex.Node)));
430        return_ACPI_STATUS (AE_AML_INTERNAL);
431    }
432
433    /*
434     * The Mutex is owned, but this thread must be the owner.
435     * Special case for Global Lock, any thread can release
436     */
437    if ((OwnerThread->ThreadId != WalkState->Thread->ThreadId) &&
438        (ObjDesc != AcpiGbl_GlobalLockMutex))
439    {
440        ACPI_ERROR ((AE_INFO,
441            "Thread %u cannot release Mutex [%4.4s] acquired by thread %u",
442            (UINT32) WalkState->Thread->ThreadId,
443            AcpiUtGetNodeName (ObjDesc->Mutex.Node),
444            (UINT32) OwnerThread->ThreadId));
445        return_ACPI_STATUS (AE_AML_NOT_OWNER);
446    }
447
448    /*
449     * The sync level of the mutex must be equal to the current sync level. In
450     * other words, the current level means that at least one mutex at that
451     * level is currently being held. Attempting to release a mutex of a
452     * different level can only mean that the mutex ordering rule is being
453     * violated. This behavior is clarified in ACPI 4.0 specification.
454     */
455    if (ObjDesc->Mutex.SyncLevel != OwnerThread->CurrentSyncLevel)
456    {
457        ACPI_ERROR ((AE_INFO,
458            "Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %u current %u",
459            AcpiUtGetNodeName (ObjDesc->Mutex.Node),
460            ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel));
461        return_ACPI_STATUS (AE_AML_MUTEX_ORDER);
462    }
463
464    /*
465     * Get the previous SyncLevel from the head of the acquired mutex list.
466     * This handles the case where several mutexes at the same level have been
467     * acquired, but are not released in reverse order.
468     */
469    PreviousSyncLevel =
470        OwnerThread->AcquiredMutexList->Mutex.OriginalSyncLevel;
471
472    Status = AcpiExReleaseMutexObject (ObjDesc);
473    if (ACPI_FAILURE (Status))
474    {
475        return_ACPI_STATUS (Status);
476    }
477
478    if (ObjDesc->Mutex.AcquisitionDepth == 0)
479    {
480        /* Restore the previous SyncLevel */
481
482        OwnerThread->CurrentSyncLevel = PreviousSyncLevel;
483    }
484
485    return_ACPI_STATUS (Status);
486}
487
488
489/*******************************************************************************
490 *
491 * FUNCTION:    AcpiExReleaseAllMutexes
492 *
493 * PARAMETERS:  Thread              - Current executing thread object
494 *
495 * RETURN:      Status
496 *
497 * DESCRIPTION: Release all mutexes held by this thread
498 *
499 * NOTE: This function is called as the thread is exiting the interpreter.
500 * Mutexes are not released when an individual control method is exited, but
501 * only when the parent thread actually exits the interpreter. This allows one
502 * method to acquire a mutex, and a different method to release it, as long as
503 * this is performed underneath a single parent control method.
504 *
505 ******************************************************************************/
506
507void
508AcpiExReleaseAllMutexes (
509    ACPI_THREAD_STATE       *Thread)
510{
511    ACPI_OPERAND_OBJECT     *Next = Thread->AcquiredMutexList;
512    ACPI_OPERAND_OBJECT     *ObjDesc;
513
514
515    ACPI_FUNCTION_NAME (ExReleaseAllMutexes);
516
517
518    /* Traverse the list of owned mutexes, releasing each one */
519
520    while (Next)
521    {
522        ObjDesc = Next;
523        Next = ObjDesc->Mutex.Next;
524
525        ObjDesc->Mutex.Prev = NULL;
526        ObjDesc->Mutex.Next = NULL;
527        ObjDesc->Mutex.AcquisitionDepth = 0;
528
529        ACPI_DEBUG_PRINT ((ACPI_DB_EXEC,
530            "Force-releasing held mutex: %p\n", ObjDesc));
531
532        /* Release the mutex, special case for Global Lock */
533
534        if (ObjDesc == AcpiGbl_GlobalLockMutex)
535        {
536            /* Ignore errors */
537
538            (void) AcpiEvReleaseGlobalLock ();
539        }
540        else
541        {
542            AcpiOsReleaseMutex (ObjDesc->Mutex.OsMutex);
543        }
544
545        /* Mark mutex unowned */
546
547        ObjDesc->Mutex.OwnerThread = NULL;
548        ObjDesc->Mutex.ThreadId = 0;
549
550        /* Update Thread SyncLevel (Last mutex is the important one) */
551
552        Thread->CurrentSyncLevel = ObjDesc->Mutex.OriginalSyncLevel;
553    }
554}
555