X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Facpi%2Fnamespace%2Fnsalloc.c;h=9b871f38b61b81ff2480c951aae6095d6a13e1fd;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=55b407aae26612372d52f61867d50226f129e673;hpb=41689045f6a3cbe0550e1d34e9cc20d2e8c432ba;p=linux-2.6.git diff --git a/drivers/acpi/namespace/nsalloc.c b/drivers/acpi/namespace/nsalloc.c index 55b407aae..9b871f38b 100644 --- a/drivers/acpi/namespace/nsalloc.c +++ b/drivers/acpi/namespace/nsalloc.c @@ -47,6 +47,9 @@ #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsalloc") +/* Local prototypes */ +static void acpi_ns_remove_reference(struct acpi_namespace_node *node); + /******************************************************************************* * * FUNCTION: acpi_ns_create_node @@ -58,13 +61,14 @@ ACPI_MODULE_NAME("nsalloc") * DESCRIPTION: Create a namespace node * ******************************************************************************/ + struct acpi_namespace_node *acpi_ns_create_node(u32 name) { struct acpi_namespace_node *node; - ACPI_FUNCTION_TRACE(ns_create_node); + ACPI_FUNCTION_TRACE("ns_create_node"); - node = acpi_os_acquire_object(acpi_gbl_namespace_cache); + node = ACPI_MEM_CALLOCATE(sizeof(struct acpi_namespace_node)); if (!node) { return_PTR(NULL); } @@ -72,7 +76,9 @@ struct acpi_namespace_node *acpi_ns_create_node(u32 name) ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_allocated++); node->name.integer = name; + node->reference_count = 1; ACPI_SET_DESCRIPTOR_TYPE(node, ACPI_DESC_TYPE_NAMED); + return_PTR(node); } @@ -94,7 +100,7 @@ void acpi_ns_delete_node(struct acpi_namespace_node *node) struct acpi_namespace_node *prev_node; struct acpi_namespace_node *next_node; - ACPI_FUNCTION_TRACE_PTR(ns_delete_node, node); + ACPI_FUNCTION_TRACE_PTR("ns_delete_node", node); parent_node = acpi_ns_get_parent_node(node); @@ -109,7 +115,6 @@ void acpi_ns_delete_node(struct acpi_namespace_node *node) } if (prev_node) { - /* Node is not first child, unlink it */ prev_node->peer = next_node->peer; @@ -120,7 +125,6 @@ void acpi_ns_delete_node(struct acpi_namespace_node *node) /* Node is first child (has no previous peer) */ if (next_node->flags & ANOBJ_END_OF_PEER_LIST) { - /* No peers at all */ parent_node->child = NULL; @@ -133,10 +137,10 @@ void acpi_ns_delete_node(struct acpi_namespace_node *node) ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++); /* - * Detach an object if there is one, then delete the node + * Detach an object if there is one then delete the node */ acpi_ns_detach_object(node); - (void)acpi_os_release_object(acpi_gbl_namespace_cache, node); + ACPI_MEM_FREE(node); return_VOID; } @@ -167,7 +171,7 @@ void acpi_ns_install_node(struct acpi_walk_state *walk_state, struct acpi_namesp acpi_owner_id owner_id = 0; struct acpi_namespace_node *child_node; - ACPI_FUNCTION_TRACE(ns_install_node); + ACPI_FUNCTION_TRACE("ns_install_node"); /* * Get the owner ID from the Walk state @@ -212,6 +216,14 @@ void acpi_ns_install_node(struct acpi_walk_state *walk_state, struct acpi_namesp acpi_ut_get_type_name(parent_node->type), parent_node)); + /* + * Increment the reference count(s) of all parents up to + * the root! + */ + while ((node = acpi_ns_get_parent_node(node)) != NULL) { + node->reference_count++; + } + return_VOID; } @@ -232,9 +244,10 @@ void acpi_ns_delete_children(struct acpi_namespace_node *parent_node) { struct acpi_namespace_node *child_node; struct acpi_namespace_node *next_node; + struct acpi_namespace_node *node; u8 flags; - ACPI_FUNCTION_TRACE_PTR(ns_delete_children, parent_node); + ACPI_FUNCTION_TRACE_PTR("ns_delete_children", parent_node); if (!parent_node) { return_VOID; @@ -251,7 +264,6 @@ void acpi_ns_delete_children(struct acpi_namespace_node *parent_node) * Deallocate all children at this level */ do { - /* Get the things we need */ next_node = child_node->peer; @@ -277,10 +289,26 @@ void acpi_ns_delete_children(struct acpi_namespace_node *parent_node) */ acpi_ns_detach_object(child_node); + /* + * Decrement the reference count(s) of all parents up to + * the root! (counts were incremented when the node was created) + */ + node = child_node; + while ((node = acpi_ns_get_parent_node(node)) != NULL) { + node->reference_count--; + } + + /* There should be only one reference remaining on this node */ + + if (child_node->reference_count != 1) { + ACPI_WARNING((AE_INFO, + "Existing references (%d) on node being deleted (%p)", + child_node->reference_count, child_node)); + } + /* Now we can delete the node */ - (void)acpi_os_release_object(acpi_gbl_namespace_cache, - child_node); + ACPI_MEM_FREE(child_node); /* And move on to the next child in the list */ @@ -313,7 +341,7 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) struct acpi_namespace_node *child_node = NULL; u32 level = 1; - ACPI_FUNCTION_TRACE(ns_delete_namespace_subtree); + ACPI_FUNCTION_TRACE("ns_delete_namespace_subtree"); if (!parent_node) { return_VOID; @@ -324,14 +352,11 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) * to where we started. */ while (level > 0) { - /* Get the next node in this scope (NULL if none) */ - child_node = - acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node, - child_node); + child_node = acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node, + child_node); if (child_node) { - /* Found a child node - detach any attached object */ acpi_ns_detach_object(child_node); @@ -374,6 +399,55 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) return_VOID; } +/******************************************************************************* + * + * FUNCTION: acpi_ns_remove_reference + * + * PARAMETERS: Node - Named node whose reference count is to be + * decremented + * + * RETURN: None. + * + * DESCRIPTION: Remove a Node reference. Decrements the reference count + * of all parent Nodes up to the root. Any node along + * the way that reaches zero references is freed. + * + ******************************************************************************/ + +static void acpi_ns_remove_reference(struct acpi_namespace_node *node) +{ + struct acpi_namespace_node *parent_node; + struct acpi_namespace_node *this_node; + + ACPI_FUNCTION_ENTRY(); + + /* + * Decrement the reference count(s) of this node and all + * nodes up to the root, Delete anything with zero remaining references. + */ + this_node = node; + while (this_node) { + /* Prepare to move up to parent */ + + parent_node = acpi_ns_get_parent_node(this_node); + + /* Decrement the reference count on this node */ + + this_node->reference_count--; + + /* Delete the node if no more references */ + + if (!this_node->reference_count) { + /* Delete all children and delete the node */ + + acpi_ns_delete_children(this_node); + acpi_ns_delete_node(this_node); + } + + this_node = parent_node; + } +} + /******************************************************************************* * * FUNCTION: acpi_ns_delete_namespace_by_owner @@ -386,34 +460,24 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) * specific ID. Used to delete entire ACPI tables. All * reference counts are updated. * - * MUTEX: Locks namespace during deletion walk. - * ******************************************************************************/ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) { struct acpi_namespace_node *child_node; struct acpi_namespace_node *deletion_node; - struct acpi_namespace_node *parent_node; u32 level; - acpi_status status; + struct acpi_namespace_node *parent_node; - ACPI_FUNCTION_TRACE_U32(ns_delete_namespace_by_owner, owner_id); + ACPI_FUNCTION_TRACE_U32("ns_delete_namespace_by_owner", owner_id); if (owner_id == 0) { return_VOID; } - /* Lock namespace for possible update */ - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return_VOID; - } - - deletion_node = NULL; parent_node = acpi_gbl_root_node; child_node = NULL; + deletion_node = NULL; level = 1; /* @@ -430,14 +494,12 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) child_node); if (deletion_node) { - acpi_ns_delete_children(deletion_node); - acpi_ns_delete_node(deletion_node); + acpi_ns_remove_reference(deletion_node); deletion_node = NULL; } if (child_node) { if (child_node->owner_id == owner_id) { - /* Found a matching child node - detach any attached object */ acpi_ns_detach_object(child_node); @@ -479,6 +541,5 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) } } - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_VOID; }