brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · 83d26ab Raw
495 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/*******************************************************************************3 *4 * Module Name: nsalloc - Namespace allocation and deletion utilities5 *6 ******************************************************************************/7 8#include <acpi/acpi.h>9#include "accommon.h"10#include "acnamesp.h"11 12#define _COMPONENT          ACPI_NAMESPACE13ACPI_MODULE_NAME("nsalloc")14 15/*******************************************************************************16 *17 * FUNCTION:    acpi_ns_create_node18 *19 * PARAMETERS:  name            - Name of the new node (4 char ACPI name)20 *21 * RETURN:      New namespace node (Null on failure)22 *23 * DESCRIPTION: Create a namespace node24 *25 ******************************************************************************/26struct acpi_namespace_node *acpi_ns_create_node(u32 name)27{28	struct acpi_namespace_node *node;29#ifdef ACPI_DBG_TRACK_ALLOCATIONS30	u32 temp;31#endif32 33	ACPI_FUNCTION_TRACE(ns_create_node);34 35	node = acpi_os_acquire_object(acpi_gbl_namespace_cache);36	if (!node) {37		return_PTR(NULL);38	}39 40	ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_allocated++);41 42#ifdef ACPI_DBG_TRACK_ALLOCATIONS43	temp = acpi_gbl_ns_node_list->total_allocated -44	    acpi_gbl_ns_node_list->total_freed;45	if (temp > acpi_gbl_ns_node_list->max_occupied) {46		acpi_gbl_ns_node_list->max_occupied = temp;47	}48#endif49 50	node->name.integer = name;51	ACPI_SET_DESCRIPTOR_TYPE(node, ACPI_DESC_TYPE_NAMED);52	return_PTR(node);53}54 55/*******************************************************************************56 *57 * FUNCTION:    acpi_ns_delete_node58 *59 * PARAMETERS:  node            - Node to be deleted60 *61 * RETURN:      None62 *63 * DESCRIPTION: Delete a namespace node. All node deletions must come through64 *              here. Detaches any attached objects, including any attached65 *              data. If a handler is associated with attached data, it is66 *              invoked before the node is deleted.67 *68 ******************************************************************************/69 70void acpi_ns_delete_node(struct acpi_namespace_node *node)71{72	union acpi_operand_object *obj_desc;73	union acpi_operand_object *next_desc;74 75	ACPI_FUNCTION_NAME(ns_delete_node);76 77	if (!node) {78		return_VOID;79	}80 81	/* Detach an object if there is one */82 83	acpi_ns_detach_object(node);84 85	/*86	 * Delete an attached data object list if present (objects that were87	 * attached via acpi_attach_data). Note: After any normal object is88	 * detached above, the only possible remaining object(s) are data89	 * objects, in a linked list.90	 */91	obj_desc = node->object;92	while (obj_desc && (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) {93 94		/* Invoke the attached data deletion handler if present */95 96		if (obj_desc->data.handler) {97			obj_desc->data.handler(node, obj_desc->data.pointer);98		}99 100		next_desc = obj_desc->common.next_object;101		acpi_ut_remove_reference(obj_desc);102		obj_desc = next_desc;103	}104 105	/* Special case for the statically allocated root node */106 107	if (node == acpi_gbl_root_node) {108		return;109	}110 111	/* Now we can delete the node */112 113	(void)acpi_os_release_object(acpi_gbl_namespace_cache, node);114 115	ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++);116	ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Node %p, Remaining %X\n",117			  node, acpi_gbl_current_node_count));118}119 120/*******************************************************************************121 *122 * FUNCTION:    acpi_ns_remove_node123 *124 * PARAMETERS:  node            - Node to be removed/deleted125 *126 * RETURN:      None127 *128 * DESCRIPTION: Remove (unlink) and delete a namespace node129 *130 ******************************************************************************/131 132void acpi_ns_remove_node(struct acpi_namespace_node *node)133{134	struct acpi_namespace_node *parent_node;135	struct acpi_namespace_node *prev_node;136	struct acpi_namespace_node *next_node;137 138	ACPI_FUNCTION_TRACE_PTR(ns_remove_node, node);139 140	parent_node = node->parent;141 142	prev_node = NULL;143	next_node = parent_node->child;144 145	/* Find the node that is the previous peer in the parent's child list */146 147	while (next_node != node) {148		prev_node = next_node;149		next_node = next_node->peer;150	}151 152	if (prev_node) {153 154		/* Node is not first child, unlink it */155 156		prev_node->peer = node->peer;157	} else {158		/*159		 * Node is first child (has no previous peer).160		 * Link peer list to parent161		 */162		parent_node->child = node->peer;163	}164 165	/* Delete the node and any attached objects */166 167	acpi_ns_delete_node(node);168	return_VOID;169}170 171/*******************************************************************************172 *173 * FUNCTION:    acpi_ns_install_node174 *175 * PARAMETERS:  walk_state      - Current state of the walk176 *              parent_node     - The parent of the new Node177 *              node            - The new Node to install178 *              type            - ACPI object type of the new Node179 *180 * RETURN:      None181 *182 * DESCRIPTION: Initialize a new namespace node and install it amongst183 *              its peers.184 *185 *              Note: Current namespace lookup is linear search. This appears186 *              to be sufficient as namespace searches consume only a small187 *              fraction of the execution time of the ACPI subsystem.188 *189 ******************************************************************************/190 191void acpi_ns_install_node(struct acpi_walk_state *walk_state, struct acpi_namespace_node *parent_node,	/* Parent */192			  struct acpi_namespace_node *node,	/* New Child */193			  acpi_object_type type)194{195	acpi_owner_id owner_id = 0;196	struct acpi_namespace_node *child_node;197 198	ACPI_FUNCTION_TRACE(ns_install_node);199 200	if (walk_state) {201		/*202		 * Get the owner ID from the Walk state. The owner ID is used to203		 * track table deletion and deletion of objects created by methods.204		 */205		owner_id = walk_state->owner_id;206 207		if ((walk_state->method_desc) &&208		    (parent_node != walk_state->method_node)) {209			/*210			 * A method is creating a new node that is not a child of the211			 * method (it is non-local). Mark the executing method as having212			 * modified the namespace. This is used for cleanup when the213			 * method exits.214			 */215			walk_state->method_desc->method.info_flags |=216			    ACPI_METHOD_MODIFIED_NAMESPACE;217		}218	}219 220	/* Link the new entry into the parent and existing children */221 222	node->peer = NULL;223	node->parent = parent_node;224	child_node = parent_node->child;225 226	if (!child_node) {227		parent_node->child = node;228	} else {229		/* Add node to the end of the peer list */230 231		while (child_node->peer) {232			child_node = child_node->peer;233		}234 235		child_node->peer = node;236	}237 238	/* Init the new entry */239 240	node->owner_id = owner_id;241	node->type = (u8) type;242 243	ACPI_DEBUG_PRINT((ACPI_DB_NAMES,244			  "%4.4s (%s) [Node %p Owner %3.3X] added to %4.4s (%s) [Node %p]\n",245			  acpi_ut_get_node_name(node),246			  acpi_ut_get_type_name(node->type), node, owner_id,247			  acpi_ut_get_node_name(parent_node),248			  acpi_ut_get_type_name(parent_node->type),249			  parent_node));250 251	return_VOID;252}253 254/*******************************************************************************255 *256 * FUNCTION:    acpi_ns_delete_children257 *258 * PARAMETERS:  parent_node     - Delete this objects children259 *260 * RETURN:      None.261 *262 * DESCRIPTION: Delete all children of the parent object. In other words,263 *              deletes a "scope".264 *265 ******************************************************************************/266 267void acpi_ns_delete_children(struct acpi_namespace_node *parent_node)268{269	struct acpi_namespace_node *next_node;270	struct acpi_namespace_node *node_to_delete;271 272	ACPI_FUNCTION_TRACE_PTR(ns_delete_children, parent_node);273 274	if (!parent_node) {275		return_VOID;276	}277 278	/* Deallocate all children at this level */279 280	next_node = parent_node->child;281	while (next_node) {282 283		/* Grandchildren should have all been deleted already */284 285		if (next_node->child) {286			ACPI_ERROR((AE_INFO, "Found a grandchild! P=%p C=%p",287				    parent_node, next_node));288		}289 290		/*291		 * Delete this child node and move on to the next child in the list.292		 * No need to unlink the node since we are deleting the entire branch.293		 */294		node_to_delete = next_node;295		next_node = next_node->peer;296		acpi_ns_delete_node(node_to_delete);297	}298 299	/* Clear the parent's child pointer */300 301	parent_node->child = NULL;302	return_VOID;303}304 305/*******************************************************************************306 *307 * FUNCTION:    acpi_ns_delete_namespace_subtree308 *309 * PARAMETERS:  parent_node     - Root of the subtree to be deleted310 *311 * RETURN:      None.312 *313 * DESCRIPTION: Delete a subtree of the namespace. This includes all objects314 *              stored within the subtree.315 *316 ******************************************************************************/317 318void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node)319{320	struct acpi_namespace_node *child_node = NULL;321	u32 level = 1;322	acpi_status status;323 324	ACPI_FUNCTION_TRACE(ns_delete_namespace_subtree);325 326	if (!parent_node) {327		return_VOID;328	}329 330	/* Lock namespace for possible update */331 332	status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);333	if (ACPI_FAILURE(status)) {334		return_VOID;335	}336 337	/*338	 * Traverse the tree of objects until we bubble back up339	 * to where we started.340	 */341	while (level > 0) {342 343		/* Get the next node in this scope (NULL if none) */344 345		child_node = acpi_ns_get_next_node(parent_node, child_node);346		if (child_node) {347 348			/* Found a child node - detach any attached object */349 350			acpi_ns_detach_object(child_node);351 352			/* Check if this node has any children */353 354			if (child_node->child) {355				/*356				 * There is at least one child of this node,357				 * visit the node358				 */359				level++;360				parent_node = child_node;361				child_node = NULL;362			}363		} else {364			/*365			 * No more children of this parent node.366			 * Move up to the grandparent.367			 */368			level--;369 370			/*371			 * Now delete all of the children of this parent372			 * all at the same time.373			 */374			acpi_ns_delete_children(parent_node);375 376			/* New "last child" is this parent node */377 378			child_node = parent_node;379 380			/* Move up the tree to the grandparent */381 382			parent_node = parent_node->parent;383		}384	}385 386	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);387	return_VOID;388}389 390/*******************************************************************************391 *392 * FUNCTION:    acpi_ns_delete_namespace_by_owner393 *394 * PARAMETERS:  owner_id    - All nodes with this owner will be deleted395 *396 * RETURN:      Status397 *398 * DESCRIPTION: Delete entries within the namespace that are owned by a399 *              specific ID. Used to delete entire ACPI tables. All400 *              reference counts are updated.401 *402 * MUTEX:       Locks namespace during deletion walk.403 *404 ******************************************************************************/405 406void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id)407{408	struct acpi_namespace_node *child_node;409	struct acpi_namespace_node *deletion_node;410	struct acpi_namespace_node *parent_node;411	u32 level;412	acpi_status status;413 414	ACPI_FUNCTION_TRACE_U32(ns_delete_namespace_by_owner, owner_id);415 416	if (owner_id == 0) {417		return_VOID;418	}419 420	/* Lock namespace for possible update */421 422	status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);423	if (ACPI_FAILURE(status)) {424		return_VOID;425	}426 427	deletion_node = NULL;428	parent_node = acpi_gbl_root_node;429	child_node = NULL;430	level = 1;431 432	/*433	 * Traverse the tree of nodes until we bubble back up434	 * to where we started.435	 */436	while (level > 0) {437		/*438		 * Get the next child of this parent node. When child_node is NULL,439		 * the first child of the parent is returned440		 */441		child_node = acpi_ns_get_next_node(parent_node, child_node);442 443		if (deletion_node) {444			acpi_ns_delete_children(deletion_node);445			acpi_ns_remove_node(deletion_node);446			deletion_node = NULL;447		}448 449		if (child_node) {450			if (child_node->owner_id == owner_id) {451 452				/* Found a matching child node - detach any attached object */453 454				acpi_ns_detach_object(child_node);455			}456 457			/* Check if this node has any children */458 459			if (child_node->child) {460				/*461				 * There is at least one child of this node,462				 * visit the node463				 */464				level++;465				parent_node = child_node;466				child_node = NULL;467			} else if (child_node->owner_id == owner_id) {468				deletion_node = child_node;469			}470		} else {471			/*472			 * No more children of this parent node.473			 * Move up to the grandparent.474			 */475			level--;476			if (level != 0) {477				if (parent_node->owner_id == owner_id) {478					deletion_node = parent_node;479				}480			}481 482			/* New "last child" is this parent node */483 484			child_node = parent_node;485 486			/* Move up the tree to the grandparent */487 488			parent_node = parent_node->parent;489		}490	}491 492	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);493	return_VOID;494}495