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: Bob Moore <robert.moore@intel.com>
Signed-off-by: Andriy Gapon <avg@FreeBSD.org>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
tirimbino
Bob Moore 12 years ago committed by Rafael J. Wysocki
parent 475df486f5
commit 58892c962a
  1. 1
      drivers/acpi/acpica/acglobal.h
  2. 82
      drivers/acpi/acpica/utdelete.c
  3. 9
      drivers/acpi/acpica/utmutex.c

@ -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)
* 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)
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;
} else {
new_count--;
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, if (original_count) {
"Obj %p Refs=%X, [Decremented]\n", new_count = original_count - 1;
object, new_count)); object->common.reference_count = new_count;
} }
if (object->common.type == ACPI_TYPE_METHOD) { acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
"Method Obj %p Refs=%X, [Decremented]\n", if (!original_count) {
object, new_count)); ACPI_WARNING((AE_INFO,
"Obj %p, Reference Count is already zero, cannot decrement\n",
object));
} }
object->common.reference_count = new_count; ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
"Obj %p Type %.2X Refs %.2X [Decremented]\n",
object, object->common.type, new_count));
/* Actually delete the object on a reference count of zero */
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)
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)
/* /*
* 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)
} }
} }
/* 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)
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)
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 */

Loading…
Cancel
Save