linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / acpi / namespace / nsalloc.c
index 55b407a..9b871f3 100644 (file)
@@ -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;
 }