提交 8a335a23 编写于 作者: B Bob Moore 提交者: Len Brown

ACPICA: Fix AcpiWalkNamespace race condition with table unload

Added a reader/writer locking mechanism to allow multiple
concurrent namespace walks (readers), but a dynamic table unload
will have exclusive access to the namespace. This fixes a problem
where a table unload could delete the portion of the namespace that
is currently being examined by a walk.  Adds a new file, utlock.c
that implements the reader/writer lock mechanism. ACPICA BZ 749.

http://www.acpica.org/bugzilla/show_bug.cgi?id=749Signed-off-by: NBob Moore <robert.moore@intel.com>
Signed-off-by: NLin Ming <ming.m.lin@intel.com>
Signed-off-by: NLen Brown <len.brown@intel.com>
上级 aab61b67
...@@ -41,4 +41,4 @@ obj-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o ...@@ -41,4 +41,4 @@ obj-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o
obj-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ obj-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \
utcopy.o utdelete.o utglobal.o utmath.o utobject.o \ utcopy.o utdelete.o utglobal.o utmath.o utobject.o \
utstate.o utmutex.o utobject.o utresrc.o utstate.o utmutex.o utobject.o utresrc.o utlock.o
...@@ -165,6 +165,10 @@ ACPI_EXTERN u8 acpi_gbl_integer_bit_width; ...@@ -165,6 +165,10 @@ ACPI_EXTERN u8 acpi_gbl_integer_bit_width;
ACPI_EXTERN u8 acpi_gbl_integer_byte_width; ACPI_EXTERN u8 acpi_gbl_integer_byte_width;
ACPI_EXTERN u8 acpi_gbl_integer_nybble_width; ACPI_EXTERN u8 acpi_gbl_integer_nybble_width;
/* Reader/Writer lock is used for namespace walk and dynamic table unload */
ACPI_EXTERN struct acpi_rw_lock acpi_gbl_namespace_rw_lock;
/***************************************************************************** /*****************************************************************************
* *
* Mutual exlusion within ACPICA subsystem * Mutual exlusion within ACPICA subsystem
......
...@@ -108,6 +108,14 @@ static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = { ...@@ -108,6 +108,14 @@ static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = {
#endif #endif
#endif #endif
/* Lock structure for reader/writer interfaces */
struct acpi_rw_lock {
acpi_mutex writer_mutex;
acpi_mutex reader_mutex;
u32 num_readers;
};
/* /*
* Predefined handles for spinlocks used within the subsystem. * Predefined handles for spinlocks used within the subsystem.
* These spinlocks are created by acpi_ut_mutex_initialize * These spinlocks are created by acpi_ut_mutex_initialize
......
...@@ -79,7 +79,7 @@ void acpi_tb_delete_table(struct acpi_table_desc *table_desc); ...@@ -79,7 +79,7 @@ void acpi_tb_delete_table(struct acpi_table_desc *table_desc);
void acpi_tb_terminate(void); void acpi_tb_terminate(void);
void acpi_tb_delete_namespace_by_owner(u32 table_index); acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index);
acpi_status acpi_tb_allocate_owner_id(u32 table_index); acpi_status acpi_tb_allocate_owner_id(u32 table_index);
......
...@@ -345,6 +345,21 @@ acpi_ut_execute_UID(struct acpi_namespace_node *device_node, ...@@ -345,6 +345,21 @@ acpi_ut_execute_UID(struct acpi_namespace_node *device_node,
acpi_status acpi_status
acpi_ut_execute_sxds(struct acpi_namespace_node *device_node, u8 * highest); acpi_ut_execute_sxds(struct acpi_namespace_node *device_node, u8 * highest);
/*
* utlock - reader/writer locks
*/
acpi_status acpi_ut_create_rw_lock(struct acpi_rw_lock *lock);
void acpi_ut_delete_rw_lock(struct acpi_rw_lock *lock);
acpi_status acpi_ut_acquire_read_lock(struct acpi_rw_lock *lock);
acpi_status acpi_ut_release_read_lock(struct acpi_rw_lock *lock);
acpi_status acpi_ut_acquire_write_lock(struct acpi_rw_lock *lock);
void acpi_ut_release_write_lock(struct acpi_rw_lock *lock);
/* /*
* utobject - internal object create/delete/cache routines * utobject - internal object create/delete/cache routines
*/ */
......
...@@ -180,11 +180,23 @@ acpi_ds_initialize_objects(u32 table_index, ...@@ -180,11 +180,23 @@ acpi_ds_initialize_objects(u32 table_index,
/* Walk entire namespace from the supplied root */ /* Walk entire namespace from the supplied root */
status = acpi_walk_namespace(ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX, status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
acpi_ds_init_one_object, &info, NULL); if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* We don't use acpi_walk_namespace since we do not want to acquire
* the namespace reader lock.
*/
status =
acpi_ns_walk_namespace(ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX,
ACPI_NS_WALK_UNLOCK, acpi_ds_init_one_object,
&info, NULL);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace")); ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace"));
} }
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
status = acpi_get_table_by_index(table_index, &table); status = acpi_get_table_by_index(table_index, &table);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
......
...@@ -520,13 +520,14 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) ...@@ -520,13 +520,14 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
} }
} }
/* /* Delete the portion of the namespace owned by this table */
* Delete the entire namespace under this table Node
* (Offset contains the table_id) status = acpi_tb_delete_namespace_by_owner(table_index);
*/ if (ACPI_FAILURE(status)) {
acpi_tb_delete_namespace_by_owner(table_index); return_ACPI_STATUS(status);
(void)acpi_tb_release_owner_id(table_index); }
(void)acpi_tb_release_owner_id(table_index);
acpi_tb_set_table_loaded_flag(table_index, FALSE); acpi_tb_set_table_loaded_flag(table_index, FALSE);
/* Table unloaded, remove a reference to the ddb_handle object */ /* Table unloaded, remove a reference to the ddb_handle object */
......
...@@ -475,21 +475,40 @@ acpi_walk_namespace(acpi_object_type type, ...@@ -475,21 +475,40 @@ acpi_walk_namespace(acpi_object_type type,
} }
/* /*
* Lock the namespace around the walk. * Need to acquire the namespace reader lock to prevent interference
* The namespace will be unlocked/locked around each call * with any concurrent table unloads (which causes the deletion of
* to the user function - since this function * namespace objects). We cannot allow the deletion of a namespace node
* must be allowed to make Acpi calls itself. * while the user function is using it. The exception to this are the
* nodes created and deleted during control method execution -- these
* nodes are marked as temporary nodes and are ignored by the namespace
* walk. Thus, control methods can be executed while holding the
* namespace deletion lock (and the user function can execute control
* methods.)
*/
status = acpi_ut_acquire_read_lock(&acpi_gbl_namespace_rw_lock);
if (ACPI_FAILURE(status)) {
return status;
}
/*
* Lock the namespace around the walk. The namespace will be
* unlocked/locked around each call to the user function - since the user
* function must be allowed to make ACPICA calls itself (for example, it
* will typically execute control methods during device enumeration.)
*/ */
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); goto unlock_and_exit;
} }
status = acpi_ns_walk_namespace(type, start_object, max_depth, status = acpi_ns_walk_namespace(type, start_object, max_depth,
ACPI_NS_WALK_UNLOCK, ACPI_NS_WALK_UNLOCK, user_function,
user_function, context, return_value); context, return_value);
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
unlock_and_exit:
(void)acpi_ut_release_read_lock(&acpi_gbl_namespace_rw_lock);
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
......
...@@ -434,27 +434,56 @@ void acpi_tb_terminate(void) ...@@ -434,27 +434,56 @@ void acpi_tb_terminate(void)
* *
* PARAMETERS: table_index - Table index * PARAMETERS: table_index - Table index
* *
* RETURN: None * RETURN: Status
* *
* DESCRIPTION: Delete all namespace objects created when this table was loaded. * DESCRIPTION: Delete all namespace objects created when this table was loaded.
* *
******************************************************************************/ ******************************************************************************/
void acpi_tb_delete_namespace_by_owner(u32 table_index) acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index)
{ {
acpi_owner_id owner_id; acpi_owner_id owner_id;
acpi_status status;
ACPI_FUNCTION_TRACE(tb_delete_namespace_by_owner);
status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
if (table_index >= acpi_gbl_root_table_list.count) {
/* The table index does not exist */
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (table_index < acpi_gbl_root_table_list.count) {
owner_id =
acpi_gbl_root_table_list.tables[table_index].owner_id;
} else {
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return; return_ACPI_STATUS(AE_NOT_EXIST);
} }
/* Get the owner ID for this table, used to delete namespace nodes */
owner_id = acpi_gbl_root_table_list.tables[table_index].owner_id;
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
/*
* Need to acquire the namespace writer lock to prevent interference
* with any concurrent namespace walks. The interpreter must be
* released during the deletion since the acquisition of the deletion
* lock may block, and also since the execution of a namespace walk
* must be allowed to use the interpreter.
*/
acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock);
acpi_ns_delete_namespace_by_owner(owner_id); acpi_ns_delete_namespace_by_owner(owner_id);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock);
status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
return_ACPI_STATUS(status);
} }
/******************************************************************************* /*******************************************************************************
......
/******************************************************************************
*
* Module Name: utlock - Reader/Writer lock interfaces
*
*****************************************************************************/
/*
* Copyright (C) 2000 - 2009, 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"
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME("utlock")
/*******************************************************************************
*
* FUNCTION: acpi_ut_create_rw_lock
* acpi_ut_delete_rw_lock
*
* PARAMETERS: Lock - Pointer to a valid RW lock
*
* RETURN: Status
*
* DESCRIPTION: Reader/writer lock creation and deletion interfaces.
*
******************************************************************************/
acpi_status acpi_ut_create_rw_lock(struct acpi_rw_lock *lock)
{
acpi_status status;
lock->num_readers = 0;
status = acpi_os_create_mutex(&lock->reader_mutex);
if (ACPI_FAILURE(status)) {
return status;
}
status = acpi_os_create_mutex(&lock->writer_mutex);
return status;
}
void acpi_ut_delete_rw_lock(struct acpi_rw_lock *lock)
{
acpi_os_delete_mutex(lock->reader_mutex);
acpi_os_delete_mutex(lock->writer_mutex);
lock->num_readers = 0;
lock->reader_mutex = NULL;
lock->writer_mutex = NULL;
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_acquire_read_lock
* acpi_ut_release_read_lock
*
* PARAMETERS: Lock - Pointer to a valid RW lock
*
* RETURN: Status
*
* DESCRIPTION: Reader interfaces for reader/writer locks. On acquisition,
* only the first reader acquires the write mutex. On release,
* only the last reader releases the write mutex. Although this
* algorithm can in theory starve writers, this should not be a
* problem with ACPICA since the subsystem is infrequently used
* in comparison to (for example) an I/O system.
*
******************************************************************************/
acpi_status acpi_ut_acquire_read_lock(struct acpi_rw_lock *lock)
{
acpi_status status;
status = acpi_os_acquire_mutex(lock->reader_mutex, ACPI_WAIT_FOREVER);
if (ACPI_FAILURE(status)) {
return status;
}
/* Acquire the write lock only for the first reader */
lock->num_readers++;
if (lock->num_readers == 1) {
status =
acpi_os_acquire_mutex(lock->writer_mutex,
ACPI_WAIT_FOREVER);
}
acpi_os_release_mutex(lock->reader_mutex);
return status;
}
acpi_status acpi_ut_release_read_lock(struct acpi_rw_lock *lock)
{
acpi_status status;
status = acpi_os_acquire_mutex(lock->reader_mutex, ACPI_WAIT_FOREVER);
if (ACPI_FAILURE(status)) {
return status;
}
/* Release the write lock only for the very last reader */
lock->num_readers--;
if (lock->num_readers == 0) {
acpi_os_release_mutex(lock->writer_mutex);
}
acpi_os_release_mutex(lock->reader_mutex);
return status;
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_acquire_write_lock
* acpi_ut_release_write_lock
*
* PARAMETERS: Lock - Pointer to a valid RW lock
*
* RETURN: Status
*
* DESCRIPTION: Writer interfaces for reader/writer locks. Simply acquire or
* release the writer mutex associated with the lock. Acquisition
* of the lock is fully exclusive and will block all readers and
* writers until it is released.
*
******************************************************************************/
acpi_status acpi_ut_acquire_write_lock(struct acpi_rw_lock *lock)
{
acpi_status status;
status = acpi_os_acquire_mutex(lock->writer_mutex, ACPI_WAIT_FOREVER);
return status;
}
void acpi_ut_release_write_lock(struct acpi_rw_lock *lock)
{
acpi_os_release_mutex(lock->writer_mutex);
}
...@@ -60,7 +60,8 @@ static acpi_status acpi_ut_delete_mutex(acpi_mutex_handle mutex_id); ...@@ -60,7 +60,8 @@ static acpi_status acpi_ut_delete_mutex(acpi_mutex_handle mutex_id);
* *
* RETURN: Status * RETURN: Status
* *
* DESCRIPTION: Create the system mutex objects. * DESCRIPTION: Create the system mutex objects. This includes mutexes,
* spin locks, and reader/writer locks.
* *
******************************************************************************/ ******************************************************************************/
...@@ -71,9 +72,8 @@ acpi_status acpi_ut_mutex_initialize(void) ...@@ -71,9 +72,8 @@ acpi_status acpi_ut_mutex_initialize(void)
ACPI_FUNCTION_TRACE(ut_mutex_initialize); ACPI_FUNCTION_TRACE(ut_mutex_initialize);
/* /* Create each of the predefined mutex objects */
* Create each of the predefined mutex objects
*/
for (i = 0; i < ACPI_NUM_MUTEX; i++) { for (i = 0; i < ACPI_NUM_MUTEX; i++) {
status = acpi_ut_create_mutex(i); status = acpi_ut_create_mutex(i);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
...@@ -86,6 +86,9 @@ acpi_status acpi_ut_mutex_initialize(void) ...@@ -86,6 +86,9 @@ acpi_status acpi_ut_mutex_initialize(void)
spin_lock_init(acpi_gbl_gpe_lock); spin_lock_init(acpi_gbl_gpe_lock);
spin_lock_init(acpi_gbl_hardware_lock); spin_lock_init(acpi_gbl_hardware_lock);
/* Create the reader/writer lock for namespace access */
status = acpi_ut_create_rw_lock(&acpi_gbl_namespace_rw_lock);
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
...@@ -97,7 +100,8 @@ acpi_status acpi_ut_mutex_initialize(void) ...@@ -97,7 +100,8 @@ acpi_status acpi_ut_mutex_initialize(void)
* *
* RETURN: None. * RETURN: None.
* *
* DESCRIPTION: Delete all of the system mutex objects. * DESCRIPTION: Delete all of the system mutex objects. This includes mutexes,
* spin locks, and reader/writer locks.
* *
******************************************************************************/ ******************************************************************************/
...@@ -107,9 +111,8 @@ void acpi_ut_mutex_terminate(void) ...@@ -107,9 +111,8 @@ void acpi_ut_mutex_terminate(void)
ACPI_FUNCTION_TRACE(ut_mutex_terminate); ACPI_FUNCTION_TRACE(ut_mutex_terminate);
/* /* Delete each predefined mutex object */
* Delete each predefined mutex object
*/
for (i = 0; i < ACPI_NUM_MUTEX; i++) { for (i = 0; i < ACPI_NUM_MUTEX; i++) {
(void)acpi_ut_delete_mutex(i); (void)acpi_ut_delete_mutex(i);
} }
...@@ -118,6 +121,10 @@ void acpi_ut_mutex_terminate(void) ...@@ -118,6 +121,10 @@ void acpi_ut_mutex_terminate(void)
acpi_os_delete_lock(acpi_gbl_gpe_lock); acpi_os_delete_lock(acpi_gbl_gpe_lock);
acpi_os_delete_lock(acpi_gbl_hardware_lock); acpi_os_delete_lock(acpi_gbl_hardware_lock);
/* Delete the reader/writer lock */
acpi_ut_delete_rw_lock(&acpi_gbl_namespace_rw_lock);
return_VOID; return_VOID;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册