Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58892c96 authored by Bob Moore's avatar Bob Moore Committed by Rafael J. Wysocki
Browse files

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: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarAndriy Gapon <avg@FreeBSD.org>
Signed-off-by: default avatarLv Zheng <lv.zheng@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 475df486
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 */

+48 −34
Original line number Diff line number Diff line
@@ -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--;
		if (original_count) {
			new_count = original_count - 1;
			object->common.reference_count = new_count;
		}

			ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
					  "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));
		}

		if (object->common.type == ACPI_TYPE_METHOD) {
		ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
					  "Method Obj %p Refs=%X, [Decremented]\n",
					  object, new_count));
		}
				  "Obj %p Type %.2X Refs %.2X [Decremented]\n",
				  object, object->common.type, new_count));

		/* Actually delete the object on a reference count of zero */

		object->common.reference_count = new_count;
		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)) {
+8 −1
Original line number Diff line number Diff line
@@ -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 */