Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
[pandora-kernel.git] / drivers / acpi / acpica / exmutex.c
index d301c1f..2f01142 100644 (file)
@@ -83,6 +83,15 @@ void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc)
 
        if (obj_desc->mutex.prev) {
                (obj_desc->mutex.prev)->mutex.next = obj_desc->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.
+                */
+               (obj_desc->mutex.prev)->mutex.original_sync_level =
+                   obj_desc->mutex.original_sync_level;
        } else {
                thread->acquired_mutex_list = obj_desc->mutex.next;
        }
@@ -349,6 +358,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
                      struct acpi_walk_state *walk_state)
 {
        acpi_status status = AE_OK;
+       u8 previous_sync_level;
 
        ACPI_FUNCTION_TRACE(ex_release_mutex);
 
@@ -373,11 +383,12 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
             walk_state->thread->thread_id)
            && (obj_desc != acpi_gbl_global_lock_mutex)) {
                ACPI_ERROR((AE_INFO,
-                           "Thread %lX cannot release Mutex [%4.4s] acquired by thread %lX",
-                           (unsigned long)walk_state->thread->thread_id,
+                           "Thread %p cannot release Mutex [%4.4s] acquired by thread %p",
+                           ACPI_CAST_PTR(void, walk_state->thread->thread_id),
                            acpi_ut_get_node_name(obj_desc->mutex.node),
-                           (unsigned long)obj_desc->mutex.owner_thread->
-                           thread_id));
+                           ACPI_CAST_PTR(void,
+                                         obj_desc->mutex.owner_thread->
+                                         thread_id)));
                return_ACPI_STATUS(AE_AML_NOT_OWNER);
        }
 
@@ -391,10 +402,14 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
        }
 
        /*
-        * The sync level of the mutex must be less than or equal to the current
-        * sync level
+        * 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 (obj_desc->mutex.sync_level > walk_state->thread->current_sync_level) {
+       if (obj_desc->mutex.sync_level !=
+           walk_state->thread->current_sync_level) {
                ACPI_ERROR((AE_INFO,
                            "Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %d current %d",
                            acpi_ut_get_node_name(obj_desc->mutex.node),
@@ -403,14 +418,24 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
                return_ACPI_STATUS(AE_AML_MUTEX_ORDER);
        }
 
+       /*
+        * Get the previous sync_level 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.
+        */
+       previous_sync_level =
+           walk_state->thread->acquired_mutex_list->mutex.original_sync_level;
+
        status = acpi_ex_release_mutex_object(obj_desc);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
 
        if (obj_desc->mutex.acquisition_depth == 0) {
 
-               /* Restore the original sync_level */
+               /* Restore the previous sync_level */
 
-               walk_state->thread->current_sync_level =
-                   obj_desc->mutex.original_sync_level;
+               walk_state->thread->current_sync_level = previous_sync_level;
        }
        return_ACPI_STATUS(status);
 }