aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBob Moore <robert.moore@intel.com>2013-04-11 20:25:41 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-04-12 07:29:48 -0400
commit58892c962a0f8b7187b036e787223365864bde3f (patch)
treefcf84c2f3f4152331aa0536e0819f3067fa3b369
parent475df486f5191ce0671b99aff029b38bff7a72f1 (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.h1
-rw-r--r--drivers/acpi/acpica/utdelete.c82
-rw-r--r--drivers/acpi/acpica/utmutex.c9
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 */
225ACPI_EXTERN acpi_spinlock acpi_gbl_gpe_lock; /* For GPE data structs and registers */ 225ACPI_EXTERN acpi_spinlock acpi_gbl_gpe_lock; /* For GPE data structs and registers */
226ACPI_EXTERN acpi_spinlock acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ 226ACPI_EXTERN acpi_spinlock acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */
227ACPI_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
370static void 370static void
371acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) 371acpi_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