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

Commit 26294842 authored by Lin Ming's avatar Lin Ming Committed by Len Brown
Browse files

ACPICA: Fix issues/fault with automatic "serialized" method support



History: This support changes a method to "serialized" on the fly if the
method generates an AE_ALREADY_EXISTS error, indicating the possibility
that it cannot handle reentrancy.

This fix repairs a couple of issues seen in the field, especially on
machines with many cores.

1) Delete method children only upon the exit of the last thread, so
as to not delete objects out from under running threads.

2) Set the "serialized" bit for the method only upon the exit of the
last thread, so as to not cause deadlock when running threads attempt
to exit.

3) Cleanup the use of the AML "MethodFlags" and internal method flags
so that there is no longer any confustion between the two.

Reported-by: default avatarDana Myers <dana.myers@oracle.com>
Signed-off-by: default avatarLin Ming <ming.m.lin@intel.com>
Signed-off-by: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 672af843
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -97,8 +97,6 @@
#define AOPOBJ_OBJECT_INITIALIZED   0x08	/* Region is initialized, _REG was run */
#define AOPOBJ_SETUP_COMPLETE       0x10	/* Region setup is complete */
#define AOPOBJ_INVALID              0x20	/* Host OS won't allow a Region address */
#define AOPOBJ_MODULE_LEVEL         0x40	/* Method is actually module-level code */
#define AOPOBJ_MODIFIED_NAMESPACE   0x80	/* Method modified the namespace */

/******************************************************************************
 *
@@ -175,7 +173,7 @@ struct acpi_object_region {
};

struct acpi_object_method {
	ACPI_OBJECT_COMMON_HEADER u8 method_flags;
	ACPI_OBJECT_COMMON_HEADER u8 info_flags;
	u8 param_count;
	u8 sync_level;
	union acpi_operand_object *mutex;
@@ -183,13 +181,21 @@ struct acpi_object_method {
	union {
		ACPI_INTERNAL_METHOD implementation;
		union acpi_operand_object *handler;
	} extra;
	} dispatch;

	u32 aml_length;
	u8 thread_count;
	acpi_owner_id owner_id;
};

/* Flags for info_flags field above */

#define ACPI_METHOD_MODULE_LEVEL        0x01	/* Method is actually module-level code */
#define ACPI_METHOD_INTERNAL_ONLY       0x02	/* Method is implemented internally (_OSI) */
#define ACPI_METHOD_SERIALIZED          0x04	/* Method is serialized */
#define ACPI_METHOD_SERIALIZED_PENDING  0x08	/* Method is to be marked serialized */
#define ACPI_METHOD_MODIFIED_NAMESPACE  0x10	/* Method modified the namespace */

/******************************************************************************
 *
 * Objects that can be notified.  All share a common notify_info area.
+1 −7
Original line number Diff line number Diff line
@@ -480,16 +480,10 @@ typedef enum {
	AML_FIELD_ATTRIB_SMB_BLOCK_CALL = 0x0D
} AML_ACCESS_ATTRIBUTE;

/* Bit fields in method_flags byte */
/* Bit fields in the AML method_flags byte */

#define AML_METHOD_ARG_COUNT        0x07
#define AML_METHOD_SERIALIZED       0x08
#define AML_METHOD_SYNC_LEVEL       0xF0

/* METHOD_FLAGS_ARG_COUNT is not used internally, define additional flags */

#define AML_METHOD_INTERNAL_ONLY    0x01
#define AML_METHOD_RESERVED1        0x02
#define AML_METHOD_RESERVED2        0x04

#endif				/* __AMLCODE_H__ */
+47 −15
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@

