diff options
author | Bob Moore <robert.moore@intel.com> | 2013-04-11 20:25:41 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-04-12 07:29:48 -0400 |
commit | 58892c962a0f8b7187b036e787223365864bde3f (patch) | |
tree | fcf84c2f3f4152331aa0536e0819f3067fa3b369 | |
parent | 475df486f5191ce0671b99aff029b38bff7a72f1 (diff) |
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>
-rw-r--r-- | drivers/acpi/acpica/acglobal.h | 1 | ||||
-rw-r--r-- | drivers/acpi/acpica/utdelete.c | 82 | ||||
-rw-r--r-- | drivers/acpi/acpica/utmutex.c | 9 |
3 files changed, 57 insertions, 35 deletions
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 833fbc5be2d6..07160928ca25 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h | |||
@@ -224,6 +224,7 @@ ACPI_EXTERN u8 acpi_gbl_global_lock_pending; | |||
224 | */ | 224 | */ |
225 | ACPI_EXTERN acpi_spinlock acpi_gbl_gpe_lock; /* For GPE data structs and registers */ | 225 | ACPI_EXTERN acpi_spinlock acpi_gbl_gpe_lock; /* For GPE data structs and registers */ |
226 | ACPI_EXTERN acpi_spinlock acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ | 226 | ACPI_EXTERN acpi_spinlock acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ |
227 | ACPI_EXTERN acpi_spinlock acpi_gbl_reference_count_lock; | ||
227 | 228 | ||
228 | /* Mutex for _OSI support */ | 229 | /* Mutex for _OSI support */ |
229 | 230 | ||
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index fc11ad12747d..29b930250b6f 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c | |||
@@ -359,19 +359,20 @@ void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list) | |||
359 | * FUNCTION: acpi_ut_update_ref_count | 359 | * FUNCTION: acpi_ut_update_ref_count |
360 | * | 360 | * |
361 | * PARAMETERS: object - Object whose ref count is to be updated | 361 | * PARAMETERS: object - Object whose ref count is to be updated |
362 | * action - What to do | 362 | * action - What to do (REF_INCREMENT or REF_DECREMENT) |
363 | * | 363 | * |
364 | * RETURN: New ref count | 364 | * RETURN: None. Sets new reference count within the object |
365 | * | 365 | * |
366 | * DESCRIPTION: Modify the ref count and return it. | 366 | * DESCRIPTION: Modify the reference count for an internal acpi object |
367 | * | 367 | * |
368 | ******************************************************************************/ | 368 | ******************************************************************************/ |
369 | 369 | ||
370 | static void | 370 | static void |
371 | acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) | 371 | acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) |
372 | { | 372 | { |
373 | u16 count; | 373 | u16 original_count; |
374 | u16 new_count; | 374 | u16 new_count = 0; |
375 | acpi_cpu_flags lock_flags; | ||
375 | 376 | ||
376 | ACPI_FUNCTION_NAME(ut_update_ref_count); | 377 | ACPI_FUNCTION_NAME(ut_update_ref_count); |
377 | 378 | ||
@@ -379,46 +380,58 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) | |||
379 | return; | 380 | return; |
380 | } | 381 | } |
381 | 382 | ||
382 | count = object->common.reference_count; | ||
383 | new_count = count; | ||
384 | |||
385 | /* | 383 | /* |
386 | * Perform the reference count action (increment, decrement, force delete) | 384 | * Always get the reference count lock. Note: Interpreter and/or |
385 | * Namespace is not always locked when this function is called. | ||
387 | */ | 386 | */ |
387 | lock_flags = acpi_os_acquire_lock(acpi_gbl_reference_count_lock); | ||
388 | original_count = object->common.reference_count; | ||
389 | |||
390 | /* Perform the reference count action (increment, decrement) */ | ||
391 | |||
388 | switch (action) { | 392 | switch (action) { |
389 | case REF_INCREMENT: | 393 | case REF_INCREMENT: |
390 | 394 | ||
391 | new_count++; | 395 | new_count = original_count + 1; |
392 | object->common.reference_count = new_count; | 396 | object->common.reference_count = new_count; |
397 | acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags); | ||
398 | |||
399 | /* The current reference count should never be zero here */ | ||
400 | |||
401 | if (!original_count) { | ||
402 | ACPI_WARNING((AE_INFO, | ||
403 | "Obj %p, Reference Count was zero before increment\n", | ||
404 | object)); | ||
405 | } | ||
393 | 406 | ||
394 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, | 407 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, |
395 | "Obj %p Refs=%X, [Incremented]\n", | 408 | "Obj %p Type %.2X Refs %.2X [Incremented]\n", |
396 | object, new_count)); | 409 | object, object->common.type, new_count)); |
397 | break; | 410 | break; |
398 | 411 | ||
399 | case REF_DECREMENT: | 412 | case REF_DECREMENT: |
400 | 413 | ||
401 | if (count < 1) { | 414 | /* The current reference count must be non-zero */ |
402 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, | ||
403 | "Obj %p Refs=%X, can't decrement! (Set to 0)\n", | ||
404 | object, new_count)); | ||
405 | |||
406 | new_count = 0; | ||
407 | } else { | ||
408 | new_count--; | ||
409 | 415 | ||
410 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, | 416 | if (original_count) { |
411 | "Obj %p Refs=%X, [Decremented]\n", | 417 | new_count = original_count - 1; |
412 | object, new_count)); | 418 | object->common.reference_count = new_count; |
413 | } | 419 | } |
414 | 420 | ||
415 | if (object->common.type == ACPI_TYPE_METHOD) { | 421 | acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags); |
416 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, | 422 | |
417 | "Method Obj %p Refs=%X, [Decremented]\n", | 423 | if (!original_count) { |
418 | object, new_count)); | 424 | ACPI_WARNING((AE_INFO, |
425 | "Obj %p, Reference Count is already zero, cannot decrement\n", | ||
426 | object)); | ||
419 | } | 427 | } |
420 | 428 | ||
421 | object->common.reference_count = new_count; | 429 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, |
430 | "Obj %p Type %.2X Refs %.2X [Decremented]\n", | ||
431 | object, object->common.type, new_count)); | ||
432 | |||
433 | /* Actually delete the object on a reference count of zero */ | ||
434 | |||
422 | if (new_count == 0) { | 435 | if (new_count == 0) { |
423 | acpi_ut_delete_internal_obj(object); | 436 | acpi_ut_delete_internal_obj(object); |
424 | } | 437 | } |
@@ -426,18 +439,20 @@ acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) | |||
426 | 439 | ||
427 | default: | 440 | default: |
428 | 441 | ||
429 | ACPI_ERROR((AE_INFO, "Unknown action (0x%X)", action)); | 442 | acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags); |
430 | break; | 443 | ACPI_ERROR((AE_INFO, "Unknown Reference Count action (0x%X)", |
444 | action)); | ||
445 | return; | ||
431 | } | 446 | } |
432 | 447 | ||
433 | /* | 448 | /* |
434 | * Sanity check the reference count, for debug purposes only. | 449 | * Sanity check the reference count, for debug purposes only. |
435 | * (A deleted object will have a huge reference count) | 450 | * (A deleted object will have a huge reference count) |
436 | */ | 451 | */ |
437 | if (count > ACPI_MAX_REFERENCE_COUNT) { | 452 | if (new_count > ACPI_MAX_REFERENCE_COUNT) { |
438 | ACPI_WARNING((AE_INFO, | 453 | ACPI_WARNING((AE_INFO, |
439 | "Large Reference Count (0x%X) in object %p", | 454 | "Large Reference Count (0x%X) in object %p, Type=0x%.2X", |
440 | count, object)); | 455 | new_count, object, object->common.type)); |
441 | } | 456 | } |
442 | } | 457 | } |
443 | 458 | ||
@@ -702,7 +717,6 @@ void acpi_ut_remove_reference(union acpi_operand_object *object) | |||
702 | /* | 717 | /* |
703 | * Allow a NULL pointer to be passed in, just ignore it. This saves | 718 | * Allow a NULL pointer to be passed in, just ignore it. This saves |
704 | * each caller from having to check. Also, ignore NS nodes. | 719 | * each caller from having to check. Also, ignore NS nodes. |
705 | * | ||
706 | */ | 720 | */ |
707 | if (!object || | 721 | if (!object || |
708 | (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED)) { | 722 | (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED)) { |
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 22feb99b8e35..08c323245584 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c | |||
@@ -81,7 +81,7 @@ acpi_status acpi_ut_mutex_initialize(void) | |||
81 | } | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | /* Create the spinlocks for use at interrupt level */ | 84 | /* Create the spinlocks for use at interrupt level or for speed */ |
85 | 85 | ||
86 | status = acpi_os_create_lock (&acpi_gbl_gpe_lock); | 86 | status = acpi_os_create_lock (&acpi_gbl_gpe_lock); |
87 | if (ACPI_FAILURE (status)) { | 87 | if (ACPI_FAILURE (status)) { |
@@ -93,7 +93,13 @@ acpi_status acpi_ut_mutex_initialize(void) | |||
93 | return_ACPI_STATUS (status); | 93 | return_ACPI_STATUS (status); |
94 | } | 94 | } |
95 | 95 | ||
96 | status = acpi_os_create_lock(&acpi_gbl_reference_count_lock); | ||
97 | if (ACPI_FAILURE(status)) { | ||
98 | return_ACPI_STATUS(status); | ||
99 | } | ||
100 | |||
96 | /* Mutex for _OSI support */ | 101 | /* Mutex for _OSI support */ |
102 | |||
97 | status = acpi_os_create_mutex(&acpi_gbl_osi_mutex); | 103 | status = acpi_os_create_mutex(&acpi_gbl_osi_mutex); |
98 | if (ACPI_FAILURE(status)) { | 104 | if (ACPI_FAILURE(status)) { |
99 | return_ACPI_STATUS(status); | 105 | return_ACPI_STATUS(status); |
@@ -136,6 +142,7 @@ void acpi_ut_mutex_terminate(void) | |||
136 | 142 | ||
137 | acpi_os_delete_lock(acpi_gbl_gpe_lock); | 143 | acpi_os_delete_lock(acpi_gbl_gpe_lock); |
138 | acpi_os_delete_lock(acpi_gbl_hardware_lock); | 144 | acpi_os_delete_lock(acpi_gbl_hardware_lock); |
145 | acpi_os_delete_lock(acpi_gbl_reference_count_lock); | ||
139 | 146 | ||
140 | /* Delete the reader/writer lock */ | 147 | /* Delete the reader/writer lock */ |
141 | 148 | ||