Commit 58892c96 authored by Bob Moore's avatar Bob Moore Committed by Rafael J. Wysocki

ACPICA: Add a lock to the internal object reference count mechanism

Certain external interfaces need to update object references
without holding the interpreter or namespace mutex objects. To
prevent race conditions, add a spinlock around the increment
and decrement of the reference counts for internal ACPI
objects. Reported by Andriy Gapon (avg@FreeBSD.org).
Signed-off-by: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarAndriy Gapon <avg@FreeBSD.org>
Signed-off-by: default avatarLv Zheng <lv.zheng@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 475df486
...@@ -224,6 +224,7 @@ ACPI_EXTERN u8 acpi_gbl_global_lock_pending; ...@@ -224,6 +224,7 @@ ACPI_EXTERN u8 acpi_gbl_global_lock_pending;
*/ */
ACPI_EXTERN acpi_spinlock acpi_gbl_gpe_lock; /* For GPE data structs and registers */ ACPI_EXTERN acpi_spinlock acpi_gbl_gpe_lock; /* For GPE data structs and registers */
ACPI_EXTERN acpi_spinlock acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ ACPI_EXTERN acpi_spinlock acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */
ACPI_EXTERN acpi_spinlock acpi_gbl_reference_count_lock;
/* Mutex for _OSI support */ /* Mutex for _OSI support */
......
...@@ -359,19 +359,20 @@ void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list) ...@@ -359,19 +359,20 @@ void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list)
* FUNCTION: acpi_ut_update_ref_count * FUNCTION: acpi_ut_update_ref_count
* *
* PARAMETERS: object - Object whose ref count is to be updated * PARAMETERS: object - Object whose ref count is to be updated
* action - What to do * action - What to do (REF_INCREMENT or REF_DECREMENT)
* *
* RETURN: New ref count * RETURN: None. Sets new reference count within the object
* *
* DESCRIPTION: Modify the ref count and return it. * DESCRIPTION: Modify the reference count for an internal acpi object
* *
******************************************************************************/ ******************************************************************************/
static void static void
acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
{ {
u16 count; u16 original_count;
u16 new_count; u16 new_count = 0;
acpi_cpu_flags lock_flags;
ACPI_FUNCTION_NAME(ut_update_ref_count); ACPI_FUNCTION_NAME(ut_update_ref_count);
...@@ -379,46 +380,58 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) ...@@ -379,46 +380,58 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
return; return;
} }
count = object->common.reference_count;
new_count = count;
/* /*
* Perform the reference count action (increment, decrement, force delete) * Always get the reference count lock. Note: Interpreter and/or
* Namespace is not always locked when this function is called.
*/ */
lock_flags = acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
original_count = object->common.reference_count;
/* Perform the reference count action (increment, decrement) */
switch (action) { switch (action) {
case REF_INCREMENT: case REF_INCREMENT:
new_count++; new_count = original_count + 1;
object->common.reference_count = new_count; object->common.reference_count = new_count;
acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
/* The current reference count should never be zero here */
if (!original_count) {
ACPI_WARNING((AE_INFO,
"Obj %p, Reference Count was zero before increment\n",
object));
}
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
"Obj %p Refs=%X, [Incremented]\n", "Obj %p Type %.2X Refs %.2X [Incremented]\n",
object, new_count)); object, object->common.type, new_count));
break; break;
case REF_DECREMENT: case REF_DECREMENT:
if (count < 1) { /* The current reference count must be non-zero */
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
"Obj %p Refs=%X, can't decrement! (Set to 0)\n",
object, new_count));
new_count = 0; if (original_count) {
} else { new_count = original_count - 1;
new_count--; object->common.reference_count = new_count;
}
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
"Obj %p Refs=%X, [Decremented]\n",
object, new_count)); if (!original_count) {
ACPI_WARNING((AE_INFO,
"Obj %p, Reference Count is already zero, cannot decrement\n",
object));
} }
if (object->common.type == ACPI_TYPE_METHOD) {
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
"Method Obj %p Refs=%X, [Decremented]\n", "Obj %p Type %.2X Refs %.2X [Decremented]\n",
object, new_count)); object, object->common.type, new_count));
}
/* Actually delete the object on a reference count of zero */
object->common.reference_count = new_count;
if (new_count == 0) { if (new_count == 0) {
acpi_ut_delete_internal_obj(object); acpi_ut_delete_internal_obj(object);
} }
...@@ -426,18 +439,20 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) ...@@ -426,18 +439,20 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
default: default:
ACPI_ERROR((AE_INFO, "Unknown action (0x%X)", action)); acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
break; ACPI_ERROR((AE_INFO, "Unknown Reference Count action (0x%X)",
action));
return;
} }
/* /*
* Sanity check the reference count, for debug purposes only. * Sanity check the reference count, for debug purposes only.
* (A deleted object will have a huge reference count) * (A deleted object will have a huge reference count)
*/ */
if (count > ACPI_MAX_REFERENCE_COUNT) { if (new_count > ACPI_MAX_REFERENCE_COUNT) {
ACPI_WARNING((AE_INFO, ACPI_WARNING((AE_INFO,
"Large Reference Count (0x%X) in object %p", "Large Reference Count (0x%X) in object %p, Type=0x%.2X",
count, object)); new_count, object, object->common.type));
} }
} }
...@@ -702,7 +717,6 @@ void acpi_ut_remove_reference(union acpi_operand_object *object) ...@@ -702,7 +717,6 @@ void acpi_ut_remove_reference(union acpi_operand_object *object)
/* /*
* Allow a NULL pointer to be passed in, just ignore it. This saves * Allow a NULL pointer to be passed in, just ignore it. This saves
* each caller from having to check. Also, ignore NS nodes. * each caller from having to check. Also, ignore NS nodes.
*
*/ */
if (!object || if (!object ||
(ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED)) { (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED)) {
......
...@@ -81,7 +81,7 @@ acpi_status acpi_ut_mutex_initialize(void) ...@@ -81,7 +81,7 @@ acpi_status acpi_ut_mutex_initialize(void)
} }
} }
/* Create the spinlocks for use at interrupt level */ /* Create the spinlocks for use at interrupt level or for speed */
status = acpi_os_create_lock (&acpi_gbl_gpe_lock); status = acpi_os_create_lock (&acpi_gbl_gpe_lock);
if (ACPI_FAILURE (status)) { if (ACPI_FAILURE (status)) {
...@@ -93,7 +93,13 @@ acpi_status acpi_ut_mutex_initialize(void) ...@@ -93,7 +93,13 @@ acpi_status acpi_ut_mutex_initialize(void)
return_ACPI_STATUS (status); return_ACPI_STATUS (status);
} }
status = acpi_os_create_lock(&acpi_gbl_reference_count_lock);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Mutex for _OSI support */ /* Mutex for _OSI support */
status = acpi_os_create_mutex(&acpi_gbl_osi_mutex); status = acpi_os_create_mutex(&acpi_gbl_osi_mutex);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
...@@ -136,6 +142,7 @@ void acpi_ut_mutex_terminate(void) ...@@ -136,6 +142,7 @@ void acpi_ut_mutex_terminate(void)
acpi_os_delete_lock(acpi_gbl_gpe_lock); acpi_os_delete_lock(acpi_gbl_gpe_lock);
acpi_os_delete_lock(acpi_gbl_hardware_lock); acpi_os_delete_lock(acpi_gbl_hardware_lock);
acpi_os_delete_lock(acpi_gbl_reference_count_lock);
/* Delete the reader/writer lock */ /* Delete the reader/writer lock */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment