summaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorBob Moore <robert.moore@intel.com>2009-05-21 10:02:34 +0800
committerLen Brown <len.brown@intel.com>2009-05-27 00:32:24 -0400
commit10a3b461a258f52b17fb8e35edf3625726eca9a8 (patch)
tree9fa487b04ecb9007f6eac30ee9a6ecb4c179090e /drivers/acpi
parentba9e2ae443feb7231d9631ea0f62b63e26cfb9b1 (diff)
ACPICA: Mutex support: Fix release ordering issue and current sync level
Fixes a problem where if multiple mutexes of the same sync level are acquired but then not released in strict opposite order, the current sync level becomes confused and can cause errors. ACPICA BZ 471. http://acpica.org/bugzilla/show_bug.cgi?id=471 Signed-off-by: Bob Moore <robert.moore@intel.com> Signed-off-by: Lin Ming <ming.m.lin@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/acpica/exmutex.c21
1 files changed, 19 insertions, 2 deletions
diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c
index d7cb030a21f..77a592aa53c 100644
--- a/drivers/acpi/acpica/exmutex.c
+++ b/drivers/acpi/acpica/exmutex.c
@@ -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);
@@ -404,14 +414,21 @@ 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 (obj_desc->mutex.acquisition_depth == 0) {
/* Restore the original 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);
}