1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Module Name: uttrack - Memory allocation tracking routines (debug only) |
5 | * |
6 | * Copyright (C) 2000 - 2023, Intel Corp. |
7 | * |
8 | *****************************************************************************/ |
9 | |
10 | /* |
11 | * These procedures are used for tracking memory leaks in the subsystem, and |
12 | * they get compiled out when the ACPI_DBG_TRACK_ALLOCATIONS is not set. |
13 | * |
14 | * Each memory allocation is tracked via a doubly linked list. Each |
15 | * element contains the caller's component, module name, function name, and |
16 | * line number. acpi_ut_allocate and acpi_ut_allocate_zeroed call |
17 | * acpi_ut_track_allocation to add an element to the list; deletion |
18 | * occurs in the body of acpi_ut_free. |
19 | */ |
20 | |
21 | #include <acpi/acpi.h> |
22 | #include "accommon.h" |
23 | |
24 | #ifdef ACPI_DBG_TRACK_ALLOCATIONS |
25 | |
26 | #define _COMPONENT ACPI_UTILITIES |
27 | ACPI_MODULE_NAME("uttrack" ) |
28 | |
29 | /* Local prototypes */ |
30 | static struct acpi_debug_mem_block *acpi_ut_find_allocation(struct |
31 | acpi_debug_mem_block |
32 | *allocation); |
33 | |
34 | static acpi_status |
35 | acpi_ut_track_allocation(struct acpi_debug_mem_block *address, |
36 | acpi_size size, |
37 | u8 alloc_type, |
38 | u32 component, const char *module, u32 line); |
39 | |
40 | static acpi_status |
41 | acpi_ut_remove_allocation(struct acpi_debug_mem_block *address, |
42 | u32 component, const char *module, u32 line); |
43 | |
44 | /******************************************************************************* |
45 | * |
46 | * FUNCTION: acpi_ut_create_list |
47 | * |
48 | * PARAMETERS: cache_name - Ascii name for the cache |
49 | * object_size - Size of each cached object |
50 | * return_cache - Where the new cache object is returned |
51 | * |
52 | * RETURN: Status |
53 | * |
54 | * DESCRIPTION: Create a local memory list for tracking purposed |
55 | * |
56 | ******************************************************************************/ |
57 | |
58 | acpi_status |
59 | acpi_ut_create_list(const char *list_name, |
60 | u16 object_size, struct acpi_memory_list **return_cache) |
61 | { |
62 | struct acpi_memory_list *cache; |
63 | |
64 | cache = acpi_os_allocate_zeroed(sizeof(struct acpi_memory_list)); |
65 | if (!cache) { |
66 | return (AE_NO_MEMORY); |
67 | } |
68 | |
69 | cache->list_name = list_name; |
70 | cache->object_size = object_size; |
71 | |
72 | *return_cache = cache; |
73 | return (AE_OK); |
74 | } |
75 | |
76 | /******************************************************************************* |
77 | * |
78 | * FUNCTION: acpi_ut_allocate_and_track |
79 | * |
80 | * PARAMETERS: size - Size of the allocation |
81 | * component - Component type of caller |
82 | * module - Source file name of caller |
83 | * line - Line number of caller |
84 | * |
85 | * RETURN: Address of the allocated memory on success, NULL on failure. |
86 | * |
87 | * DESCRIPTION: The subsystem's equivalent of malloc. |
88 | * |
89 | ******************************************************************************/ |
90 | |
91 | void *acpi_ut_allocate_and_track(acpi_size size, |
92 | u32 component, const char *module, u32 line) |
93 | { |
94 | struct acpi_debug_mem_block *allocation; |
95 | acpi_status status; |
96 | |
97 | /* Check for an inadvertent size of zero bytes */ |
98 | |
99 | if (!size) { |
100 | ACPI_WARNING((module, line, |
101 | "Attempt to allocate zero bytes, allocating 1 byte" )); |
102 | size = 1; |
103 | } |
104 | |
105 | allocation = |
106 | acpi_os_allocate(size + sizeof(struct acpi_debug_mem_header)); |
107 | if (!allocation) { |
108 | |
109 | /* Report allocation error */ |
110 | |
111 | ACPI_WARNING((module, line, |
112 | "Could not allocate size %u" , (u32)size)); |
113 | |
114 | return (NULL); |
115 | } |
116 | |
117 | status = |
118 | acpi_ut_track_allocation(allocation, size, ACPI_MEM_MALLOC, |
119 | component, module, line); |
120 | if (ACPI_FAILURE(status)) { |
121 | acpi_os_free(allocation); |
122 | return (NULL); |
123 | } |
124 | |
125 | acpi_gbl_global_list->total_allocated++; |
126 | acpi_gbl_global_list->total_size += (u32)size; |
127 | acpi_gbl_global_list->current_total_size += (u32)size; |
128 | |
129 | if (acpi_gbl_global_list->current_total_size > |
130 | acpi_gbl_global_list->max_occupied) { |
131 | acpi_gbl_global_list->max_occupied = |
132 | acpi_gbl_global_list->current_total_size; |
133 | } |
134 | |
135 | return ((void *)&allocation->user_space); |
136 | } |
137 | |
138 | /******************************************************************************* |
139 | * |
140 | * FUNCTION: acpi_ut_allocate_zeroed_and_track |
141 | * |
142 | * PARAMETERS: size - Size of the allocation |
143 | * component - Component type of caller |
144 | * module - Source file name of caller |
145 | * line - Line number of caller |
146 | * |
147 | * RETURN: Address of the allocated memory on success, NULL on failure. |
148 | * |
149 | * DESCRIPTION: Subsystem equivalent of calloc. |
150 | * |
151 | ******************************************************************************/ |
152 | |
153 | void *acpi_ut_allocate_zeroed_and_track(acpi_size size, |
154 | u32 component, |
155 | const char *module, u32 line) |
156 | { |
157 | struct acpi_debug_mem_block *allocation; |
158 | acpi_status status; |
159 | |
160 | /* Check for an inadvertent size of zero bytes */ |
161 | |
162 | if (!size) { |
163 | ACPI_WARNING((module, line, |
164 | "Attempt to allocate zero bytes, allocating 1 byte" )); |
165 | size = 1; |
166 | } |
167 | |
168 | allocation = |
169 | acpi_os_allocate_zeroed(size + |
170 | sizeof(struct acpi_debug_mem_header)); |
171 | if (!allocation) { |
172 | |
173 | /* Report allocation error */ |
174 | |
175 | ACPI_ERROR((module, line, |
176 | "Could not allocate size %u" , (u32)size)); |
177 | return (NULL); |
178 | } |
179 | |
180 | status = acpi_ut_track_allocation(allocation, size, |
181 | ACPI_MEM_CALLOC, component, module, |
182 | line); |
183 | if (ACPI_FAILURE(status)) { |
184 | acpi_os_free(allocation); |
185 | return (NULL); |
186 | } |
187 | |
188 | acpi_gbl_global_list->total_allocated++; |
189 | acpi_gbl_global_list->total_size += (u32)size; |
190 | acpi_gbl_global_list->current_total_size += (u32)size; |
191 | |
192 | if (acpi_gbl_global_list->current_total_size > |
193 | acpi_gbl_global_list->max_occupied) { |
194 | acpi_gbl_global_list->max_occupied = |
195 | acpi_gbl_global_list->current_total_size; |
196 | } |
197 | |
198 | return ((void *)&allocation->user_space); |
199 | } |
200 | |
201 | /******************************************************************************* |
202 | * |
203 | * FUNCTION: acpi_ut_free_and_track |
204 | * |
205 | * PARAMETERS: allocation - Address of the memory to deallocate |
206 | * component - Component type of caller |
207 | * module - Source file name of caller |
208 | * line - Line number of caller |
209 | * |
210 | * RETURN: None |
211 | * |
212 | * DESCRIPTION: Frees the memory at Allocation |
213 | * |
214 | ******************************************************************************/ |
215 | |
216 | void |
217 | acpi_ut_free_and_track(void *allocation, |
218 | u32 component, const char *module, u32 line) |
219 | { |
220 | struct acpi_debug_mem_block *debug_block; |
221 | acpi_status status; |
222 | |
223 | ACPI_FUNCTION_TRACE_PTR(ut_free, allocation); |
224 | |
225 | if (NULL == allocation) { |
226 | ACPI_ERROR((module, line, "Attempt to delete a NULL address" )); |
227 | |
228 | return_VOID; |
229 | } |
230 | |
231 | debug_block = ACPI_CAST_PTR(struct acpi_debug_mem_block, |
232 | (((char *)allocation) - |
233 | sizeof(struct acpi_debug_mem_header))); |
234 | |
235 | acpi_gbl_global_list->total_freed++; |
236 | acpi_gbl_global_list->current_total_size -= debug_block->size; |
237 | |
238 | status = |
239 | acpi_ut_remove_allocation(debug_block, component, module, line); |
240 | if (ACPI_FAILURE(status)) { |
241 | ACPI_EXCEPTION((AE_INFO, status, "Could not free memory" )); |
242 | } |
243 | |
244 | acpi_os_free(debug_block); |
245 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "%p freed (block %p)\n" , |
246 | allocation, debug_block)); |
247 | return_VOID; |
248 | } |
249 | |
250 | /******************************************************************************* |
251 | * |
252 | * FUNCTION: acpi_ut_find_allocation |
253 | * |
254 | * PARAMETERS: allocation - Address of allocated memory |
255 | * |
256 | * RETURN: Three cases: |
257 | * 1) List is empty, NULL is returned. |
258 | * 2) Element was found. Returns Allocation parameter. |
259 | * 3) Element was not found. Returns position where it should be |
260 | * inserted into the list. |
261 | * |
262 | * DESCRIPTION: Searches for an element in the global allocation tracking list. |
263 | * If the element is not found, returns the location within the |
264 | * list where the element should be inserted. |
265 | * |
266 | * Note: The list is ordered by larger-to-smaller addresses. |
267 | * |
268 | * This global list is used to detect memory leaks in ACPICA as |
269 | * well as other issues such as an attempt to release the same |
270 | * internal object more than once. Although expensive as far |
271 | * as cpu time, this list is much more helpful for finding these |
272 | * types of issues than using memory leak detectors outside of |
273 | * the ACPICA code. |
274 | * |
275 | ******************************************************************************/ |
276 | |
277 | static struct acpi_debug_mem_block *acpi_ut_find_allocation(struct |
278 | acpi_debug_mem_block |
279 | *allocation) |
280 | { |
281 | struct acpi_debug_mem_block *element; |
282 | |
283 | element = acpi_gbl_global_list->list_head; |
284 | if (!element) { |
285 | return (NULL); |
286 | } |
287 | |
288 | /* |
289 | * Search for the address. |
290 | * |
291 | * Note: List is ordered by larger-to-smaller addresses, on the |
292 | * assumption that a new allocation usually has a larger address |
293 | * than previous allocations. |
294 | */ |
295 | while (element > allocation) { |
296 | |
297 | /* Check for end-of-list */ |
298 | |
299 | if (!element->next) { |
300 | return (element); |
301 | } |
302 | |
303 | element = element->next; |
304 | } |
305 | |
306 | if (element == allocation) { |
307 | return (element); |
308 | } |
309 | |
310 | return (element->previous); |
311 | } |
312 | |
313 | /******************************************************************************* |
314 | * |
315 | * FUNCTION: acpi_ut_track_allocation |
316 | * |
317 | * PARAMETERS: allocation - Address of allocated memory |
318 | * size - Size of the allocation |
319 | * alloc_type - MEM_MALLOC or MEM_CALLOC |
320 | * component - Component type of caller |
321 | * module - Source file name of caller |
322 | * line - Line number of caller |
323 | * |
324 | * RETURN: Status |
325 | * |
326 | * DESCRIPTION: Inserts an element into the global allocation tracking list. |
327 | * |
328 | ******************************************************************************/ |
329 | |
330 | static acpi_status |
331 | acpi_ut_track_allocation(struct acpi_debug_mem_block *allocation, |
332 | acpi_size size, |
333 | u8 alloc_type, |
334 | u32 component, const char *module, u32 line) |
335 | { |
336 | struct acpi_memory_list *mem_list; |
337 | struct acpi_debug_mem_block *element; |
338 | acpi_status status = AE_OK; |
339 | |
340 | ACPI_FUNCTION_TRACE_PTR(ut_track_allocation, allocation); |
341 | |
342 | if (acpi_gbl_disable_mem_tracking) { |
343 | return_ACPI_STATUS(AE_OK); |
344 | } |
345 | |
346 | mem_list = acpi_gbl_global_list; |
347 | status = acpi_ut_acquire_mutex(ACPI_MTX_MEMORY); |
348 | if (ACPI_FAILURE(status)) { |
349 | return_ACPI_STATUS(status); |
350 | } |
351 | |
352 | /* |
353 | * Search the global list for this address to make sure it is not |
354 | * already present. This will catch several kinds of problems. |
355 | */ |
356 | element = acpi_ut_find_allocation(allocation); |
357 | if (element == allocation) { |
358 | ACPI_ERROR((AE_INFO, |
359 | "UtTrackAllocation: Allocation (%p) already present in global list!" , |
360 | allocation)); |
361 | goto unlock_and_exit; |
362 | } |
363 | |
364 | /* Fill in the instance data */ |
365 | |
366 | allocation->size = (u32)size; |
367 | allocation->alloc_type = alloc_type; |
368 | allocation->component = component; |
369 | allocation->line = line; |
370 | |
371 | acpi_ut_safe_strncpy(allocation->module, (char *)module, |
372 | ACPI_MAX_MODULE_NAME); |
373 | |
374 | if (!element) { |
375 | |
376 | /* Insert at list head */ |
377 | |
378 | if (mem_list->list_head) { |
379 | ((struct acpi_debug_mem_block *)(mem_list->list_head))-> |
380 | previous = allocation; |
381 | } |
382 | |
383 | allocation->next = mem_list->list_head; |
384 | allocation->previous = NULL; |
385 | |
386 | mem_list->list_head = allocation; |
387 | } else { |
388 | /* Insert after element */ |
389 | |
390 | allocation->next = element->next; |
391 | allocation->previous = element; |
392 | |
393 | if (element->next) { |
394 | (element->next)->previous = allocation; |
395 | } |
396 | |
397 | element->next = allocation; |
398 | } |
399 | |
400 | unlock_and_exit: |
401 | status = acpi_ut_release_mutex(ACPI_MTX_MEMORY); |
402 | return_ACPI_STATUS(status); |
403 | } |
404 | |
405 | /******************************************************************************* |
406 | * |
407 | * FUNCTION: acpi_ut_remove_allocation |
408 | * |
409 | * PARAMETERS: allocation - Address of allocated memory |
410 | * component - Component type of caller |
411 | * module - Source file name of caller |
412 | * line - Line number of caller |
413 | * |
414 | * RETURN: Status |
415 | * |
416 | * DESCRIPTION: Deletes an element from the global allocation tracking list. |
417 | * |
418 | ******************************************************************************/ |
419 | |
420 | static acpi_status |
421 | acpi_ut_remove_allocation(struct acpi_debug_mem_block *allocation, |
422 | u32 component, const char *module, u32 line) |
423 | { |
424 | struct acpi_memory_list *mem_list; |
425 | acpi_status status; |
426 | |
427 | ACPI_FUNCTION_NAME(ut_remove_allocation); |
428 | |
429 | if (acpi_gbl_disable_mem_tracking) { |
430 | return (AE_OK); |
431 | } |
432 | |
433 | mem_list = acpi_gbl_global_list; |
434 | if (NULL == mem_list->list_head) { |
435 | |
436 | /* No allocations! */ |
437 | |
438 | ACPI_ERROR((module, line, |
439 | "Empty allocation list, nothing to free!" )); |
440 | |
441 | return (AE_OK); |
442 | } |
443 | |
444 | status = acpi_ut_acquire_mutex(ACPI_MTX_MEMORY); |
445 | if (ACPI_FAILURE(status)) { |
446 | return (status); |
447 | } |
448 | |
449 | /* Unlink */ |
450 | |
451 | if (allocation->previous) { |
452 | (allocation->previous)->next = allocation->next; |
453 | } else { |
454 | mem_list->list_head = allocation->next; |
455 | } |
456 | |
457 | if (allocation->next) { |
458 | (allocation->next)->previous = allocation->previous; |
459 | } |
460 | |
461 | ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Freeing %p, size 0%X\n" , |
462 | &allocation->user_space, allocation->size)); |
463 | |
464 | /* Mark the segment as deleted */ |
465 | |
466 | memset(&allocation->user_space, 0xEA, allocation->size); |
467 | |
468 | status = acpi_ut_release_mutex(ACPI_MTX_MEMORY); |
469 | return (status); |
470 | } |
471 | |
472 | /******************************************************************************* |
473 | * |
474 | * FUNCTION: acpi_ut_dump_allocation_info |
475 | * |
476 | * PARAMETERS: None |
477 | * |
478 | * RETURN: None |
479 | * |
480 | * DESCRIPTION: Print some info about the outstanding allocations. |
481 | * |
482 | ******************************************************************************/ |
483 | |
484 | void acpi_ut_dump_allocation_info(void) |
485 | { |
486 | /* |
487 | struct acpi_memory_list *mem_list; |
488 | */ |
489 | |
490 | ACPI_FUNCTION_TRACE(ut_dump_allocation_info); |
491 | |
492 | /* |
493 | ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, |
494 | ("%30s: %4d (%3d Kb)\n", "Current allocations", |
495 | mem_list->current_count, |
496 | ROUND_UP_TO_1K (mem_list->current_size))); |
497 | |
498 | ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, |
499 | ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations", |
500 | mem_list->max_concurrent_count, |
501 | ROUND_UP_TO_1K (mem_list->max_concurrent_size))); |
502 | |
503 | ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, |
504 | ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects", |
505 | running_object_count, |
506 | ROUND_UP_TO_1K (running_object_size))); |
507 | |
508 | ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, |
509 | ("%30s: %4d (%3d Kb)\n", "Total (all) allocations", |
510 | running_alloc_count, |
511 | ROUND_UP_TO_1K (running_alloc_size))); |
512 | |
513 | ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, |
514 | ("%30s: %4d (%3d Kb)\n", "Current Nodes", |
515 | acpi_gbl_current_node_count, |
516 | ROUND_UP_TO_1K (acpi_gbl_current_node_size))); |
517 | |
518 | ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, |
519 | ("%30s: %4d (%3d Kb)\n", "Max Nodes", |
520 | acpi_gbl_max_concurrent_node_count, |
521 | ROUND_UP_TO_1K ((acpi_gbl_max_concurrent_node_count * |
522 | sizeof (struct acpi_namespace_node))))); |
523 | */ |
524 | return_VOID; |
525 | } |
526 | |
527 | /******************************************************************************* |
528 | * |
529 | * FUNCTION: acpi_ut_dump_allocations |
530 | * |
531 | * PARAMETERS: component - Component(s) to dump info for. |
532 | * module - Module to dump info for. NULL means all. |
533 | * |
534 | * RETURN: None |
535 | * |
536 | * DESCRIPTION: Print a list of all outstanding allocations. |
537 | * |
538 | ******************************************************************************/ |
539 | |
540 | void acpi_ut_dump_allocations(u32 component, const char *module) |
541 | { |
542 | struct acpi_debug_mem_block *element; |
543 | union acpi_descriptor *descriptor; |
544 | u32 num_outstanding = 0; |
545 | u8 descriptor_type; |
546 | |
547 | ACPI_FUNCTION_TRACE(ut_dump_allocations); |
548 | |
549 | if (acpi_gbl_disable_mem_tracking) { |
550 | return_VOID; |
551 | } |
552 | |
553 | /* |
554 | * Walk the allocation list. |
555 | */ |
556 | if (ACPI_FAILURE(acpi_ut_acquire_mutex(ACPI_MTX_MEMORY))) { |
557 | return_VOID; |
558 | } |
559 | |
560 | if (!acpi_gbl_global_list) { |
561 | goto exit; |
562 | } |
563 | |
564 | element = acpi_gbl_global_list->list_head; |
565 | while (element) { |
566 | if ((element->component & component) && |
567 | ((module == NULL) |
568 | || (0 == strcmp(module, element->module)))) { |
569 | descriptor = |
570 | ACPI_CAST_PTR(union acpi_descriptor, |
571 | &element->user_space); |
572 | |
573 | if (element->size < |
574 | sizeof(struct acpi_common_descriptor)) { |
575 | acpi_os_printf("%p Length 0x%04X %9.9s-%4.4u " |
576 | "[Not a Descriptor - too small]\n" , |
577 | descriptor, element->size, |
578 | element->module, element->line); |
579 | } else { |
580 | /* Ignore allocated objects that are in a cache */ |
581 | |
582 | if (ACPI_GET_DESCRIPTOR_TYPE(descriptor) != |
583 | ACPI_DESC_TYPE_CACHED) { |
584 | acpi_os_printf |
585 | ("%p Length 0x%04X %9.9s-%4.4u [%s] " , |
586 | descriptor, element->size, |
587 | element->module, element->line, |
588 | acpi_ut_get_descriptor_name |
589 | (descriptor)); |
590 | |
591 | /* Optional object hex dump */ |
592 | |
593 | if (acpi_gbl_verbose_leak_dump) { |
594 | acpi_os_printf("\n" ); |
595 | acpi_ut_dump_buffer((u8 *) |
596 | descriptor, |
597 | element-> |
598 | size, |
599 | DB_BYTE_DISPLAY, |
600 | 0); |
601 | } |
602 | |
603 | /* Validate the descriptor type using Type field and length */ |
604 | |
605 | descriptor_type = 0; /* Not a valid descriptor type */ |
606 | |
607 | switch (ACPI_GET_DESCRIPTOR_TYPE |
608 | (descriptor)) { |
609 | case ACPI_DESC_TYPE_OPERAND: |
610 | |
611 | if (element->size == |
612 | sizeof(union |
613 | acpi_operand_object)) |
614 | { |
615 | descriptor_type = |
616 | ACPI_DESC_TYPE_OPERAND; |
617 | } |
618 | break; |
619 | |
620 | case ACPI_DESC_TYPE_PARSER: |
621 | |
622 | if (element->size == |
623 | sizeof(union |
624 | acpi_parse_object)) { |
625 | descriptor_type = |
626 | ACPI_DESC_TYPE_PARSER; |
627 | } |
628 | break; |
629 | |
630 | case ACPI_DESC_TYPE_NAMED: |
631 | |
632 | if (element->size == |
633 | sizeof(struct |
634 | acpi_namespace_node)) |
635 | { |
636 | descriptor_type = |
637 | ACPI_DESC_TYPE_NAMED; |
638 | } |
639 | break; |
640 | |
641 | default: |
642 | |
643 | break; |
644 | } |
645 | |
646 | /* Display additional info for the major descriptor types */ |
647 | |
648 | switch (descriptor_type) { |
649 | case ACPI_DESC_TYPE_OPERAND: |
650 | |
651 | acpi_os_printf |
652 | ("%12.12s RefCount 0x%04X\n" , |
653 | acpi_ut_get_type_name |
654 | (descriptor->object.common. |
655 | type), |
656 | descriptor->object.common. |
657 | reference_count); |
658 | break; |
659 | |
660 | case ACPI_DESC_TYPE_PARSER: |
661 | |
662 | acpi_os_printf |
663 | ("AmlOpcode 0x%04X\n" , |
664 | descriptor->op.asl. |
665 | aml_opcode); |
666 | break; |
667 | |
668 | case ACPI_DESC_TYPE_NAMED: |
669 | |
670 | acpi_os_printf("%4.4s\n" , |
671 | acpi_ut_get_node_name |
672 | (&descriptor-> |
673 | node)); |
674 | break; |
675 | |
676 | default: |
677 | |
678 | acpi_os_printf("\n" ); |
679 | break; |
680 | } |
681 | } |
682 | } |
683 | |
684 | num_outstanding++; |
685 | } |
686 | |
687 | element = element->next; |
688 | } |
689 | |
690 | exit: |
691 | (void)acpi_ut_release_mutex(ACPI_MTX_MEMORY); |
692 | |
693 | /* Print summary */ |
694 | |
695 | if (!num_outstanding) { |
696 | ACPI_INFO(("No outstanding allocations" )); |
697 | } else { |
698 | ACPI_ERROR((AE_INFO, "%u (0x%X) Outstanding cache allocations" , |
699 | num_outstanding, num_outstanding)); |
700 | } |
701 | |
702 | return_VOID; |
703 | } |
704 | |
705 | #endif /* ACPI_DBG_TRACK_ALLOCATIONS */ |
706 | |