diff options
Diffstat (limited to 'drivers/acpi/acpica/utdelete.c')
| -rw-r--r-- | drivers/acpi/acpica/utdelete.c | 186 |
1 files changed, 112 insertions, 74 deletions
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index 798105443d0..a3516de213f 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2012, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -75,6 +75,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) union acpi_operand_object *handler_desc; union acpi_operand_object *second_desc; union acpi_operand_object *next_desc; + union acpi_operand_object *start_desc; union acpi_operand_object **last_obj_ptr; ACPI_FUNCTION_TRACE_PTR(ut_delete_internal_obj, object); @@ -235,10 +236,11 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) if (handler_desc) { next_desc = handler_desc->address_space.region_list; + start_desc = next_desc; last_obj_ptr = &handler_desc->address_space.region_list; - /* Remove the region object from the handler's list */ + /* Remove the region object from the handler list */ while (next_desc) { if (next_desc == object) { @@ -247,10 +249,19 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) break; } - /* Walk the linked list of handler */ + /* Walk the linked list of handlers */ last_obj_ptr = &next_desc->region.next; next_desc = next_desc->region.next; + + /* Prevent infinite loop if list is corrupted */ + + if (next_desc == start_desc) { + ACPI_ERROR((AE_INFO, + "Circular region list in address handler object %p", + handler_desc)); + return_VOID; + } } if (handler_desc->address_space.handler_flags & @@ -303,6 +314,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) break; default: + break; } @@ -340,7 +352,7 @@ void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list) { union acpi_operand_object **internal_obj; - ACPI_FUNCTION_TRACE(ut_delete_internal_object_list); + ACPI_FUNCTION_ENTRY(); /* Walk the null-terminated internal list */ @@ -351,7 +363,7 @@ void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list) /* Free the combined parameter pointer list and object array */ ACPI_FREE(obj_list); - return_VOID; + return; } /******************************************************************************* @@ -359,19 +371,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,76 +392,79 @@ 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)); - - new_count = 0; - } else { - new_count--; + /* The current reference count must be non-zero */ - 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); - object->common.reference_count = new_count; - if (new_count == 0) { - acpi_ut_delete_internal_obj(object); + if (!original_count) { + ACPI_WARNING((AE_INFO, + "Obj %p, Reference Count is already zero, cannot decrement\n", + object)); } - break; - - case REF_FORCE_DELETE: ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, - "Obj %p Refs=%X, Force delete! (Set to 0)\n", - object, count)); + "Obj %p Type %.2X Refs %.2X [Decremented]\n", + object, object->common.type, new_count)); - new_count = 0; - object->common.reference_count = new_count; - acpi_ut_delete_internal_obj(object); + /* Actually delete the object on a reference count of zero */ + + if (new_count == 0) { + acpi_ut_delete_internal_obj(object); + } break; 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)); } } @@ -458,8 +474,7 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) * * PARAMETERS: object - Increment ref count for this object * and all sub-objects - * action - Either REF_INCREMENT or REF_DECREMENT or - * REF_FORCE_DELETE + * action - Either REF_INCREMENT or REF_DECREMENT * * RETURN: Status * @@ -484,7 +499,7 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) union acpi_generic_state *state; u32 i; - ACPI_FUNCTION_TRACE_PTR(ut_update_object_reference, object); + ACPI_FUNCTION_NAME(ut_update_object_reference); while (object) { @@ -493,7 +508,7 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) if (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED) { ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Object %p is NS handle\n", object)); - return_ACPI_STATUS(AE_OK); + return (AE_OK); } /* @@ -505,7 +520,6 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) case ACPI_TYPE_PROCESSOR: case ACPI_TYPE_POWER: case ACPI_TYPE_THERMAL: - /* * Update the notify objects for these types (if present) * Two lists, system and device notify handlers. @@ -530,18 +544,42 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) */ for (i = 0; i < object->package.count; i++) { /* - * Push each element onto the stack for later processing. - * Note: There can be null elements within the package, - * these are simply ignored + * Null package elements are legal and can be simply + * ignored. */ - status = - acpi_ut_create_update_state_and_push - (object->package.elements[i], action, - &state_list); - if (ACPI_FAILURE(status)) { - goto error_exit; + next_object = object->package.elements[i]; + if (!next_object) { + continue; + } + + switch (next_object->common.type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + /* + * For these very simple sub-objects, we can just + * update the reference count here and continue. + * Greatly increases performance of this operation. + */ + acpi_ut_update_ref_count(next_object, + action); + break; + + default: + /* + * For complex sub-objects, push them onto the stack + * for later processing (this eliminates recursion.) + */ + status = + acpi_ut_create_update_state_and_push + (next_object, action, &state_list); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + break; } } + next_object = NULL; break; case ACPI_TYPE_BUFFER_FIELD: @@ -596,6 +634,7 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) case ACPI_TYPE_REGION: default: + break; /* No subobjects for all other types */ } @@ -619,9 +658,9 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) } } - return_ACPI_STATUS(AE_OK); + return (AE_OK); - error_exit: +error_exit: ACPI_EXCEPTION((AE_INFO, status, "Could not update object reference count")); @@ -633,7 +672,7 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) acpi_ut_delete_generic_state(state); } - return_ACPI_STATUS(status); + return (status); } /******************************************************************************* @@ -652,12 +691,12 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) void acpi_ut_add_reference(union acpi_operand_object *object) { - ACPI_FUNCTION_TRACE_PTR(ut_add_reference, object); + ACPI_FUNCTION_NAME(ut_add_reference); /* Ensure that we have a valid object */ if (!acpi_ut_valid_internal_object(object)) { - return_VOID; + return; } ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, @@ -667,7 +706,7 @@ void acpi_ut_add_reference(union acpi_operand_object *object) /* Increment the reference count */ (void)acpi_ut_update_object_reference(object, REF_INCREMENT); - return_VOID; + return; } /******************************************************************************* @@ -685,22 +724,21 @@ void acpi_ut_add_reference(union acpi_operand_object *object) void acpi_ut_remove_reference(union acpi_operand_object *object) { - ACPI_FUNCTION_TRACE_PTR(ut_remove_reference, object); + ACPI_FUNCTION_NAME(ut_remove_reference); /* * 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)) { - return_VOID; + return; } /* Ensure that we have a valid object */ if (!acpi_ut_valid_internal_object(object)) { - return_VOID; + return; } ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, @@ -713,5 +751,5 @@ void acpi_ut_remove_reference(union acpi_operand_object *object) * of all subobjects!) */ (void)acpi_ut_update_object_reference(object, REF_DECREMENT); - return_VOID; + return; } |
