mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 05:34:13 +08:00
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>
This commit is contained in:
parent
475df486f5
commit
58892c962a
@ -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_hardware_lock; /* For ACPI H/W except GPE registers */
|
||||
ACPI_EXTERN acpi_spinlock acpi_gbl_reference_count_lock;
|
||||
|
||||
/* 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
|
||||
*
|
||||
* 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
|
||||
acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
|
||||
{
|
||||
u16 count;
|
||||
u16 new_count;
|
||||
u16 original_count;
|
||||
u16 new_count = 0;
|
||||
acpi_cpu_flags lock_flags;
|
||||
|
||||
ACPI_FUNCTION_NAME(ut_update_ref_count);
|
||||
|
||||
@ -379,46 +380,58 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
|
||||
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) {
|
||||
case REF_INCREMENT:
|
||||
|
||||
new_count++;
|
||||
new_count = original_count + 1;
|
||||
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,
|
||||
"Obj %p Refs=%X, [Incremented]\n",
|
||||
object, new_count));
|
||||
"Obj %p Type %.2X Refs %.2X [Incremented]\n",
|
||||
object, object->common.type, new_count));
|
||||
break;
|
||||
|
||||
case REF_DECREMENT:
|
||||
|
||||
if (count < 1) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
|
||||
"Obj %p Refs=%X, can't decrement! (Set to 0)\n",
|
||||
object, new_count));
|
||||
/* The current reference count must be non-zero */
|
||||
|
||||
new_count = 0;
|
||||
} else {
|
||||
new_count--;
|
||||
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
|
||||
"Obj %p Refs=%X, [Decremented]\n",
|
||||
object, new_count));
|
||||
if (original_count) {
|
||||
new_count = original_count - 1;
|
||||
object->common.reference_count = new_count;
|
||||
}
|
||||
|
||||
if (object->common.type == ACPI_TYPE_METHOD) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
|
||||
"Method Obj %p Refs=%X, [Decremented]\n",
|
||||
object, new_count));
|
||||
acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
|
||||
|
||||
if (!original_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) {
|
||||
acpi_ut_delete_internal_obj(object);
|
||||
}
|
||||
@ -426,18 +439,20 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action)
|
||||
|
||||
default:
|
||||
|
||||
ACPI_ERROR((AE_INFO, "Unknown action (0x%X)", action));
|
||||
break;
|
||||
acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
|
||||
ACPI_ERROR((AE_INFO, "Unknown Reference Count action (0x%X)",
|
||||
action));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check the reference count, for debug purposes only.
|
||||
* (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,
|
||||
"Large Reference Count (0x%X) in object %p",
|
||||
count, object));
|
||||
"Large Reference Count (0x%X) in object %p, Type=0x%.2X",
|
||||
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
|
||||
* each caller from having to check. Also, ignore NS nodes.
|
||||
*
|
||||
*/
|
||||
if (!object ||
|
||||
(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);
|
||||
if (ACPI_FAILURE (status)) {
|
||||
@ -93,7 +93,13 @@ acpi_status acpi_ut_mutex_initialize(void)
|
||||
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 */
|
||||
|
||||
status = acpi_os_create_mutex(&acpi_gbl_osi_mutex);
|
||||
if (ACPI_FAILURE(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_hardware_lock);
|
||||
acpi_os_delete_lock(acpi_gbl_reference_count_lock);
|
||||
|
||||
/* Delete the reader/writer lock */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user