ACPICA: Add a lock to the internal object reference count mechanism
authorBob Moore <robert.moore@intel.com>
Fri, 12 Apr 2013 00:25:41 +0000 (00:25 +0000)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 12 Apr 2013 11:29:48 +0000 (13:29 +0200)
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>
drivers/acpi/acpica/acglobal.h
drivers/acpi/acpica/utdelete.c
drivers/acpi/acpica/utmutex.c

index 833fbc5be2d61c4a6ab7fa97fc1504cf6a7ac334..07160928ca25cced7906412401d46d40b72194be 100644 (file)
@@ -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 */
 
index fc11ad12747d6c8ca4e66bef69849bf8f573cfe0..29b930250b6fe01a7822a229dc0f45ed80290a52 100644 (file)
@@ -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));
-
-                       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);
+
+               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)) {
index 22feb99b8e35438767f4f4880332c19d0feb0851..08c32324558449001af0c8ad6374c4286f73d43f 100644 (file)
@@ -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 */