diff options
Diffstat (limited to 'drivers/acpi/acpica/dsmethod.c')
| -rw-r--r-- | drivers/acpi/acpica/dsmethod.c | 241 | 
1 files changed, 211 insertions, 30 deletions
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index d94dd8974b5..3c7f7378b94 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -5,7 +5,7 @@   *****************************************************************************/  /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp.   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -43,26 +43,164 @@  #include <acpi/acpi.h>  #include "accommon.h" -#include "amlcode.h"  #include "acdispat.h"  #include "acinterp.h"  #include "acnamesp.h"  #ifdef	ACPI_DISASSEMBLER -#include <acpi/acdisasm.h> +#include "acdisasm.h"  #endif +#include "acparser.h" +#include "amlcode.h"  #define _COMPONENT          ACPI_DISPATCHER  ACPI_MODULE_NAME("dsmethod")  /* Local prototypes */  static acpi_status +acpi_ds_detect_named_opcodes(struct acpi_walk_state *walk_state, +			     union acpi_parse_object **out_op); + +static acpi_status  acpi_ds_create_method_mutex(union acpi_operand_object *method_desc);  /*******************************************************************************   * + * FUNCTION:    acpi_ds_auto_serialize_method + * + * PARAMETERS:  node                        - Namespace Node of the method + *              obj_desc                    - Method object attached to node + * + * RETURN:      Status + * + * DESCRIPTION: Parse a control method AML to scan for control methods that + *              need serialization due to the creation of named objects. + * + * NOTE: It is a bit of overkill to mark all such methods serialized, since + * there is only a problem if the method actually blocks during execution. + * A blocking operation is, for example, a Sleep() operation, or any access + * to an operation region. However, it is probably not possible to easily + * detect whether a method will block or not, so we simply mark all suspicious + * methods as serialized. + * + * NOTE2: This code is essentially a generic routine for parsing a single + * control method. + * + ******************************************************************************/ + +acpi_status +acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, +			      union acpi_operand_object *obj_desc) +{ +	acpi_status status; +	union acpi_parse_object *op = NULL; +	struct acpi_walk_state *walk_state; + +	ACPI_FUNCTION_TRACE_PTR(ds_auto_serialize_method, node); + +	ACPI_DEBUG_PRINT((ACPI_DB_PARSE, +			  "Method auto-serialization parse [%4.4s] %p\n", +			  acpi_ut_get_node_name(node), node)); + +	/* Create/Init a root op for the method parse tree */ + +	op = acpi_ps_alloc_op(AML_METHOD_OP); +	if (!op) { +		return_ACPI_STATUS(AE_NO_MEMORY); +	} + +	acpi_ps_set_name(op, node->name.integer); +	op->common.node = node; + +	/* Create and initialize a new walk state */ + +	walk_state = +	    acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL); +	if (!walk_state) { +		return_ACPI_STATUS(AE_NO_MEMORY); +	} + +	status = +	    acpi_ds_init_aml_walk(walk_state, op, node, +				  obj_desc->method.aml_start, +				  obj_desc->method.aml_length, NULL, 0); +	if (ACPI_FAILURE(status)) { +		acpi_ds_delete_walk_state(walk_state); +		return_ACPI_STATUS(status); +	} + +	walk_state->descending_callback = acpi_ds_detect_named_opcodes; + +	/* Parse the method, scan for creation of named objects */ + +	status = acpi_ps_parse_aml(walk_state); +	if (ACPI_FAILURE(status)) { +		return_ACPI_STATUS(status); +	} + +	acpi_ps_delete_parse_tree(op); +	return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION:    acpi_ds_detect_named_opcodes + * + * PARAMETERS:  walk_state      - Current state of the parse tree walk + *              out_op          - Unused, required for parser interface + * + * RETURN:      Status + * + * DESCRIPTION: Descending callback used during the loading of ACPI tables. + *              Currently used to detect methods that must be marked serialized + *              in order to avoid problems with the creation of named objects. + * + ******************************************************************************/ + +static acpi_status +acpi_ds_detect_named_opcodes(struct acpi_walk_state *walk_state, +			     union acpi_parse_object **out_op) +{ + +	ACPI_FUNCTION_NAME(acpi_ds_detect_named_opcodes); + +	/* We are only interested in opcodes that create a new name */ + +	if (! +	    (walk_state->op_info-> +	     flags & (AML_NAMED | AML_CREATE | AML_FIELD))) { +		return (AE_OK); +	} + +	/* +	 * At this point, we know we have a Named object opcode. +	 * Mark the method as serialized. Later code will create a mutex for +	 * this method to enforce serialization. +	 * +	 * Note, ACPI_METHOD_IGNORE_SYNC_LEVEL flag means that we will ignore the +	 * Sync Level mechanism for this method, even though it is now serialized. +	 * Otherwise, there can be conflicts with existing ASL code that actually +	 * uses sync levels. +	 */ +	walk_state->method_desc->method.sync_level = 0; +	walk_state->method_desc->method.info_flags |= +	    (ACPI_METHOD_SERIALIZED | ACPI_METHOD_IGNORE_SYNC_LEVEL); + +	ACPI_DEBUG_PRINT((ACPI_DB_INFO, +			  "Method serialized [%4.4s] %p - [%s] (%4.4X)\n", +			  walk_state->method_node->name.ascii, +			  walk_state->method_node, walk_state->op_info->name, +			  walk_state->opcode)); + +	/* Abort the parse, no need to examine this method any further */ + +	return (AE_CTRL_TERMINATE); +} + +/******************************************************************************* + *   * FUNCTION:    acpi_ds_method_error   * - * PARAMETERS:  Status          - Execution status + * PARAMETERS:  status          - Execution status   *              walk_state      - Current state   *   * RETURN:      Status @@ -75,7 +213,7 @@ acpi_ds_create_method_mutex(union acpi_operand_object *method_desc);   ******************************************************************************/  acpi_status -acpi_ds_method_error(acpi_status status, struct acpi_walk_state *walk_state) +acpi_ds_method_error(acpi_status status, struct acpi_walk_state * walk_state)  {  	ACPI_FUNCTION_ENTRY(); @@ -152,6 +290,7 @@ acpi_ds_create_method_mutex(union acpi_operand_object *method_desc)  	status = acpi_os_create_mutex(&mutex_desc->mutex.os_mutex);  	if (ACPI_FAILURE(status)) { +		acpi_ut_delete_object_desc(mutex_desc);  		return_ACPI_STATUS(status);  	} @@ -171,7 +310,7 @@ acpi_ds_create_method_mutex(union acpi_operand_object *method_desc)   *   * RETURN:      Status   * - * DESCRIPTION: Prepare a method for execution.  Parses the method if necessary, + * DESCRIPTION: Prepare a method for execution. Parses the method if necessary,   *              increments the thread count, and waits at the method semaphore   *              for clearance to execute.   * @@ -201,7 +340,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 @@ -217,13 +356,19 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,  		/*  		 * The current_sync_level (per-thread) must be less than or equal to  		 * the sync level of the method. This mechanism provides some -		 * deadlock prevention +		 * deadlock prevention. +		 * +		 * If the method was auto-serialized, we just ignore the sync level +		 * mechanism, because auto-serialization of methods can interfere +		 * with ASL code that actually uses sync levels.  		 *  		 * Top-level method invocation has no walk state at this point  		 */  		if (walk_state && -		    (walk_state->thread->current_sync_level > -		     obj_desc->method.mutex->mutex.sync_level)) { +		    (!(obj_desc->method. +		       info_flags & ACPI_METHOD_IGNORE_SYNC_LEVEL)) +		    && (walk_state->thread->current_sync_level > +			obj_desc->method.mutex->mutex.sync_level)) {  			ACPI_ERROR((AE_INFO,  				    "Cannot acquire Mutex for method [%4.4s], current SyncLevel is too large (%u)",  				    acpi_ut_get_node_name(method_node), @@ -292,9 +437,10 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,  	 * reentered one more time (even if it is the same thread)  	 */  	obj_desc->method.thread_count++; +	acpi_method_count++;  	return_ACPI_STATUS(status); -      cleanup: +cleanup:  	/* On error, must release the method mutex (if present) */  	if (obj_desc->method.mutex) { @@ -307,9 +453,9 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,   *   * FUNCTION:    acpi_ds_call_control_method   * - * PARAMETERS:  Thread              - Info for this thread + * PARAMETERS:  thread              - Info for this thread   *              this_walk_state     - Current walk state - *              Op                  - Current Op to be walked + *              op                  - Current Op to be walked   *   * RETURN:      Status   * @@ -379,7 +525,8 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,  	 */  	info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));  	if (!info) { -		return_ACPI_STATUS(AE_NO_MEMORY); +		status = AE_NO_MEMORY; +		goto cleanup;  	}  	info->parameters = &this_walk_state->operands[0]; @@ -413,8 +560,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;  		} @@ -422,7 +570,7 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,  	return_ACPI_STATUS(status); -      cleanup: +cleanup:  	/* On error, we must terminate the method properly */ @@ -444,7 +592,7 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,   * RETURN:      Status   *   * DESCRIPTION: Restart a method that was preempted by another (nested) method - *              invocation.  Handle the return value (if any) from the callee. + *              invocation. Handle the return value (if any) from the callee.   *   ******************************************************************************/ @@ -530,7 +678,7 @@ acpi_ds_restart_control_method(struct acpi_walk_state *walk_state,   *   * RETURN:      None   * - * DESCRIPTION: Terminate a control method.  Delete everything that the method + * DESCRIPTION: Terminate a control method. Delete everything that the method   *              created, delete all locals and arguments, and delete the parse   *              tree if requested.   * @@ -579,11 +727,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 +744,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 +785,44 @@ 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.method_flags & AML_METHOD_SERIALIZED) -		    && (!method_desc->method.mutex)) { -			(void)acpi_ds_create_method_mutex(method_desc); +		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. +			 */ +			method_desc->method.info_flags &= +			    ~ACPI_METHOD_SERIALIZED_PENDING; +			method_desc->method.info_flags |= +			    (ACPI_METHOD_SERIALIZED | +			     ACPI_METHOD_IGNORE_SYNC_LEVEL); +			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);  		}  	}  | 
