diff options
Diffstat (limited to 'usr/src/common/acpica/executer/exmutex.c')
-rw-r--r-- | usr/src/common/acpica/executer/exmutex.c | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/usr/src/common/acpica/executer/exmutex.c b/usr/src/common/acpica/executer/exmutex.c new file mode 100644 index 0000000000..30e10af38c --- /dev/null +++ b/usr/src/common/acpica/executer/exmutex.c @@ -0,0 +1,585 @@ +/****************************************************************************** + * + * Module Name: exmutex - ASL Mutex Acquire/Release functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2016, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include "acpi.h" +#include "accommon.h" +#include "acinterp.h" +#include "acevents.h" + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exmutex") + +/* Local prototypes */ + +static void +AcpiExLinkMutex ( + ACPI_OPERAND_OBJECT *ObjDesc, + ACPI_THREAD_STATE *Thread); + + +/******************************************************************************* + * + * FUNCTION: AcpiExUnlinkMutex + * + * PARAMETERS: ObjDesc - The mutex to be unlinked + * + * RETURN: None + * + * DESCRIPTION: Remove a mutex from the "AcquiredMutex" list + * + ******************************************************************************/ + +void +AcpiExUnlinkMutex ( + ACPI_OPERAND_OBJECT *ObjDesc) +{ + ACPI_THREAD_STATE *Thread = ObjDesc->Mutex.OwnerThread; + + + if (!Thread) + { + return; + } + + /* Doubly linked list */ + + if (ObjDesc->Mutex.Next) + { + (ObjDesc->Mutex.Next)->Mutex.Prev = ObjDesc->Mutex.Prev; + } + + if (ObjDesc->Mutex.Prev) + { + (ObjDesc->Mutex.Prev)->Mutex.Next = ObjDesc->Mutex.Next; + + /* + * Migrate the previous sync level associated with this mutex to + * the previous mutex on the list so that it may be preserved. + * This handles the case where several mutexes have been acquired + * at the same level, but are not released in opposite order. + */ + (ObjDesc->Mutex.Prev)->Mutex.OriginalSyncLevel = + ObjDesc->Mutex.OriginalSyncLevel; + } + else + { + Thread->AcquiredMutexList = ObjDesc->Mutex.Next; + } +} + + +/******************************************************************************* + * + * FUNCTION: AcpiExLinkMutex + * + * PARAMETERS: ObjDesc - The mutex to be linked + * Thread - Current executing thread object + * + * RETURN: None + * + * DESCRIPTION: Add a mutex to the "AcquiredMutex" list for this walk + * + ******************************************************************************/ + +static void +AcpiExLinkMutex ( + ACPI_OPERAND_OBJECT *ObjDesc, + ACPI_THREAD_STATE *Thread) +{ + ACPI_OPERAND_OBJECT *ListHead; + + + ListHead = Thread->AcquiredMutexList; + + /* This object will be the first object in the list */ + + ObjDesc->Mutex.Prev = NULL; + ObjDesc->Mutex.Next = ListHead; + + /* Update old first object to point back to this object */ + + if (ListHead) + { + ListHead->Mutex.Prev = ObjDesc; + } + + /* Update list head */ + + Thread->AcquiredMutexList = ObjDesc; +} + + +/******************************************************************************* + * + * FUNCTION: AcpiExAcquireMutexObject + * + * PARAMETERS: Timeout - Timeout in milliseconds + * ObjDesc - Mutex object + * ThreadId - Current thread state + * + * RETURN: Status + * + * DESCRIPTION: Acquire an AML mutex, low-level interface. Provides a common + * path that supports multiple acquires by the same thread. + * + * MUTEX: Interpreter must be locked + * + * NOTE: This interface is called from three places: + * 1) From AcpiExAcquireMutex, via an AML Acquire() operator + * 2) From AcpiExAcquireGlobalLock when an AML Field access requires the + * global lock + * 3) From the external interface, AcpiAcquireGlobalLock + * + ******************************************************************************/ + +ACPI_STATUS +AcpiExAcquireMutexObject ( + UINT16 Timeout, + ACPI_OPERAND_OBJECT *ObjDesc, + ACPI_THREAD_ID ThreadId) +{ + ACPI_STATUS Status; + + + ACPI_FUNCTION_TRACE_PTR (ExAcquireMutexObject, ObjDesc); + + + if (!ObjDesc) + { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Support for multiple acquires by the owning thread */ + + if (ObjDesc->Mutex.ThreadId == ThreadId) + { + /* + * The mutex is already owned by this thread, just increment the + * acquisition depth + */ + ObjDesc->Mutex.AcquisitionDepth++; + return_ACPI_STATUS (AE_OK); + } + + /* Acquire the mutex, wait if necessary. Special case for Global Lock */ + + if (ObjDesc == AcpiGbl_GlobalLockMutex) + { + Status = AcpiEvAcquireGlobalLock (Timeout); + } + else + { + Status = AcpiExSystemWaitMutex (ObjDesc->Mutex.OsMutex, Timeout); + } + + if (ACPI_FAILURE (Status)) + { + /* Includes failure from a timeout on TimeDesc */ + + return_ACPI_STATUS (Status); + } + + /* Acquired the mutex: update mutex object */ + + ObjDesc->Mutex.ThreadId = ThreadId; + ObjDesc->Mutex.AcquisitionDepth = 1; + ObjDesc->Mutex.OriginalSyncLevel = 0; + ObjDesc->Mutex.OwnerThread = NULL; /* Used only for AML Acquire() */ + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: AcpiExAcquireMutex + * + * PARAMETERS: TimeDesc - Timeout integer + * ObjDesc - Mutex object + * WalkState - Current method execution state + * + * RETURN: Status + * + * DESCRIPTION: Acquire an AML mutex + * + ******************************************************************************/ + +ACPI_STATUS +AcpiExAcquireMutex ( + ACPI_OPERAND_OBJECT *TimeDesc, + ACPI_OPERAND_OBJECT *ObjDesc, + ACPI_WALK_STATE *WalkState) +{ + ACPI_STATUS Status; + + + ACPI_FUNCTION_TRACE_PTR (ExAcquireMutex, ObjDesc); + + + if (!ObjDesc) + { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Must have a valid thread state struct */ + + if (!WalkState->Thread) + { + ACPI_ERROR ((AE_INFO, + "Cannot acquire Mutex [%4.4s], null thread info", + AcpiUtGetNodeName (ObjDesc->Mutex.Node))); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* + * Current sync level must be less than or equal to the sync level + * of the mutex. This mechanism provides some deadlock prevention. + */ + if (WalkState->Thread->CurrentSyncLevel > ObjDesc->Mutex.SyncLevel) + { + ACPI_ERROR ((AE_INFO, + "Cannot acquire Mutex [%4.4s], " + "current SyncLevel is too large (%u)", + AcpiUtGetNodeName (ObjDesc->Mutex.Node), + WalkState->Thread->CurrentSyncLevel)); + return_ACPI_STATUS (AE_AML_MUTEX_ORDER); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Acquiring: Mutex SyncLevel %u, Thread SyncLevel %u, " + "Depth %u TID %p\n", + ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel, + ObjDesc->Mutex.AcquisitionDepth, WalkState->Thread)); + + Status = AcpiExAcquireMutexObject ((UINT16) TimeDesc->Integer.Value, + ObjDesc, WalkState->Thread->ThreadId); + + if (ACPI_SUCCESS (Status) && ObjDesc->Mutex.AcquisitionDepth == 1) + { + /* Save Thread object, original/current sync levels */ + + ObjDesc->Mutex.OwnerThread = WalkState->Thread; + ObjDesc->Mutex.OriginalSyncLevel = + WalkState->Thread->CurrentSyncLevel; + WalkState->Thread->CurrentSyncLevel = + ObjDesc->Mutex.SyncLevel; + + /* Link the mutex to the current thread for force-unlock at method exit */ + + AcpiExLinkMutex (ObjDesc, WalkState->Thread); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Acquired: Mutex SyncLevel %u, Thread SyncLevel %u, Depth %u\n", + ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel, + ObjDesc->Mutex.AcquisitionDepth)); + + return_ACPI_STATUS (Status); +} + + +/******************************************************************************* + * + * FUNCTION: AcpiExReleaseMutexObject + * + * PARAMETERS: ObjDesc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Release a previously acquired Mutex, low level interface. + * Provides a common path that supports multiple releases (after + * previous multiple acquires) by the same thread. + * + * MUTEX: Interpreter must be locked + * + * NOTE: This interface is called from three places: + * 1) From AcpiExReleaseMutex, via an AML Acquire() operator + * 2) From AcpiExReleaseGlobalLock when an AML Field access requires the + * global lock + * 3) From the external interface, AcpiReleaseGlobalLock + * + ******************************************************************************/ + +ACPI_STATUS +AcpiExReleaseMutexObject ( + ACPI_OPERAND_OBJECT *ObjDesc) +{ + ACPI_STATUS Status = AE_OK; + + + ACPI_FUNCTION_TRACE (ExReleaseMutexObject); + + + if (ObjDesc->Mutex.AcquisitionDepth == 0) + { + return_ACPI_STATUS (AE_NOT_ACQUIRED); + } + + /* Match multiple Acquires with multiple Releases */ + + ObjDesc->Mutex.AcquisitionDepth--; + if (ObjDesc->Mutex.AcquisitionDepth != 0) + { + /* Just decrement the depth and return */ + + return_ACPI_STATUS (AE_OK); + } + + if (ObjDesc->Mutex.OwnerThread) + { + /* Unlink the mutex from the owner's list */ + + AcpiExUnlinkMutex (ObjDesc); + ObjDesc->Mutex.OwnerThread = NULL; + } + + /* Release the mutex, special case for Global Lock */ + + if (ObjDesc == AcpiGbl_GlobalLockMutex) + { + Status = AcpiEvReleaseGlobalLock (); + } + else + { + AcpiOsReleaseMutex (ObjDesc->Mutex.OsMutex); + } + + /* Clear mutex info */ + + ObjDesc->Mutex.ThreadId = 0; + return_ACPI_STATUS (Status); +} + + +/******************************************************************************* + * + * FUNCTION: AcpiExReleaseMutex + * + * PARAMETERS: ObjDesc - The object descriptor for this op + * WalkState - Current method execution state + * + * RETURN: Status + * + * DESCRIPTION: Release a previously acquired Mutex. + * + ******************************************************************************/ + +ACPI_STATUS +AcpiExReleaseMutex ( + ACPI_OPERAND_OBJECT *ObjDesc, + ACPI_WALK_STATE *WalkState) +{ + UINT8 PreviousSyncLevel; + ACPI_THREAD_STATE *OwnerThread; + ACPI_STATUS Status = AE_OK; + + + ACPI_FUNCTION_TRACE (ExReleaseMutex); + + + if (!ObjDesc) + { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + OwnerThread = ObjDesc->Mutex.OwnerThread; + + /* The mutex must have been previously acquired in order to release it */ + + if (!OwnerThread) + { + ACPI_ERROR ((AE_INFO, + "Cannot release Mutex [%4.4s], not acquired", + AcpiUtGetNodeName (ObjDesc->Mutex.Node))); + return_ACPI_STATUS (AE_AML_MUTEX_NOT_ACQUIRED); + } + + /* Must have a valid thread ID */ + + if (!WalkState->Thread) + { + ACPI_ERROR ((AE_INFO, + "Cannot release Mutex [%4.4s], null thread info", + AcpiUtGetNodeName (ObjDesc->Mutex.Node))); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* + * The Mutex is owned, but this thread must be the owner. + * Special case for Global Lock, any thread can release + */ + if ((OwnerThread->ThreadId != WalkState->Thread->ThreadId) && + (ObjDesc != AcpiGbl_GlobalLockMutex)) + { + ACPI_ERROR ((AE_INFO, + "Thread %u cannot release Mutex [%4.4s] acquired by thread %u", + (UINT32) WalkState->Thread->ThreadId, + AcpiUtGetNodeName (ObjDesc->Mutex.Node), + (UINT32) OwnerThread->ThreadId)); + return_ACPI_STATUS (AE_AML_NOT_OWNER); + } + + /* + * The sync level of the mutex must be equal to the current sync level. In + * other words, the current level means that at least one mutex at that + * level is currently being held. Attempting to release a mutex of a + * different level can only mean that the mutex ordering rule is being + * violated. This behavior is clarified in ACPI 4.0 specification. + */ + if (ObjDesc->Mutex.SyncLevel != OwnerThread->CurrentSyncLevel) + { + ACPI_ERROR ((AE_INFO, + "Cannot release Mutex [%4.4s], SyncLevel mismatch: " + "mutex %u current %u", + AcpiUtGetNodeName (ObjDesc->Mutex.Node), + ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel)); + return_ACPI_STATUS (AE_AML_MUTEX_ORDER); + } + + /* + * Get the previous SyncLevel from the head of the acquired mutex list. + * This handles the case where several mutexes at the same level have been + * acquired, but are not released in reverse order. + */ + PreviousSyncLevel = + OwnerThread->AcquiredMutexList->Mutex.OriginalSyncLevel; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Releasing: Object SyncLevel %u, Thread SyncLevel %u, " + "Prev SyncLevel %u, Depth %u TID %p\n", + ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel, + PreviousSyncLevel, ObjDesc->Mutex.AcquisitionDepth, + WalkState->Thread)); + + Status = AcpiExReleaseMutexObject (ObjDesc); + if (ACPI_FAILURE (Status)) + { + return_ACPI_STATUS (Status); + } + + if (ObjDesc->Mutex.AcquisitionDepth == 0) + { + /* Restore the previous SyncLevel */ + + OwnerThread->CurrentSyncLevel = PreviousSyncLevel; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Released: Object SyncLevel %u, Thread SyncLevel, %u, " + "Prev SyncLevel %u, Depth %u\n", + ObjDesc->Mutex.SyncLevel, WalkState->Thread->CurrentSyncLevel, + PreviousSyncLevel, ObjDesc->Mutex.AcquisitionDepth)); + + return_ACPI_STATUS (Status); +} + + +/******************************************************************************* + * + * FUNCTION: AcpiExReleaseAllMutexes + * + * PARAMETERS: Thread - Current executing thread object + * + * RETURN: Status + * + * DESCRIPTION: Release all mutexes held by this thread + * + * NOTE: This function is called as the thread is exiting the interpreter. + * Mutexes are not released when an individual control method is exited, but + * only when the parent thread actually exits the interpreter. This allows one + * method to acquire a mutex, and a different method to release it, as long as + * this is performed underneath a single parent control method. + * + ******************************************************************************/ + +void +AcpiExReleaseAllMutexes ( + ACPI_THREAD_STATE *Thread) +{ + ACPI_OPERAND_OBJECT *Next = Thread->AcquiredMutexList; + ACPI_OPERAND_OBJECT *ObjDesc; + + + ACPI_FUNCTION_TRACE (ExReleaseAllMutexes); + + + /* Traverse the list of owned mutexes, releasing each one */ + + while (Next) + { + ObjDesc = Next; + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Mutex [%4.4s] force-release, SyncLevel %u Depth %u\n", + ObjDesc->Mutex.Node->Name.Ascii, ObjDesc->Mutex.SyncLevel, + ObjDesc->Mutex.AcquisitionDepth)); + + /* Release the mutex, special case for Global Lock */ + + if (ObjDesc == AcpiGbl_GlobalLockMutex) + { + /* Ignore errors */ + + (void) AcpiEvReleaseGlobalLock (); + } + else + { + AcpiOsReleaseMutex (ObjDesc->Mutex.OsMutex); + } + + /* Update Thread SyncLevel (Last mutex is the important one) */ + + Thread->CurrentSyncLevel = ObjDesc->Mutex.OriginalSyncLevel; + + /* Mark mutex unowned */ + + Next = ObjDesc->Mutex.Next; + + ObjDesc->Mutex.Prev = NULL; + ObjDesc->Mutex.Next = NULL; + ObjDesc->Mutex.AcquisitionDepth = 0; + ObjDesc->Mutex.OwnerThread = NULL; + ObjDesc->Mutex.ThreadId = 0; + } + + return_VOID; +} |