diff options
Diffstat (limited to 'drivers/acpi/acpica/nsalloc.c')
-rw-r--r-- | drivers/acpi/acpica/nsalloc.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c new file mode 100644 index 000000000000..cb2afbf4e457 --- /dev/null +++ b/drivers/acpi/acpica/nsalloc.c | |||
@@ -0,0 +1,497 @@ | |||
1 | /******************************************************************************* | ||
2 | * | ||
3 | * Module Name: nsalloc - Namespace allocation and deletion utilities | ||
4 | * | ||
5 | ******************************************************************************/ | ||
6 | |||
7 | /* | ||
8 | * Copyright (C) 2000 - 2008, Intel Corp. | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or without | ||
12 | * modification, are permitted provided that the following conditions | ||
13 | * are met: | ||
14 | * 1. Redistributions of source code must retain the above copyright | ||
15 | * notice, this list of conditions, and the following disclaimer, | ||
16 | * without modification. | ||
17 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | ||
18 | * substantially similar to the "NO WARRANTY" disclaimer below | ||
19 | * ("Disclaimer") and any redistribution must be conditioned upon | ||
20 | * including a substantially similar Disclaimer requirement for further | ||
21 | * binary redistribution. | ||
22 | * 3. Neither the names of the above-listed copyright holders nor the names | ||
23 | * of any contributors may be used to endorse or promote products derived | ||
24 | * from this software without specific prior written permission. | ||
25 | * | ||
26 | * Alternatively, this software may be distributed under the terms of the | ||
27 | * GNU General Public License ("GPL") version 2 as published by the Free | ||
28 | * Software Foundation. | ||
29 | * | ||
30 | * NO WARRANTY | ||
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
32 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
33 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | ||
34 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
35 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
36 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
37 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
38 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
39 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | ||
40 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
41 | * POSSIBILITY OF SUCH DAMAGES. | ||
42 | */ | ||
43 | |||
44 | #include <acpi/acpi.h> | ||
45 | #include <acpi/accommon.h> | ||
46 | #include <acpi/acnamesp.h> | ||
47 | |||
48 | #define _COMPONENT ACPI_NAMESPACE | ||
49 | ACPI_MODULE_NAME("nsalloc") | ||
50 | |||
51 | /******************************************************************************* | ||
52 | * | ||
53 | * FUNCTION: acpi_ns_create_node | ||
54 | * | ||
55 | * PARAMETERS: Name - Name of the new node (4 char ACPI name) | ||
56 | * | ||
57 | * RETURN: New namespace node (Null on failure) | ||
58 | * | ||
59 | * DESCRIPTION: Create a namespace node | ||
60 | * | ||
61 | ******************************************************************************/ | ||
62 | struct acpi_namespace_node *acpi_ns_create_node(u32 name) | ||
63 | { | ||
64 | struct acpi_namespace_node *node; | ||
65 | #ifdef ACPI_DBG_TRACK_ALLOCATIONS | ||
66 | u32 temp; | ||
67 | #endif | ||
68 | |||
69 | ACPI_FUNCTION_TRACE(ns_create_node); | ||
70 | |||
71 | node = acpi_os_acquire_object(acpi_gbl_namespace_cache); | ||
72 | if (!node) { | ||
73 | return_PTR(NULL); | ||
74 | } | ||
75 | |||
76 | ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_allocated++); | ||
77 | |||
78 | #ifdef ACPI_DBG_TRACK_ALLOCATIONS | ||
79 | temp = | ||
80 | acpi_gbl_ns_node_list->total_allocated - | ||
81 | acpi_gbl_ns_node_list->total_freed; | ||
82 | if (temp > acpi_gbl_ns_node_list->max_occupied) { | ||
83 | acpi_gbl_ns_node_list->max_occupied = temp; | ||
84 | } | ||
85 | #endif | ||
86 | |||
87 | node->name.integer = name; | ||
88 | ACPI_SET_DESCRIPTOR_TYPE(node, ACPI_DESC_TYPE_NAMED); | ||
89 | return_PTR(node); | ||
90 | } | ||
91 | |||
92 | /******************************************************************************* | ||
93 | * | ||
94 | * FUNCTION: acpi_ns_delete_node | ||
95 | * | ||
96 | * PARAMETERS: Node - Node to be deleted | ||
97 | * | ||
98 | * RETURN: None | ||
99 | * | ||
100 | * DESCRIPTION: Delete a namespace node | ||
101 | * | ||
102 | ******************************************************************************/ | ||
103 | |||
104 | void acpi_ns_delete_node(struct acpi_namespace_node *node) | ||
105 | { | ||
106 | struct acpi_namespace_node *parent_node; | ||
107 | struct acpi_namespace_node *prev_node; | ||
108 | struct acpi_namespace_node *next_node; | ||
109 | |||
110 | ACPI_FUNCTION_TRACE_PTR(ns_delete_node, node); | ||
111 | |||
112 | parent_node = acpi_ns_get_parent_node(node); | ||
113 | |||
114 | prev_node = NULL; | ||
115 | next_node = parent_node->child; | ||
116 | |||
117 | /* Find the node that is the previous peer in the parent's child list */ | ||
118 | |||
119 | while (next_node != node) { | ||
120 | prev_node = next_node; | ||
121 | next_node = prev_node->peer; | ||
122 | } | ||
123 | |||
124 | if (prev_node) { | ||
125 | |||
126 | /* Node is not first child, unlink it */ | ||
127 | |||
128 | prev_node->peer = next_node->peer; | ||
129 | if (next_node->flags & ANOBJ_END_OF_PEER_LIST) { | ||
130 | prev_node->flags |= ANOBJ_END_OF_PEER_LIST; | ||
131 | } | ||
132 | } else { | ||
133 | /* Node is first child (has no previous peer) */ | ||
134 | |||
135 | if (next_node->flags & ANOBJ_END_OF_PEER_LIST) { | ||
136 | |||
137 | /* No peers at all */ | ||
138 | |||
139 | parent_node->child = NULL; | ||
140 | } else { /* Link peer list to parent */ | ||
141 | |||
142 | parent_node->child = next_node->peer; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++); | ||
147 | |||
148 | /* | ||
149 | * Detach an object if there is one, then delete the node | ||
150 | */ | ||
151 | acpi_ns_detach_object(node); | ||
152 | (void)acpi_os_release_object(acpi_gbl_namespace_cache, node); | ||
153 | return_VOID; | ||
154 | } | ||
155 | |||
156 | /******************************************************************************* | ||
157 | * | ||
158 | * FUNCTION: acpi_ns_install_node | ||
159 | * | ||
160 | * PARAMETERS: walk_state - Current state of the walk | ||
161 | * parent_node - The parent of the new Node | ||
162 | * Node - The new Node to install | ||
163 | * Type - ACPI object type of the new Node | ||
164 | * | ||
165 | * RETURN: None | ||
166 | * | ||
167 | * DESCRIPTION: Initialize a new namespace node and install it amongst | ||
168 | * its peers. | ||
169 | * | ||
170 | * Note: Current namespace lookup is linear search. This appears | ||
171 | * to be sufficient as namespace searches consume only a small | ||
172 | * fraction of the execution time of the ACPI subsystem. | ||
173 | * | ||
174 | ******************************************************************************/ | ||
175 | |||
176 | void acpi_ns_install_node(struct acpi_walk_state *walk_state, struct acpi_namespace_node *parent_node, /* Parent */ | ||
177 | struct acpi_namespace_node *node, /* New Child */ | ||
178 | acpi_object_type type) | ||
179 | { | ||
180 | acpi_owner_id owner_id = 0; | ||
181 | struct acpi_namespace_node *child_node; | ||
182 | |||
183 | ACPI_FUNCTION_TRACE(ns_install_node); | ||
184 | |||
185 | /* | ||
186 | * Get the owner ID from the Walk state | ||
187 | * The owner ID is used to track table deletion and | ||
188 | * deletion of objects created by methods | ||
189 | */ | ||
190 | if (walk_state) { | ||
191 | owner_id = walk_state->owner_id; | ||
192 | } | ||
193 | |||
194 | /* Link the new entry into the parent and existing children */ | ||
195 | |||
196 | child_node = parent_node->child; | ||
197 | if (!child_node) { | ||
198 | parent_node->child = node; | ||
199 | node->flags |= ANOBJ_END_OF_PEER_LIST; | ||
200 | node->peer = parent_node; | ||
201 | } else { | ||
202 | while (!(child_node->flags & ANOBJ_END_OF_PEER_LIST)) { | ||
203 | child_node = child_node->peer; | ||
204 | } | ||
205 | |||
206 | child_node->peer = node; | ||
207 | |||
208 | /* Clear end-of-list flag */ | ||
209 | |||
210 | child_node->flags &= ~ANOBJ_END_OF_PEER_LIST; | ||
211 | node->flags |= ANOBJ_END_OF_PEER_LIST; | ||
212 | node->peer = parent_node; | ||
213 | } | ||
214 | |||
215 | /* Init the new entry */ | ||
216 | |||
217 | node->owner_id = owner_id; | ||
218 | node->type = (u8) type; | ||
219 | |||
220 | ACPI_DEBUG_PRINT((ACPI_DB_NAMES, | ||
221 | "%4.4s (%s) [Node %p Owner %X] added to %4.4s (%s) [Node %p]\n", | ||
222 | acpi_ut_get_node_name(node), | ||
223 | acpi_ut_get_type_name(node->type), node, owner_id, | ||
224 | acpi_ut_get_node_name(parent_node), | ||
225 | acpi_ut_get_type_name(parent_node->type), | ||
226 | parent_node)); | ||
227 | |||
228 | return_VOID; | ||
229 | } | ||
230 | |||
231 | /******************************************************************************* | ||
232 | * | ||
233 | * FUNCTION: acpi_ns_delete_children | ||
234 | * | ||
235 | * PARAMETERS: parent_node - Delete this objects children | ||
236 | * | ||
237 | * RETURN: None. | ||
238 | * | ||
239 | * DESCRIPTION: Delete all children of the parent object. In other words, | ||
240 | * deletes a "scope". | ||
241 | * | ||
242 | ******************************************************************************/ | ||
243 | |||
244 | void acpi_ns_delete_children(struct acpi_namespace_node *parent_node) | ||
245 | { | ||
246 | struct acpi_namespace_node *child_node; | ||
247 | struct acpi_namespace_node *next_node; | ||
248 | u8 flags; | ||
249 | |||
250 | ACPI_FUNCTION_TRACE_PTR(ns_delete_children, parent_node); | ||
251 | |||
252 | if (!parent_node) { | ||
253 | return_VOID; | ||
254 | } | ||
255 | |||
256 | /* If no children, all done! */ | ||
257 | |||
258 | child_node = parent_node->child; | ||
259 | if (!child_node) { | ||
260 | return_VOID; | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * Deallocate all children at this level | ||
265 | */ | ||
266 | do { | ||
267 | |||
268 | /* Get the things we need */ | ||
269 | |||
270 | next_node = child_node->peer; | ||
271 | flags = child_node->flags; | ||
272 | |||
273 | /* Grandchildren should have all been deleted already */ | ||
274 | |||
275 | if (child_node->child) { | ||
276 | ACPI_ERROR((AE_INFO, "Found a grandchild! P=%p C=%p", | ||
277 | parent_node, child_node)); | ||
278 | } | ||
279 | |||
280 | /* Now we can free this child object */ | ||
281 | |||
282 | ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++); | ||
283 | |||
284 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, | ||
285 | "Object %p, Remaining %X\n", child_node, | ||
286 | acpi_gbl_current_node_count)); | ||
287 | |||
288 | /* | ||
289 | * Detach an object if there is one, then free the child node | ||
290 | */ | ||
291 | acpi_ns_detach_object(child_node); | ||
292 | |||
293 | /* Now we can delete the node */ | ||
294 | |||
295 | (void)acpi_os_release_object(acpi_gbl_namespace_cache, | ||
296 | child_node); | ||
297 | |||
298 | /* And move on to the next child in the list */ | ||
299 | |||
300 | child_node = next_node; | ||
301 | |||
302 | } while (!(flags & ANOBJ_END_OF_PEER_LIST)); | ||
303 | |||
304 | /* Clear the parent's child pointer */ | ||
305 | |||
306 | parent_node->child = NULL; | ||
307 | |||
308 | return_VOID; | ||
309 | } | ||
310 | |||
311 | /******************************************************************************* | ||
312 | * | ||
313 | * FUNCTION: acpi_ns_delete_namespace_subtree | ||
314 | * | ||
315 | * PARAMETERS: parent_node - Root of the subtree to be deleted | ||
316 | * | ||
317 | * RETURN: None. | ||
318 | * | ||
319 | * DESCRIPTION: Delete a subtree of the namespace. This includes all objects | ||
320 | * stored within the subtree. | ||
321 | * | ||
322 | ******************************************************************************/ | ||
323 | |||
324 | void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) | ||
325 | { | ||
326 | struct acpi_namespace_node *child_node = NULL; | ||
327 | u32 level = 1; | ||
328 | |||
329 | ACPI_FUNCTION_TRACE(ns_delete_namespace_subtree); | ||
330 | |||
331 | if (!parent_node) { | ||
332 | return_VOID; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Traverse the tree of objects until we bubble back up | ||
337 | * to where we started. | ||
338 | */ | ||
339 | while (level > 0) { | ||
340 | |||
341 | /* Get the next node in this scope (NULL if none) */ | ||
342 | |||
343 | child_node = | ||
344 | acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node, | ||
345 | 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 (acpi_ns_get_next_node | ||
355 | (ACPI_TYPE_ANY, child_node, NULL)) { | ||
356 | /* | ||
357 | * There is at least one child of this node, | ||
358 | * visit the node | ||
359 | */ | ||
360 | level++; | ||
361 | parent_node = child_node; | ||
362 | child_node = NULL; | ||
363 | } | ||
364 | } else { | ||
365 | /* | ||
366 | * No more children of this parent node. | ||
367 | * Move up to the grandparent. | ||
368 | */ | ||
369 | level--; | ||
370 | |||
371 | /* | ||
372 | * Now delete all of the children of this parent | ||
373 | * all at the same time. | ||
374 | */ | ||
375 | acpi_ns_delete_children(parent_node); | ||
376 | |||
377 | /* New "last child" is this parent node */ | ||
378 | |||
379 | child_node = parent_node; | ||
380 | |||
381 | /* Move up the tree to the grandparent */ | ||
382 | |||
383 | parent_node = acpi_ns_get_parent_node(parent_node); | ||
384 | } | ||
385 | } | ||
386 | |||
387 | return_VOID; | ||
388 | } | ||
389 | |||
390 | /******************************************************************************* | ||
391 | * | ||
392 | * FUNCTION: acpi_ns_delete_namespace_by_owner | ||
393 | * | ||
394 | * PARAMETERS: owner_id - All nodes with this owner will be deleted | ||
395 | * | ||
396 | * RETURN: Status | ||
397 | * | ||
398 | * DESCRIPTION: Delete entries within the namespace that are owned by a | ||
399 | * specific ID. Used to delete entire ACPI tables. All | ||
400 | * reference counts are updated. | ||
401 | * | ||
402 | * MUTEX: Locks namespace during deletion walk. | ||
403 | * | ||
404 | ******************************************************************************/ | ||
405 | |||
406 | void 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 up | ||
434 | * 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 returned | ||
440 | */ | ||
441 | child_node = | ||
442 | acpi_ns_get_next_node(ACPI_TYPE_ANY, parent_node, | ||
443 | child_node); | ||
444 | |||
445 | if (deletion_node) { | ||
446 | acpi_ns_delete_children(deletion_node); | ||
447 | acpi_ns_delete_node(deletion_node); | ||
448 | deletion_node = NULL; | ||
449 | } | ||
450 | |||
451 | if (child_node) { | ||
452 | if (child_node->owner_id == owner_id) { | ||
453 | |||
454 | /* Found a matching child node - detach any attached object */ | ||
455 | |||
456 | acpi_ns_detach_object(child_node); | ||
457 | } | ||
458 | |||
459 | /* Check if this node has any children */ | ||
460 | |||
461 | if (acpi_ns_get_next_node | ||
462 | (ACPI_TYPE_ANY, child_node, NULL)) { | ||
463 | /* | ||
464 | * There is at least one child of this node, | ||
465 | * visit the node | ||
466 | */ | ||
467 | level++; | ||
468 | parent_node = child_node; | ||
469 | child_node = NULL; | ||
470 | } else if (child_node->owner_id == owner_id) { | ||
471 | deletion_node = child_node; | ||
472 | } | ||
473 | } else { | ||
474 | /* | ||
475 | * No more children of this parent node. | ||
476 | * Move up to the grandparent. | ||
477 | */ | ||
478 | level--; | ||
479 | if (level != 0) { | ||
480 | if (parent_node->owner_id == owner_id) { | ||
481 | deletion_node = parent_node; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | /* New "last child" is this parent node */ | ||
486 | |||
487 | child_node = parent_node; | ||
488 | |||
489 | /* Move up the tree to the grandparent */ | ||
490 | |||
491 | parent_node = acpi_ns_get_parent_node(parent_node); | ||
492 | } | ||
493 | } | ||
494 | |||
495 | (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); | ||
496 | return_VOID; | ||
497 | } | ||