![Lv Zheng](/assets/img/avatar_default.png)
ACPICA commit b2294cae776f5a66a7697414b21949d307e6856f This patch removes unwanted spaces for typedef. This solution doesn't cover function types. Note that the linuxize result of this commit is very giant and should have many conflicts against the current Linux upstream. Thus it is required to modify the linuxize result of this commit and the commits around it manually in order to have them merged to the Linux upstream. Since this is very costy, we should do this only once, and if we can't ensure to do this only once, we need to revert the Linux code to the wrong indentation result before merging the linuxize result of this commit. Lv Zheng. Link: https://github.com/acpica/acpica/commit/b2294cae Signed-off-by: Lv Zheng <lv.zheng@intel.com> Signed-off-by: Bob Moore <robert.moore@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
765 lines
22 KiB
C
765 lines
22 KiB
C
/*******************************************************************************
|
|
*
|
|
* Module Name: dbexec - debugger control method execution
|
|
*
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Copyright (C) 2000 - 2016, Intel Corp.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions, and the following disclaimer,
|
|
* without modification.
|
|
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
|
* substantially similar to the "NO WARRANTY" disclaimer below
|
|
* ("Disclaimer") and any redistribution must be conditioned upon
|
|
* including a substantially similar Disclaimer requirement for further
|
|
* binary redistribution.
|
|
* 3. Neither the names of the above-listed copyright holders nor the names
|
|
* of any contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of the
|
|
* GNU General Public License ("GPL") version 2 as published by the Free
|
|
* Software Foundation.
|
|
*
|
|
* NO WARRANTY
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGES.
|
|
*/
|
|
|
|
#include <acpi/acpi.h>
|
|
#include "accommon.h"
|
|
#include "acdebug.h"
|
|
#include "acnamesp.h"
|
|
|
|
#define _COMPONENT ACPI_CA_DEBUGGER
|
|
ACPI_MODULE_NAME("dbexec")
|
|
|
|
static struct acpi_db_method_info acpi_gbl_db_method_info;
|
|
|
|
/* Local prototypes */
|
|
|
|
static acpi_status
|
|
acpi_db_execute_method(struct acpi_db_method_info *info,
|
|
struct acpi_buffer *return_obj);
|
|
|
|
static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info);
|
|
|
|
static u32 acpi_db_get_outstanding_allocations(void);
|
|
|
|
static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context);
|
|
|
|
static acpi_status
|
|
acpi_db_execution_walk(acpi_handle obj_handle,
|
|
u32 nesting_level, void *context, void **return_value);
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FUNCTION: acpi_db_delete_objects
|
|
*
|
|
* PARAMETERS: count - Count of objects in the list
|
|
* objects - Array of ACPI_OBJECTs to be deleted
|
|
*
|
|
* RETURN: None
|
|
*
|
|
* DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested
|
|
* packages via recursion.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void acpi_db_delete_objects(u32 count, union acpi_object *objects)
|
|
{
|
|
u32 i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
switch (objects[i].type) {
|
|
case ACPI_TYPE_BUFFER:
|
|
|
|
ACPI_FREE(objects[i].buffer.pointer);
|
|
break;
|
|
|
|
case ACPI_TYPE_PACKAGE:
|
|
|
|
/* Recursive call to delete package elements */
|
|
|
|
acpi_db_delete_objects(objects[i].package.count,
|
|
objects[i].package.elements);
|
|
|
|
/* Free the elements array */
|
|
|
|
ACPI_FREE(objects[i].package.elements);
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FUNCTION: acpi_db_execute_method
|
|
*
|
|
* PARAMETERS: info - Valid info segment
|
|
* return_obj - Where to put return object
|
|
*
|
|
* RETURN: Status
|
|
*
|
|
* DESCRIPTION: Execute a control method.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static acpi_status
|
|
acpi_db_execute_method(struct acpi_db_method_info *info,
|
|
struct acpi_buffer *return_obj)
|
|
{
|
|
acpi_status status;
|
|
struct acpi_object_list param_objects;
|
|
union acpi_object params[ACPI_DEBUGGER_MAX_ARGS + 1];
|
|
u32 i;
|
|
|
|
ACPI_FUNCTION_TRACE(db_execute_method);
|
|
|
|
if (acpi_gbl_db_output_to_file && !acpi_dbg_level) {
|
|
acpi_os_printf("Warning: debug output is not enabled!\n");
|
|
}
|
|
|
|
param_objects.count = 0;
|
|
param_objects.pointer = NULL;
|
|
|
|
/* Pass through any command-line arguments */
|
|
|
|
if (info->args && info->args[0]) {
|
|
|
|
/* Get arguments passed on the command line */
|
|
|
|
for (i = 0; (info->args[i] && *(info->args[i])); i++) {
|
|
|
|
/* Convert input string (token) to an actual union acpi_object */
|
|
|
|
status = acpi_db_convert_to_object(info->types[i],
|
|
info->args[i],
|
|
¶ms[i]);
|
|
if (ACPI_FAILURE(status)) {
|
|
ACPI_EXCEPTION((AE_INFO, status,
|
|
"While parsing method arguments"));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
param_objects.count = i;
|
|
param_objects.pointer = params;
|
|
}
|
|
|
|
/* Prepare for a return object of arbitrary size */
|
|
|
|
return_obj->pointer = acpi_gbl_db_buffer;
|
|
return_obj->length = ACPI_DEBUG_BUFFER_SIZE;
|
|
|
|
/* Do the actual method execution */
|
|
|
|
acpi_gbl_method_executing = TRUE;
|
|
status = acpi_evaluate_object(NULL, info->pathname,
|
|
¶m_objects, return_obj);
|
|
|
|
acpi_gbl_cm_single_step = FALSE;
|
|
acpi_gbl_method_executing = FALSE;
|
|
|
|
if (ACPI_FAILURE(status)) {
|
|
ACPI_EXCEPTION((AE_INFO, status,
|
|
"while executing %s from debugger",
|
|
info->pathname));
|
|
|
|
if (status == AE_BUFFER_OVERFLOW) {
|
|
ACPI_ERROR((AE_INFO,
|
|
"Possible overflow of internal debugger "
|
|
"buffer (size 0x%X needed 0x%X)",
|
|
ACPI_DEBUG_BUFFER_SIZE,
|
|
(u32)return_obj->length));
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
acpi_db_delete_objects(param_objects.count, params);
|
|
return_ACPI_STATUS(status);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FUNCTION: acpi_db_execute_setup
|
|
*
|
|
* PARAMETERS: info - Valid method info
|
|
*
|
|
* RETURN: None
|
|
*
|
|
* DESCRIPTION: Setup info segment prior to method execution
|
|
*
|
|
******************************************************************************/
|
|
|
|
static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info)
|
|
{
|
|
acpi_status status;
|
|
|
|
ACPI_FUNCTION_NAME(db_execute_setup);
|
|
|
|
/* Catenate the current scope to the supplied name */
|
|
|
|
info->pathname[0] = 0;
|
|
if ((info->name[0] != '\\') && (info->name[0] != '/')) {
|
|
if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname),
|
|
acpi_gbl_db_scope_buf)) {
|
|
status = AE_BUFFER_OVERFLOW;
|
|
goto error_exit;
|
|
}
|
|
}
|
|
|
|
if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname),
|
|
info->name)) {
|
|
status = AE_BUFFER_OVERFLOW;
|
|
goto error_exit;
|
|
}
|
|
|
|
acpi_db_prep_namestring(info->pathname);
|
|
|
|
acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
|
|
acpi_os_printf("Evaluating %s\n", info->pathname);
|
|
|
|
if (info->flags & EX_SINGLE_STEP) {
|
|
acpi_gbl_cm_single_step = TRUE;
|
|
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
|
|
}
|
|
|
|
else {
|
|
/* No single step, allow redirection to a file */
|
|
|
|
acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT);
|
|
}
|
|
|
|
return (AE_OK);
|
|
|
|
error_exit:
|
|
|
|
ACPI_EXCEPTION((AE_INFO, status, "During setup for method execution"));
|
|
return (status);
|
|
}
|
|
|
|
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
|
|
u32 acpi_db_get_cache_info(struct acpi_memory_list *cache)
|
|
{
|
|
|
|
return (cache->total_allocated - cache->total_freed -
|
|
cache->current_depth);
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FUNCTION: acpi_db_get_outstanding_allocations
|
|
*
|
|
* PARAMETERS: None
|
|
*
|
|
* RETURN: Current global allocation count minus cache entries
|
|
*
|
|
* DESCRIPTION: Determine the current number of "outstanding" allocations --
|
|
* those allocations that have not been freed and also are not
|
|
* in one of the various object caches.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static u32 acpi_db_get_outstanding_allocations(void)
|
|
{
|
|
u32 outstanding = 0;
|
|
|
|
#ifdef ACPI_DBG_TRACK_ALLOCATIONS
|
|
|
|
outstanding += acpi_db_get_cache_info(acpi_gbl_state_cache);
|
|
outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_cache);
|
|
outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_ext_cache);
|
|
outstanding += acpi_db_get_cache_info(acpi_gbl_operand_cache);
|
|
#endif
|
|
|
|
return (outstanding);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FUNCTION: acpi_db_execution_walk
|
|
*
|
|
* PARAMETERS: WALK_CALLBACK
|
|
*
|
|
* RETURN: Status
|
|
*
|
|
* DESCRIPTION: Execute a control method. Name is relative to the current
|
|
* scope.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static acpi_status
|
|
acpi_db_execution_walk(acpi_handle obj_handle,
|
|
u32 nesting_level, void *context, void **return_value)
|
|
{
|
|
union acpi_operand_object *obj_desc;
|
|
struct acpi_namespace_node *node =
|
|
(struct acpi_namespace_node *)obj_handle;
|
|
struct acpi_buffer return_obj;
|
|
acpi_status status;
|
|
|
|
obj_desc = acpi_ns_get_attached_object(node);
|
|
if (obj_desc->method.param_count) {
|
|
return (AE_OK);
|
|
}
|
|
|
|
return_obj.pointer = NULL;
|
|
return_obj.length = ACPI_ALLOCATE_BUFFER;
|
|
|
|
acpi_ns_print_node_pathname(node, "Evaluating");
|
|
|
|
/* Do the actual method execution */
|
|
|
|
acpi_os_printf("\n");
|
|
acpi_gbl_method_executing = TRUE;
|
|
|
|
status = acpi_evaluate_object(node, NULL, NULL, &return_obj);
|
|
|
|
acpi_os_printf("Evaluation of [%4.4s] returned %s\n",
|
|
acpi_ut_get_node_name(node),
|
|
acpi_format_exception(status));
|
|
|
|
acpi_gbl_method_executing = FALSE;
|
|
return (AE_OK);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FUNCTION: acpi_db_execute
|
|
*
|
|
* PARAMETERS: name - Name of method to execute
|
|
* args - Parameters to the method
|
|
* Types -
|
|
* flags - single step/no single step
|
|
*
|
|
* RETURN: None
|
|
*
|
|
* DESCRIPTION: Execute a control method. Name is relative to the current
|
|
* scope.
|
|
*
|
|
******************************************************************************/
|
|
|
|
void
|
|
acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags)
|
|
{
|
|
acpi_status status;
|
|
struct acpi_buffer return_obj;
|
|
char *name_string;
|
|
|
|
#ifdef ACPI_DEBUG_OUTPUT
|
|
u32 previous_allocations;
|
|
u32 allocations;
|
|
#endif
|
|
|
|
/*
|
|
* Allow one execution to be performed by debugger or single step
|
|
* execution will be dead locked by the interpreter mutexes.
|
|
*/
|
|
if (acpi_gbl_method_executing) {
|
|
acpi_os_printf("Only one debugger execution is allowed.\n");
|
|
return;
|
|
}
|
|
#ifdef ACPI_DEBUG_OUTPUT
|
|
/* Memory allocation tracking */
|
|
|
|
previous_allocations = acpi_db_get_outstanding_allocations();
|
|
#endif
|
|
|
|
if (*name == '*') {
|
|
(void)acpi_walk_namespace(ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT,
|
|
ACPI_UINT32_MAX,
|
|
acpi_db_execution_walk, NULL, NULL,
|
|
NULL);
|
|
return;
|
|
} else {
|
|
name_string = ACPI_ALLOCATE(strlen(name) + 1);
|
|
if (!name_string) {
|
|
return;
|
|
}
|
|
|
|
memset(&acpi_gbl_db_method_info, 0,
|
|
sizeof(struct acpi_db_method_info));
|
|
|
|
strcpy(name_string, name);
|
|
acpi_ut_strupr(name_string);
|
|
acpi_gbl_db_method_info.name = name_string;
|
|
acpi_gbl_db_method_info.args = args;
|
|
acpi_gbl_db_method_info.types = types;
|
|
acpi_gbl_db_method_info.flags = flags;
|
|
|
|
return_obj.pointer = NULL;
|
|
return_obj.length = ACPI_ALLOCATE_BUFFER;
|
|
|
|
status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
|
|
if (ACPI_FAILURE(status)) {
|
|
ACPI_FREE(name_string);
|
|
return;
|
|
}
|
|
|
|
/* Get the NS node, determines existence also */
|
|
|
|
status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
|
|
&acpi_gbl_db_method_info.method);
|
|
if (ACPI_SUCCESS(status)) {
|
|
status =
|
|
acpi_db_execute_method(&acpi_gbl_db_method_info,
|
|
&return_obj);
|
|
}
|
|
ACPI_FREE(name_string);
|
|
}
|
|
|
|
/*
|
|
* Allow any handlers in separate threads to complete.
|
|
* (Such as Notify handlers invoked from AML executed above).
|
|
*/
|
|
acpi_os_sleep((u64)10);
|
|
|
|
#ifdef ACPI_DEBUG_OUTPUT
|
|
|
|
/* Memory allocation tracking */
|
|
|
|
allocations =
|
|
acpi_db_get_outstanding_allocations() - previous_allocations;
|
|
|
|
acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
|
|
|
|
if (allocations > 0) {
|
|
acpi_os_printf
|
|
("0x%X Outstanding allocations after evaluation of %s\n",
|
|
allocations, acpi_gbl_db_method_info.pathname);
|
|
}
|
|
#endif
|
|
|
|
if (ACPI_FAILURE(status)) {
|
|
acpi_os_printf("Evaluation of %s failed with status %s\n",
|
|
acpi_gbl_db_method_info.pathname,
|
|
acpi_format_exception(status));
|
|
} else {
|
|
/* Display a return object, if any */
|
|
|
|
if (return_obj.length) {
|
|
acpi_os_printf("Evaluation of %s returned object %p, "
|
|
"external buffer length %X\n",
|
|
acpi_gbl_db_method_info.pathname,
|
|
return_obj.pointer,
|
|
(u32)return_obj.length);
|
|
|
|
acpi_db_dump_external_object(return_obj.pointer, 1);
|
|
|
|
/* Dump a _PLD buffer if present */
|
|
|
|
if (ACPI_COMPARE_NAME
|
|
((ACPI_CAST_PTR
|
|
(struct acpi_namespace_node,
|
|
acpi_gbl_db_method_info.method)->name.ascii),
|
|
METHOD_NAME__PLD)) {
|
|
acpi_db_dump_pld_buffer(return_obj.pointer);
|
|
}
|
|
} else {
|
|
acpi_os_printf
|
|
("No object was returned from evaluation of %s\n",
|
|
acpi_gbl_db_method_info.pathname);
|
|
}
|
|
}
|
|
|
|
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FUNCTION: acpi_db_method_thread
|
|
*
|
|
* PARAMETERS: context - Execution info segment
|
|
*
|
|
* RETURN: None
|
|
*
|
|
* DESCRIPTION: Debugger execute thread. Waits for a command line, then
|
|
* simply dispatches it.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context)
|
|
{
|
|
acpi_status status;
|
|
struct acpi_db_method_info *info = context;
|
|
struct acpi_db_method_info local_info;
|
|
u32 i;
|
|
u8 allow;
|
|
struct acpi_buffer return_obj;
|
|
|
|
/*
|
|
* acpi_gbl_db_method_info.Arguments will be passed as method arguments.
|
|
* Prevent acpi_gbl_db_method_info from being modified by multiple threads
|
|
* concurrently.
|
|
*
|
|
* Note: The arguments we are passing are used by the ASL test suite
|
|
* (aslts). Do not change them without updating the tests.
|
|
*/
|
|
(void)acpi_os_wait_semaphore(info->info_gate, 1, ACPI_WAIT_FOREVER);
|
|
|
|
if (info->init_args) {
|
|
acpi_db_uint32_to_hex_string(info->num_created,
|
|
info->index_of_thread_str);
|
|
acpi_db_uint32_to_hex_string((u32)acpi_os_get_thread_id(),
|
|
info->id_of_thread_str);
|
|
}
|
|
|
|
if (info->threads && (info->num_created < info->num_threads)) {
|
|
info->threads[info->num_created++] = acpi_os_get_thread_id();
|
|
}
|
|
|
|
local_info = *info;
|
|
local_info.args = local_info.arguments;
|
|
local_info.arguments[0] = local_info.num_threads_str;
|
|
local_info.arguments[1] = local_info.id_of_thread_str;
|
|
local_info.arguments[2] = local_info.index_of_thread_str;
|
|
local_info.arguments[3] = NULL;
|
|
|
|
local_info.types = local_info.arg_types;
|
|
|
|
(void)acpi_os_signal_semaphore(info->info_gate, 1);
|
|
|
|
for (i = 0; i < info->num_loops; i++) {
|
|
status = acpi_db_execute_method(&local_info, &return_obj);
|
|
if (ACPI_FAILURE(status)) {
|
|
acpi_os_printf
|
|
("%s During evaluation of %s at iteration %X\n",
|
|
acpi_format_exception(status), info->pathname, i);
|
|
if (status == AE_ABORT_METHOD) {
|
|
break;
|
|
}
|
|
}
|
|
#if 0
|
|
if ((i % 100) == 0) {
|
|
acpi_os_printf("%u loops, Thread 0x%x\n",
|
|
i, acpi_os_get_thread_id());
|
|
}
|
|
|
|
if (return_obj.length) {
|
|
acpi_os_printf
|
|
("Evaluation of %s returned object %p Buflen %X\n",
|
|
info->pathname, return_obj.pointer,
|
|
(u32)return_obj.length);
|
|
acpi_db_dump_external_object(return_obj.pointer, 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Signal our completion */
|
|
|
|
allow = 0;
|
|
(void)acpi_os_wait_semaphore(info->thread_complete_gate,
|
|
1, ACPI_WAIT_FOREVER);
|
|
info->num_completed++;
|
|
|
|
if (info->num_completed == info->num_threads) {
|
|
|
|
/* Do signal for main thread once only */
|
|
allow = 1;
|
|
}
|
|
|
|
(void)acpi_os_signal_semaphore(info->thread_complete_gate, 1);
|
|
|
|
if (allow) {
|
|
status = acpi_os_signal_semaphore(info->main_thread_gate, 1);
|
|
if (ACPI_FAILURE(status)) {
|
|
acpi_os_printf
|
|
("Could not signal debugger thread sync semaphore, %s\n",
|
|
acpi_format_exception(status));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* FUNCTION: acpi_db_create_execution_threads
|
|
*
|
|
* PARAMETERS: num_threads_arg - Number of threads to create
|
|
* num_loops_arg - Loop count for the thread(s)
|
|
* method_name_arg - Control method to execute
|
|
*
|
|
* RETURN: None
|
|
*
|
|
* DESCRIPTION: Create threads to execute method(s)
|
|
*
|
|
******************************************************************************/
|
|
|
|
void
|
|
acpi_db_create_execution_threads(char *num_threads_arg,
|
|
char *num_loops_arg, char *method_name_arg)
|
|
{
|
|
acpi_status status;
|
|
u32 num_threads;
|
|
u32 num_loops;
|
|
u32 i;
|
|
u32 size;
|
|
acpi_mutex main_thread_gate;
|
|
acpi_mutex thread_complete_gate;
|
|
acpi_mutex info_gate;
|
|
|
|
/* Get the arguments */
|
|
|
|
num_threads = strtoul(num_threads_arg, NULL, 0);
|
|
num_loops = strtoul(num_loops_arg, NULL, 0);
|
|
|
|
if (!num_threads || !num_loops) {
|
|
acpi_os_printf("Bad argument: Threads %X, Loops %X\n",
|
|
num_threads, num_loops);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Create the semaphore for synchronization of
|
|
* the created threads with the main thread.
|
|
*/
|
|
status = acpi_os_create_semaphore(1, 0, &main_thread_gate);
|
|
if (ACPI_FAILURE(status)) {
|
|
acpi_os_printf("Could not create semaphore for "
|
|
"synchronization with the main thread, %s\n",
|
|
acpi_format_exception(status));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Create the semaphore for synchronization
|
|
* between the created threads.
|
|
*/
|
|
status = acpi_os_create_semaphore(1, 1, &thread_complete_gate);
|
|
if (ACPI_FAILURE(status)) {
|
|
acpi_os_printf("Could not create semaphore for "
|
|
"synchronization between the created threads, %s\n",
|
|
acpi_format_exception(status));
|
|
|
|
(void)acpi_os_delete_semaphore(main_thread_gate);
|
|
return;
|
|
}
|
|
|
|
status = acpi_os_create_semaphore(1, 1, &info_gate);
|
|
if (ACPI_FAILURE(status)) {
|
|
acpi_os_printf("Could not create semaphore for "
|
|
"synchronization of AcpiGbl_DbMethodInfo, %s\n",
|
|
acpi_format_exception(status));
|
|
|
|
(void)acpi_os_delete_semaphore(thread_complete_gate);
|
|
(void)acpi_os_delete_semaphore(main_thread_gate);
|
|
return;
|
|
}
|
|
|
|
memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
|
|
|
|
/* Array to store IDs of threads */
|
|
|
|
acpi_gbl_db_method_info.num_threads = num_threads;
|
|
size = sizeof(acpi_thread_id) * acpi_gbl_db_method_info.num_threads;
|
|
|
|
acpi_gbl_db_method_info.threads = acpi_os_allocate(size);
|
|
if (acpi_gbl_db_method_info.threads == NULL) {
|
|
acpi_os_printf("No memory for thread IDs array\n");
|
|
(void)acpi_os_delete_semaphore(main_thread_gate);
|
|
(void)acpi_os_delete_semaphore(thread_complete_gate);
|
|
(void)acpi_os_delete_semaphore(info_gate);
|
|
return;
|
|
}
|
|
memset(acpi_gbl_db_method_info.threads, 0, size);
|
|
|
|
/* Setup the context to be passed to each thread */
|
|
|
|
acpi_gbl_db_method_info.name = method_name_arg;
|
|
acpi_gbl_db_method_info.flags = 0;
|
|
acpi_gbl_db_method_info.num_loops = num_loops;
|
|
acpi_gbl_db_method_info.main_thread_gate = main_thread_gate;
|
|
acpi_gbl_db_method_info.thread_complete_gate = thread_complete_gate;
|
|
acpi_gbl_db_method_info.info_gate = info_gate;
|
|
|
|
/* Init arguments to be passed to method */
|
|
|
|
acpi_gbl_db_method_info.init_args = 1;
|
|
acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments;
|
|
acpi_gbl_db_method_info.arguments[0] =
|
|
acpi_gbl_db_method_info.num_threads_str;
|
|
acpi_gbl_db_method_info.arguments[1] =
|
|
acpi_gbl_db_method_info.id_of_thread_str;
|
|
acpi_gbl_db_method_info.arguments[2] =
|
|
acpi_gbl_db_method_info.index_of_thread_str;
|
|
acpi_gbl_db_method_info.arguments[3] = NULL;
|
|
|
|
acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types;
|
|
acpi_gbl_db_method_info.arg_types[0] = ACPI_TYPE_INTEGER;
|
|
acpi_gbl_db_method_info.arg_types[1] = ACPI_TYPE_INTEGER;
|
|
acpi_gbl_db_method_info.arg_types[2] = ACPI_TYPE_INTEGER;
|
|
|
|
acpi_db_uint32_to_hex_string(num_threads,
|
|
acpi_gbl_db_method_info.num_threads_str);
|
|
|
|
status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
|
|
if (ACPI_FAILURE(status)) {
|
|
goto cleanup_and_exit;
|
|
}
|
|
|
|
/* Get the NS node, determines existence also */
|
|
|
|
status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
|
|
&acpi_gbl_db_method_info.method);
|
|
if (ACPI_FAILURE(status)) {
|
|
acpi_os_printf("%s Could not get handle for %s\n",
|
|
acpi_format_exception(status),
|
|
acpi_gbl_db_method_info.pathname);
|
|
goto cleanup_and_exit;
|
|
}
|
|
|
|
/* Create the threads */
|
|
|
|
acpi_os_printf("Creating %X threads to execute %X times each\n",
|
|
num_threads, num_loops);
|
|
|
|
for (i = 0; i < (num_threads); i++) {
|
|
status =
|
|
acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD,
|
|
acpi_db_method_thread,
|
|
&acpi_gbl_db_method_info);
|
|
if (ACPI_FAILURE(status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Wait for all threads to complete */
|
|
|
|
(void)acpi_os_wait_semaphore(main_thread_gate, 1, ACPI_WAIT_FOREVER);
|
|
|
|
acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
|
|
acpi_os_printf("All threads (%X) have completed\n", num_threads);
|
|
acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
|
|
|
|
cleanup_and_exit:
|
|
|
|
/* Cleanup and exit */
|
|
|
|
(void)acpi_os_delete_semaphore(main_thread_gate);
|
|
(void)acpi_os_delete_semaphore(thread_complete_gate);
|
|
(void)acpi_os_delete_semaphore(info_gate);
|
|
|
|
acpi_os_free(acpi_gbl_db_method_info.threads);
|
|
acpi_gbl_db_method_info.threads = NULL;
|
|
}
|