#include <acpi/acpi.h>
#include "accommon.h"
#include "amlcode.h"
#include "acdispat.h"
#include "acinterp.h"
#include "acnamesp.h"
@@ -201,7 +200,7 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,
	/*
	 * If this method is serialized, we need to acquire the method mutex.
	 */
	if (obj_desc->method.method_flags & AML_METHOD_SERIALIZED) {
	if (obj_desc->method.info_flags & ACPI_METHOD_SERIALIZED) {
		/*
		 * Create a mutex for the method if it is defined to be Serialized
		 * and a mutex has not already been created. We defer the mutex creation
@@ -413,8 +412,9 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,

	/* Invoke an internal method if necessary */

	if (obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) {
		status = obj_desc->method.extra.implementation(next_walk_state);
	if (obj_desc->method.info_flags & ACPI_METHOD_INTERNAL_ONLY) {
		status =
		    obj_desc->method.dispatch.implementation(next_walk_state);
		if (status == AE_OK) {
			status = AE_CTRL_TERMINATE;
		}
@@ -579,11 +579,14 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,

		/*
		 * Delete any namespace objects created anywhere within the
		 * namespace by the execution of this method. Unless this method
		 * is a module-level executable code method, in which case we
		 * want make the objects permanent.
		 * namespace by the execution of this method. Unless:
		 * 1) This method is a module-level executable code method, in which
		 *    case we want make the objects permanent.
		 * 2) There are other threads executing the method, in which case we
		 *    will wait until the last thread has completed.
		 */
		if (!(method_desc->method.flags & AOPOBJ_MODULE_LEVEL)) {
		if (!(method_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL)
		    && (method_desc->method.thread_count == 1)) {

			/* Delete any direct children of (created by) this method */

@@ -593,12 +596,17 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
			/*
			 * Delete any objects that were created by this method
			 * elsewhere in the namespace (if any were created).
			 * Use of the ACPI_METHOD_MODIFIED_NAMESPACE optimizes the
			 * deletion such that we don't have to perform an entire
			 * namespace walk for every control method execution.
			 */
			if (method_desc->method.
			    flags & AOPOBJ_MODIFIED_NAMESPACE) {
			    info_flags & ACPI_METHOD_MODIFIED_NAMESPACE) {
				acpi_ns_delete_namespace_by_owner(method_desc->
								  method.
								  owner_id);
				method_desc->method.info_flags &=
				    ~ACPI_METHOD_MODIFIED_NAMESPACE;
			}
		}
	}
@@ -629,19 +637,43 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
		 * Serialized if it appears that the method is incorrectly written and
		 * does not support multiple thread execution. The best example of this
		 * is if such a method creates namespace objects and blocks. A second
		 * thread will fail with an AE_ALREADY_EXISTS exception
		 * thread will fail with an AE_ALREADY_EXISTS exception.
		 *
		 * This code is here because we must wait until the last thread exits
		 * before creating the synchronization semaphore.
		 * before marking the method as serialized.
		 */
		if (method_desc->method.
		    info_flags & ACPI_METHOD_SERIALIZED_PENDING) {
			if (walk_state) {
				ACPI_INFO((AE_INFO,
					   "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error",
					   walk_state->method_node->name.
					   ascii));
			}

			/*
			 * Method tried to create an object twice and was marked as
			 * "pending serialized". The probable cause is that the method
			 * cannot handle reentrancy.
			 *
			 * The method was created as not_serialized, but it tried to create
			 * a named object and then blocked, causing the second thread
			 * entrance to begin and then fail. Workaround this problem by
			 * marking the method permanently as Serialized when the last
			 * thread exits here.
			 */
		if ((method_desc->method.method_flags & AML_METHOD_SERIALIZED)
		    && (!method_desc->method.mutex)) {
			(void)acpi_ds_create_method_mutex(method_desc);
			method_desc->method.info_flags &=
			    ~ACPI_METHOD_SERIALIZED_PENDING;
			method_desc->method.info_flags |=
			    ACPI_METHOD_SERIALIZED;
			method_desc->method.sync_level = 0;
		}

		/* No more threads, we can free the owner_id */

		if (!(method_desc->method.flags & AOPOBJ_MODULE_LEVEL)) {
		if (!
		    (method_desc->method.
		     info_flags & ACPI_METHOD_MODULE_LEVEL)) {
			acpi_ut_release_owner_id(&method_desc->method.owner_id);
		}
	}
+2 −2
Original line number Diff line number Diff line
@@ -590,9 +590,9 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
				 * See acpi_ns_exec_module_code
				 */
				if (obj_desc->method.
				    flags & AOPOBJ_MODULE_LEVEL) {
				    info_flags & ACPI_METHOD_MODULE_LEVEL) {
					handler_obj =
					    obj_desc->method.extra.handler;
					    obj_desc->method.dispatch.handler;
				}
				break;

+4 −4
Original line number Diff line number Diff line
@@ -482,13 +482,11 @@ acpi_ex_create_method(u8 * aml_start,
	obj_desc->method.aml_length = aml_length;

	/*
	 * Disassemble the method flags. Split off the Arg Count
	 * for efficiency
	 * Disassemble the method flags. Split off the arg_count, Serialized
	 * flag, and sync_level for efficiency.
	 */
	method_flags = (u8) operand[1]->integer.value;

	obj_desc->method.method_flags =
	    (u8) (method_flags & ~AML_METHOD_ARG_COUNT);
	obj_desc->method.param_count =
	    (u8) (method_flags & AML_METHOD_ARG_COUNT);

@@ -497,6 +495,8 @@ acpi_ex_create_method(u8 * aml_start,
	 * created for this method when it is parsed.
	 */
	if (method_flags & AML_METHOD_SERIALIZED) {
		obj_desc->method.info_flags = ACPI_METHOD_SERIALIZED;

		/*
		 * ACPI 1.0: sync_level = 0
		 * ACPI 2.0: sync_level = sync_level in method declaration
Loading