Skip to content

Commit

Permalink
ACPICA: Fix AcpiWalkNamespace race condition with table unload
Browse files Browse the repository at this point in the history
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=749

Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
  • Loading branch information
Bob Moore authored and Len Brown committed Mar 27, 2009
1 parent aab61b6 commit 8a335a2
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 33 deletions.
2 changes: 1 addition & 1 deletion drivers/acpi/acpica/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
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
4 changes: 4 additions & 0 deletions drivers/acpi/acpica/acglobal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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
Expand Down
8 changes: 8 additions & 0 deletions drivers/acpi/acpica/aclocal.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = {
#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.
* These spinlocks are created by acpi_ut_mutex_initialize
Expand Down
2 changes: 1 addition & 1 deletion drivers/acpi/acpica/actables.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void acpi_tb_delete_table(struct acpi_table_desc *table_desc);

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);

Expand Down
15 changes: 15 additions & 0 deletions drivers/acpi/acpica/acutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,21 @@ acpi_ut_execute_UID(struct acpi_namespace_node *device_node,
acpi_status
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
*/
Expand Down
16 changes: 14 additions & 2 deletions drivers/acpi/acpica/dsinit.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,23 @@ acpi_ds_initialize_objects(u32 table_index,

/* Walk entire namespace from the supplied root */

status = acpi_walk_namespace(ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX,
acpi_ds_init_one_object, &info, NULL);
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
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)) {
ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace"));
}
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);

status = acpi_get_table_by_index(table_index, &table);
if (ACPI_FAILURE(status)) {
Expand Down
13 changes: 7 additions & 6 deletions drivers/acpi/acpica/exconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,13 +520,14 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
}
}

/*
* Delete the entire namespace under this table Node
* (Offset contains the table_id)
*/
acpi_tb_delete_namespace_by_owner(table_index);
(void)acpi_tb_release_owner_id(table_index);
/* Delete the portion of the namespace owned by this table */

status = acpi_tb_delete_namespace_by_owner(table_index);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}

(void)acpi_tb_release_owner_id(table_index);
acpi_tb_set_table_loaded_flag(table_index, FALSE);

/* Table unloaded, remove a reference to the ddb_handle object */
Expand Down
33 changes: 26 additions & 7 deletions drivers/acpi/acpica/nsxfeval.c
Original file line number Diff line number Diff line change
Expand Up @@ -475,21 +475,40 @@ acpi_walk_namespace(acpi_object_type type,
}

/*
* Lock the namespace around the walk.
* The namespace will be unlocked/locked around each call
* to the user function - since this function
* must be allowed to make Acpi calls itself.
* Need to acquire the namespace reader lock to prevent interference
* with any concurrent table unloads (which causes the deletion of
* namespace objects). We cannot allow the deletion of a namespace node
* 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);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
goto unlock_and_exit;
}

status = acpi_ns_walk_namespace(type, start_object, max_depth,
ACPI_NS_WALK_UNLOCK,
user_function, context, return_value);
ACPI_NS_WALK_UNLOCK, user_function,
context, return_value);

(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);
}

Expand Down
45 changes: 37 additions & 8 deletions drivers/acpi/acpica/tbinstal.c
Original file line number Diff line number Diff line change
Expand Up @@ -434,27 +434,56 @@ void acpi_tb_terminate(void)
*
* PARAMETERS: table_index - Table index
*
* RETURN: None
* RETURN: Status
*
* 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_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);
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);

/*
* 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);
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);
}

/*******************************************************************************
Expand Down
175 changes: 175 additions & 0 deletions drivers/acpi/acpica/utlock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/******************************************************************************
*
* 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);
}
Loading

0 comments on commit 8a335a2

Please sign in to comment.