xref: /haiku/src/system/kernel/heap.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2008-2009, Michael Lotz, mmlr@mlotz.ch.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de.
6  * Distributed under the terms of the MIT License.
7  *
8  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
9  * Distributed under the terms of the NewOS License.
10  */
11 
12 #include <arch/debug.h>
13 #include <debug.h>
14 #include <elf.h>
15 #include <heap.h>
16 #include <int.h>
17 #include <kernel.h>
18 #include <lock.h>
19 #include <malloc.h>
20 #include <signal.h>
21 #include <string.h>
22 #include <team.h>
23 #include <thread.h>
24 #include <tracing.h>
25 #include <util/AutoLock.h>
26 #include <vm.h>
27 
28 
29 //#define TRACE_HEAP
30 #ifdef TRACE_HEAP
31 #	define TRACE(x) dprintf x
32 #else
33 #	define TRACE(x) ;
34 #endif
35 
36 
37 #if KERNEL_HEAP_LEAK_CHECK
38 typedef struct heap_leak_check_info_s {
39 	addr_t		caller;
40 	size_t		size;
41 	thread_id	thread;
42 	team_id		team;
43 } heap_leak_check_info;
44 
45 struct caller_info {
46 	addr_t		caller;
47 	uint32		count;
48 	uint32		size;
49 };
50 
51 static const int32 kCallerInfoTableSize = 1024;
52 static caller_info sCallerInfoTable[kCallerInfoTableSize];
53 static int32 sCallerInfoCount = 0;
54 #endif
55 
56 typedef struct heap_page_s heap_page;
57 
58 typedef struct heap_area_s {
59 	area_id			area;
60 
61 	addr_t			base;
62 	size_t			size;
63 
64 	uint32			page_count;
65 	uint32			free_page_count;
66 
67 	heap_page *		free_pages;
68 	heap_page *		page_table;
69 
70 	heap_area_s *	prev;
71 	heap_area_s *	next;
72 	heap_area_s *	all_next;
73 } heap_area;
74 
75 typedef struct heap_page_s {
76 	heap_area *		area;
77 	uint16			index;
78 	uint16			bin_index : 5;
79 	uint16			free_count : 10;
80 	uint16			in_use : 1;
81 	heap_page_s *	next;
82 	heap_page_s *	prev;
83 	union {
84 		uint16			empty_index;
85 		uint16			allocation_id; // used for bin == bin_count allocations
86 	};
87 	addr_t *		free_list;
88 } heap_page;
89 
90 typedef struct heap_bin_s {
91 	mutex		lock;
92 	uint32		element_size;
93 	uint16		max_free_count;
94 	heap_page *	page_list; // sorted so that the desired page is always first
95 } heap_bin;
96 
97 struct heap_allocator_s {
98 	rw_lock		area_lock;
99 	mutex		page_lock;
100 
101 	const char *name;
102 	uint32		bin_count;
103 	uint32		page_size;
104 
105 	uint32		total_pages;
106 	uint32		total_free_pages;
107 	uint32		empty_areas;
108 
109 #if KERNEL_HEAP_LEAK_CHECK
110 	addr_t		(*get_caller)();
111 #endif
112 
113 	heap_bin *	bins;
114 	heap_area *	areas; // sorted so that the desired area is always first
115 	heap_area *	all_areas; // all areas including full ones
116 };
117 
118 static const uint32 kAreaAllocationMagic = 'AAMG';
119 typedef struct area_allocation_info_s {
120 	area_id		area;
121 	void *		base;
122 	uint32		magic;
123 	size_t		size;
124 	size_t		allocation_size;
125 	size_t		allocation_alignment;
126 	void *		allocation_base;
127 } area_allocation_info;
128 
129 struct DeferredFreeListEntry : SinglyLinkedListLinkImpl<DeferredFreeListEntry> {
130 };
131 
132 typedef SinglyLinkedList<DeferredFreeListEntry> DeferredFreeList;
133 typedef SinglyLinkedList<DeferredDeletable> DeferredDeletableList;
134 
135 // Heap class configuration
136 #define HEAP_CLASS_COUNT 3
137 static heap_class sHeapClasses[HEAP_CLASS_COUNT] = {
138 	{
139 		"small",					/* name */
140 		50,							/* initial percentage */
141 		B_PAGE_SIZE / 8,			/* max allocation size */
142 		B_PAGE_SIZE,				/* page size */
143 		8,							/* min bin size */
144 		4,							/* bin alignment */
145 		8,							/* min count per page */
146 		16							/* max waste per page */
147 	},
148 	{
149 		"medium",					/* name */
150 		30,							/* initial percentage */
151 		B_PAGE_SIZE * 2,			/* max allocation size */
152 		B_PAGE_SIZE * 8,			/* page size */
153 		B_PAGE_SIZE / 8,			/* min bin size */
154 		32,							/* bin alignment */
155 		4,							/* min count per page */
156 		64							/* max waste per page */
157 	},
158 	{
159 		"large",					/* name */
160 		20,							/* initial percentage */
161 		HEAP_AREA_USE_THRESHOLD,	/* max allocation size */
162 		B_PAGE_SIZE * 16,			/* page size */
163 		B_PAGE_SIZE * 2,			/* min bin size */
164 		128,						/* bin alignment */
165 		1,							/* min count per page */
166 		256							/* max waste per page */
167 	}
168 };
169 
170 static heap_allocator *sHeaps[HEAP_CLASS_COUNT];
171 static uint32 *sLastGrowRequest[HEAP_CLASS_COUNT];
172 static uint32 *sLastHandledGrowRequest[HEAP_CLASS_COUNT];
173 static heap_allocator *sGrowHeap = NULL;
174 static thread_id sHeapGrowThread = -1;
175 static sem_id sHeapGrowSem = -1;
176 static sem_id sHeapGrownNotify = -1;
177 static bool sAddGrowHeap = false;
178 
179 static DeferredFreeList sDeferredFreeList;
180 static DeferredDeletableList sDeferredDeletableList;
181 static spinlock sDeferredFreeListLock;
182 
183 
184 
185 // #pragma mark - Tracing
186 
187 #if KERNEL_HEAP_TRACING
188 namespace KernelHeapTracing {
189 
190 class Allocate : public AbstractTraceEntry {
191 	public:
192 		Allocate(addr_t address, size_t size)
193 			:	fAddress(address),
194 				fSize(size)
195 		{
196 			Initialized();
197 		}
198 
199 		virtual void AddDump(TraceOutput &out)
200 		{
201 			out.Print("heap allocate: 0x%08lx (%lu bytes)", fAddress, fSize);
202 		}
203 
204 	private:
205 		addr_t	fAddress;
206 		size_t	fSize;
207 };
208 
209 
210 class Reallocate : public AbstractTraceEntry {
211 	public:
212 		Reallocate(addr_t oldAddress, addr_t newAddress, size_t newSize)
213 			:	fOldAddress(oldAddress),
214 				fNewAddress(newAddress),
215 				fNewSize(newSize)
216 		{
217 			Initialized();
218 		};
219 
220 		virtual void AddDump(TraceOutput &out)
221 		{
222 			out.Print("heap reallocate: 0x%08lx -> 0x%08lx (%lu bytes)",
223 				fOldAddress, fNewAddress, fNewSize);
224 		}
225 
226 	private:
227 		addr_t	fOldAddress;
228 		addr_t	fNewAddress;
229 		size_t	fNewSize;
230 };
231 
232 
233 class Free : public AbstractTraceEntry {
234 	public:
235 		Free(addr_t address)
236 			:	fAddress(address)
237 		{
238 			Initialized();
239 		};
240 
241 		virtual void AddDump(TraceOutput &out)
242 		{
243 			out.Print("heap free: 0x%08lx", fAddress);
244 		}
245 
246 	private:
247 		addr_t	fAddress;
248 };
249 
250 
251 } // namespace KernelHeapTracing
252 
253 #	define T(x)	if (!gKernelStartup) new(std::nothrow) KernelHeapTracing::x;
254 #else
255 #	define T(x)	;
256 #endif
257 
258 
259 // #pragma mark - Debug functions
260 
261 
262 #if KERNEL_HEAP_LEAK_CHECK
263 static addr_t
264 get_caller()
265 {
266 	// Find the first return address outside of the allocator code. Note, that
267 	// this makes certain assumptions about how the code for the functions
268 	// ends up in the kernel object.
269 	addr_t returnAddresses[5];
270 	int32 depth = arch_debug_get_stack_trace(returnAddresses, 5, 0, 1, false);
271 	for (int32 i = 0; i < depth; i++) {
272 		if (returnAddresses[i] < (addr_t)&get_caller
273 			|| returnAddresses[i] > (addr_t)&malloc_referenced_release) {
274 			return returnAddresses[i];
275 		}
276 	}
277 
278 	return 0;
279 }
280 #endif
281 
282 
283 static void
284 dump_page(heap_page *page)
285 {
286 	uint32 count = 0;
287 	for (addr_t *temp = page->free_list; temp != NULL; temp = (addr_t *)*temp)
288 		count++;
289 
290 	kprintf("\t\tpage %p: bin_index: %u; free_count: %u; empty_index: %u; "
291 		"free_list %p (%lu entr%s)\n", page, page->bin_index, page->free_count,
292 		page->empty_index, page->free_list, count, count == 1 ? "y" : "ies");
293 }
294 
295 
296 static void
297 dump_bin(heap_bin *bin)
298 {
299 	kprintf("\telement_size: %lu; max_free_count: %u; page_list %p;\n",
300 		bin->element_size, bin->max_free_count, bin->page_list);
301 
302 	for (heap_page *temp = bin->page_list; temp != NULL; temp = temp->next)
303 		dump_page(temp);
304 }
305 
306 
307 static void
308 dump_bin_list(heap_allocator *heap)
309 {
310 	for (uint32 i = 0; i < heap->bin_count; i++)
311 		dump_bin(&heap->bins[i]);
312 	kprintf("\n");
313 }
314 
315 
316 static void
317 dump_allocator_areas(heap_allocator *heap)
318 {
319 	heap_area *area = heap->all_areas;
320 	while (area) {
321 		kprintf("\tarea %p: area: %ld; base: 0x%08lx; size: %lu; page_count: "
322 			"%lu; free_pages: %p (%lu entr%s)\n", area, area->area, area->base,
323 			area->size, area->page_count, area->free_pages,
324 			area->free_page_count, area->free_page_count == 1 ? "y" : "ies");
325 		area = area->all_next;
326 	}
327 
328 	kprintf("\n");
329 }
330 
331 
332 static void
333 dump_allocator(heap_allocator *heap, bool areas, bool bins)
334 {
335 	kprintf("allocator %p: name: %s; page_size: %lu; bin_count: %lu; pages: "
336 		"%lu; free_pages: %lu; empty_areas: %lu\n", heap, heap->name,
337 		heap->page_size, heap->bin_count, heap->total_pages,
338 		heap->total_free_pages, heap->empty_areas);
339 
340 	if (areas)
341 		dump_allocator_areas(heap);
342 	if (bins)
343 		dump_bin_list(heap);
344 }
345 
346 
347 static int
348 dump_heap_list(int argc, char **argv)
349 {
350 	if (argc == 2) {
351 		uint64 heapAddress;
352 
353 		if (strcmp(argv[1], "grow") == 0) {
354 			// only dump dedicated grow heap info
355 			kprintf("dedicated grow heap:\n");
356 			dump_allocator(sGrowHeap, true, true);
357 		} else if (strcmp(argv[1], "stats") == 0) {
358 			for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++)
359 				dump_allocator(sHeaps[i], false, false);
360 		} else if (evaluate_debug_expression(argv[1], &heapAddress, true)) {
361 			dump_allocator((heap_allocator*)(addr_t)heapAddress, true, true);
362 		} else
363 			print_debugger_command_usage(argv[0]);
364 
365 		return 0;
366 	}
367 
368 	for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++)
369 		dump_allocator(sHeaps[i], true, true);
370 
371 	return 0;
372 }
373 
374 
375 #if !KERNEL_HEAP_LEAK_CHECK
376 
377 static int
378 dump_allocations(int argc, char **argv)
379 {
380 	uint64 heapAddress = 0;
381 	bool statsOnly = false;
382 	for (int32 i = 1; i < argc; i++) {
383 		if (strcmp(argv[i], "stats") == 0)
384 			statsOnly = true;
385 		else if (!evaluate_debug_expression(argv[i], &heapAddress, true)) {
386 			print_debugger_command_usage(argv[0]);
387 			return 0;
388 		}
389 	}
390 
391 	size_t totalSize = 0;
392 	uint32 totalCount = 0;
393 	for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT; classIndex++) {
394 		heap_allocator *heap = sHeaps[classIndex];
395 		if (heapAddress != 0)
396 			heap = (heap_allocator *)(addr_t)heapAddress;
397 
398 		// go through all the pages in all the areas
399 		heap_area *area = heap->all_areas;
400 		while (area) {
401 			for (uint32 i = 0; i < area->page_count; i++) {
402 				heap_page *page = &area->page_table[i];
403 				if (!page->in_use)
404 					continue;
405 
406 				addr_t base = area->base + i * heap->page_size;
407 				if (page->bin_index < heap->bin_count) {
408 					// page is used by a small allocation bin
409 					uint32 elementCount = page->empty_index;
410 					size_t elementSize
411 						= heap->bins[page->bin_index].element_size;
412 					for (uint32 j = 0; j < elementCount;
413 							j++, base += elementSize) {
414 						// walk the free list to see if this element is in use
415 						bool elementInUse = true;
416 						for (addr_t *temp = page->free_list; temp != NULL;
417 								temp = (addr_t *)*temp) {
418 							if ((addr_t)temp == base) {
419 								elementInUse = false;
420 								break;
421 							}
422 						}
423 
424 						if (!elementInUse)
425 							continue;
426 
427 						if (!statsOnly) {
428 							kprintf("address: 0x%08lx; size: %lu bytes\n",
429 								base, elementSize);
430 						}
431 
432 						totalSize += elementSize;
433 						totalCount++;
434 					}
435 				} else {
436 					// page is used by a big allocation, find the page count
437 					uint32 pageCount = 1;
438 					while (i + pageCount < area->page_count
439 						&& area->page_table[i + pageCount].in_use
440 						&& area->page_table[i + pageCount].bin_index
441 							== heap->bin_count
442 						&& area->page_table[i + pageCount].allocation_id
443 							== page->allocation_id)
444 						pageCount++;
445 
446 					size_t size = pageCount * heap->page_size;
447 
448 					if (!statsOnly) {
449 						kprintf("address: 0x%08lx; size: %lu bytes\n", base,
450 							size);
451 					}
452 
453 					totalSize += size;
454 					totalCount++;
455 
456 					// skip the allocated pages
457 					i += pageCount - 1;
458 				}
459 			}
460 
461 			area = area->all_next;
462 		}
463 
464 		if (heapAddress != 0)
465 			break;
466 	}
467 
468 	kprintf("total allocations: %lu; total bytes: %lu\n", totalCount, totalSize);
469 	return 0;
470 }
471 
472 #else // !KERNEL_HEAP_LEAK_CHECK
473 
474 static int
475 dump_allocations(int argc, char **argv)
476 {
477 	team_id team = -1;
478 	thread_id thread = -1;
479 	addr_t caller = 0;
480 	bool statsOnly = false;
481 	for (int32 i = 1; i < argc; i++) {
482 		if (strcmp(argv[i], "team") == 0)
483 			team = strtoul(argv[++i], NULL, 0);
484 		else if (strcmp(argv[i], "thread") == 0)
485 			thread = strtoul(argv[++i], NULL, 0);
486 		else if (strcmp(argv[i], "caller") == 0)
487 			caller = strtoul(argv[++i], NULL, 0);
488 		else if (strcmp(argv[i], "stats") == 0)
489 			statsOnly = true;
490 		else {
491 			print_debugger_command_usage(argv[0]);
492 			return 0;
493 		}
494 	}
495 
496 	size_t totalSize = 0;
497 	uint32 totalCount = 0;
498 	for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT; classIndex++) {
499 		heap_allocator *heap = sHeaps[classIndex];
500 
501 		// go through all the pages in all the areas
502 		heap_area *area = heap->all_areas;
503 		while (area) {
504 			heap_leak_check_info *info = NULL;
505 			for (uint32 i = 0; i < area->page_count; i++) {
506 				heap_page *page = &area->page_table[i];
507 				if (!page->in_use)
508 					continue;
509 
510 				addr_t base = area->base + i * heap->page_size;
511 				if (page->bin_index < heap->bin_count) {
512 					// page is used by a small allocation bin
513 					uint32 elementCount = page->empty_index;
514 					size_t elementSize
515 						= heap->bins[page->bin_index].element_size;
516 					for (uint32 j = 0; j < elementCount;
517 							j++, base += elementSize) {
518 						// walk the free list to see if this element is in use
519 						bool elementInUse = true;
520 						for (addr_t *temp = page->free_list; temp != NULL;
521 								temp = (addr_t *)*temp) {
522 							if ((addr_t)temp == base) {
523 								elementInUse = false;
524 								break;
525 							}
526 						}
527 
528 						if (!elementInUse)
529 							continue;
530 
531 						info = (heap_leak_check_info *)(base + elementSize
532 							- sizeof(heap_leak_check_info));
533 
534 						if ((team == -1 || info->team == team)
535 							&& (thread == -1 || info->thread == thread)
536 							&& (caller == 0 || info->caller == caller)) {
537 							// interesting...
538 							if (!statsOnly) {
539 								kprintf("team: % 6ld; thread: % 6ld; "
540 									"address: 0x%08lx; size: %lu bytes; "
541 									"caller: %#lx\n", info->team, info->thread,
542 									base, info->size, info->caller);
543 							}
544 
545 							totalSize += info->size;
546 							totalCount++;
547 						}
548 					}
549 				} else {
550 					// page is used by a big allocation, find the page count
551 					uint32 pageCount = 1;
552 					while (i + pageCount < area->page_count
553 						&& area->page_table[i + pageCount].in_use
554 						&& area->page_table[i + pageCount].bin_index
555 							== heap->bin_count
556 						&& area->page_table[i + pageCount].allocation_id
557 							== page->allocation_id)
558 						pageCount++;
559 
560 					info = (heap_leak_check_info *)(base + pageCount
561 						* heap->page_size - sizeof(heap_leak_check_info));
562 
563 					if ((team == -1 || info->team == team)
564 						&& (thread == -1 || info->thread == thread)
565 						&& (caller == 0 || info->caller == caller)) {
566 						// interesting...
567 						if (!statsOnly) {
568 							kprintf("team: % 6ld; thread: % 6ld;"
569 								" address: 0x%08lx; size: %lu bytes;"
570 								" caller: %#lx\n", info->team, info->thread,
571 								base, info->size, info->caller);
572 						}
573 
574 						totalSize += info->size;
575 						totalCount++;
576 					}
577 
578 					// skip the allocated pages
579 					i += pageCount - 1;
580 				}
581 			}
582 
583 			area = area->all_next;
584 		}
585 	}
586 
587 	kprintf("total allocations: %lu; total bytes: %lu\n", totalCount, totalSize);
588 	return 0;
589 }
590 
591 
592 static caller_info*
593 get_caller_info(addr_t caller)
594 {
595 	// find the caller info
596 	for (int32 i = 0; i < sCallerInfoCount; i++) {
597 		if (caller == sCallerInfoTable[i].caller)
598 			return &sCallerInfoTable[i];
599 	}
600 
601 	// not found, add a new entry, if there are free slots
602 	if (sCallerInfoCount >= kCallerInfoTableSize)
603 		return NULL;
604 
605 	caller_info* info = &sCallerInfoTable[sCallerInfoCount++];
606 	info->caller = caller;
607 	info->count = 0;
608 	info->size = 0;
609 
610 	return info;
611 }
612 
613 
614 static int
615 caller_info_compare_size(const void* _a, const void* _b)
616 {
617 	const caller_info* a = (const caller_info*)_a;
618 	const caller_info* b = (const caller_info*)_b;
619 	return (int)(b->size - a->size);
620 }
621 
622 
623 static int
624 caller_info_compare_count(const void* _a, const void* _b)
625 {
626 	const caller_info* a = (const caller_info*)_a;
627 	const caller_info* b = (const caller_info*)_b;
628 	return (int)(b->count - a->count);
629 }
630 
631 
632 static bool
633 analyze_allocation_callers(heap_allocator *heap)
634 {
635 	// go through all the pages in all the areas
636 	heap_area *area = heap->all_areas;
637 	while (area) {
638 		heap_leak_check_info *info = NULL;
639 		for (uint32 i = 0; i < area->page_count; i++) {
640 			heap_page *page = &area->page_table[i];
641 			if (!page->in_use)
642 				continue;
643 
644 			addr_t base = area->base + i * heap->page_size;
645 			if (page->bin_index < heap->bin_count) {
646 				// page is used by a small allocation bin
647 				uint32 elementCount = page->empty_index;
648 				size_t elementSize = heap->bins[page->bin_index].element_size;
649 				for (uint32 j = 0; j < elementCount; j++, base += elementSize) {
650 					// walk the free list to see if this element is in use
651 					bool elementInUse = true;
652 					for (addr_t *temp = page->free_list; temp != NULL;
653 						temp = (addr_t *)*temp) {
654 						if ((addr_t)temp == base) {
655 							elementInUse = false;
656 							break;
657 						}
658 					}
659 
660 					if (!elementInUse)
661 						continue;
662 
663 					info = (heap_leak_check_info *)(base + elementSize
664 						- sizeof(heap_leak_check_info));
665 
666 					caller_info *callerInfo = get_caller_info(info->caller);
667 					if (callerInfo == NULL) {
668 						kprintf("out of space for caller infos\n");
669 						return 0;
670 					}
671 
672 					callerInfo->count++;
673 					callerInfo->size += info->size;
674 				}
675 			} else {
676 				// page is used by a big allocation, find the page count
677 				uint32 pageCount = 1;
678 				while (i + pageCount < area->page_count
679 					&& area->page_table[i + pageCount].in_use
680 					&& area->page_table[i + pageCount].bin_index
681 						== heap->bin_count
682 					&& area->page_table[i + pageCount].allocation_id
683 						== page->allocation_id) {
684 					pageCount++;
685 				}
686 
687 				info = (heap_leak_check_info *)(base + pageCount
688 					* heap->page_size - sizeof(heap_leak_check_info));
689 
690 				caller_info *callerInfo = get_caller_info(info->caller);
691 				if (callerInfo == NULL) {
692 					kprintf("out of space for caller infos\n");
693 					return false;
694 				}
695 
696 				callerInfo->count++;
697 				callerInfo->size += info->size;
698 
699 				// skip the allocated pages
700 				i += pageCount - 1;
701 			}
702 		}
703 
704 		area = area->all_next;
705 	}
706 
707 	return true;
708 }
709 
710 
711 static int
712 dump_allocations_per_caller(int argc, char **argv)
713 {
714 	bool sortBySize = true;
715 	heap_allocator *heap = NULL;
716 
717 	for (int32 i = 1; i < argc; i++) {
718 		if (strcmp(argv[i], "-c") == 0) {
719 			sortBySize = false;
720 		} else if (strcmp(argv[i], "-h") == 0) {
721 			uint64 heapAddress;
722 			if (++i >= argc
723 				|| !evaluate_debug_expression(argv[i], &heapAddress, true)) {
724 				print_debugger_command_usage(argv[0]);
725 				return 0;
726 			}
727 
728 			heap = (heap_allocator*)(addr_t)heapAddress;
729 		} else {
730 			print_debugger_command_usage(argv[0]);
731 			return 0;
732 		}
733 	}
734 
735 	sCallerInfoCount = 0;
736 
737 	if (heap != NULL) {
738 		if (!analyze_allocation_callers(heap))
739 			return 0;
740 	} else {
741 		for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT;
742 				classIndex++) {
743 			if (!analyze_allocation_callers(sHeaps[classIndex]))
744 				return 0;
745 		}
746 	}
747 
748 	// sort the array
749 	qsort(sCallerInfoTable, sCallerInfoCount, sizeof(caller_info),
750 		sortBySize ? &caller_info_compare_size : &caller_info_compare_count);
751 
752 	kprintf("%ld different callers, sorted by %s...\n\n", sCallerInfoCount,
753 		sortBySize ? "size" : "count");
754 
755 	kprintf("     count        size      caller\n");
756 	kprintf("----------------------------------\n");
757 	for (int32 i = 0; i < sCallerInfoCount; i++) {
758 		caller_info& info = sCallerInfoTable[i];
759 		kprintf("%10ld  %10ld  %#08lx", info.count, info.size, info.caller);
760 
761 		const char *symbol;
762 		const char *imageName;
763 		bool exactMatch;
764 		addr_t baseAddress;
765 
766 		if (elf_debug_lookup_symbol_address(info.caller, &baseAddress, &symbol,
767 				&imageName, &exactMatch) == B_OK) {
768 			kprintf("  %s + 0x%lx (%s)%s\n", symbol,
769 				info.caller - baseAddress, imageName,
770 				exactMatch ? "" : " (nearest)");
771 		} else
772 			kprintf("\n");
773 	}
774 
775 	return 0;
776 }
777 
778 #endif // KERNEL_HEAP_LEAK_CHECK
779 
780 
781 #if PARANOID_HEAP_VALIDATION
782 static void
783 heap_validate_heap(heap_allocator *heap)
784 {
785 	ReadLocker areaReadLocker(heap->area_lock);
786 	for (uint32 i = 0; i < heap->bin_count; i++)
787 		mutex_lock(&heap->bins[i].lock);
788 	MutexLocker pageLocker(heap->page_lock);
789 
790 	uint32 totalPageCount = 0;
791 	uint32 totalFreePageCount = 0;
792 	heap_area *area = heap->all_areas;
793 	while (area != NULL) {
794 		// validate the free pages list
795 		uint32 freePageCount = 0;
796 		heap_page *lastPage = NULL;
797 		heap_page *page = area->free_pages;
798 		while (page) {
799 			if ((addr_t)page < (addr_t)&area->page_table[0]
800 				|| (addr_t)page >= (addr_t)&area->page_table[area->page_count])
801 				panic("free page is not part of the page table\n");
802 
803 			if (page->index >= area->page_count)
804 				panic("free page has invalid index\n");
805 
806 			if ((addr_t)&area->page_table[page->index] != (addr_t)page)
807 				panic("free page index does not lead to target page\n");
808 
809 			if (page->prev != lastPage)
810 				panic("free page entry has invalid prev link\n");
811 
812 			if (page->in_use)
813 				panic("free page marked as in use\n");
814 
815 			lastPage = page;
816 			page = page->next;
817 			freePageCount++;
818 		}
819 
820 		totalPageCount += freePageCount;
821 		totalFreePageCount += freePageCount;
822 		if (area->free_page_count != freePageCount)
823 			panic("free page count doesn't match free page list\n");
824 
825 		// validate the page table
826 		uint32 usedPageCount = 0;
827 		for (uint32 i = 0; i < area->page_count; i++) {
828 			if (area->page_table[i].in_use)
829 				usedPageCount++;
830 		}
831 
832 		totalPageCount += usedPageCount;
833 		if (freePageCount + usedPageCount != area->page_count) {
834 			panic("free pages and used pages do not add up (%lu + %lu != %lu)\n",
835 				freePageCount, usedPageCount, area->page_count);
836 		}
837 
838 		area = area->all_next;
839 	}
840 
841 	// validate the areas
842 	area = heap->areas;
843 	heap_area *lastArea = NULL;
844 	uint32 lastFreeCount = 0;
845 	while (area != NULL) {
846 		if (area->free_page_count < lastFreeCount)
847 			panic("size ordering of area list broken\n");
848 
849 		if (area->prev != lastArea)
850 			panic("area list entry has invalid prev link\n");
851 
852 		lastArea = area;
853 		lastFreeCount = area->free_page_count;
854 		area = area->next;
855 	}
856 
857 	lastArea = NULL;
858 	area = heap->all_areas;
859 	while (area != NULL) {
860 		if (lastArea != NULL && lastArea->base < area->base)
861 			panic("base ordering of all_areas list broken\n");
862 
863 		lastArea = area;
864 		area = area->all_next;
865 	}
866 
867 	// validate the bins
868 	for (uint32 i = 0; i < heap->bin_count; i++) {
869 		heap_bin *bin = &heap->bins[i];
870 		heap_page *lastPage = NULL;
871 		heap_page *page = bin->page_list;
872 		lastFreeCount = 0;
873 		while (page) {
874 			area = heap->all_areas;
875 			while (area) {
876 				if (area == page->area)
877 					break;
878 				area = area->all_next;
879 			}
880 
881 			if (area == NULL) {
882 				panic("page area not present in area list\n");
883 				page = page->next;
884 				continue;
885 			}
886 
887 			if ((addr_t)page < (addr_t)&area->page_table[0]
888 				|| (addr_t)page >= (addr_t)&area->page_table[area->page_count])
889 				panic("used page is not part of the page table\n");
890 
891 			if (page->index >= area->page_count)
892 				panic("used page has invalid index\n");
893 
894 			if ((addr_t)&area->page_table[page->index] != (addr_t)page)
895 				panic("used page index does not lead to target page\n");
896 
897 			if (page->prev != lastPage) {
898 				panic("used page entry has invalid prev link (%p vs %p bin "
899 					"%lu)\n", page->prev, lastPage, i);
900 			}
901 
902 			if (!page->in_use)
903 				panic("used page marked as not in use\n");
904 
905 			if (page->bin_index != i) {
906 				panic("used page with bin index %u in page list of bin %lu\n",
907 					page->bin_index, i);
908 			}
909 
910 			if (page->free_count < lastFreeCount)
911 				panic("ordering of bin page list broken\n");
912 
913 			// validate the free list
914 			uint32 freeSlotsCount = 0;
915 			addr_t *element = page->free_list;
916 			addr_t pageBase = area->base + page->index * heap->page_size;
917 			while (element) {
918 				if ((addr_t)element < pageBase
919 					|| (addr_t)element >= pageBase + heap->page_size)
920 					panic("free list entry out of page range\n");
921 
922 				if (((addr_t)element - pageBase) % bin->element_size != 0)
923 					panic("free list entry not on a element boundary\n");
924 
925 				element = (addr_t *)*element;
926 				freeSlotsCount++;
927 			}
928 
929 			uint32 slotCount = bin->max_free_count;
930 			if (page->empty_index > slotCount) {
931 				panic("empty index beyond slot count (%u with %lu slots)\n",
932 					page->empty_index, slotCount);
933 			}
934 
935 			freeSlotsCount += (slotCount - page->empty_index);
936 			if (freeSlotsCount > slotCount)
937 				panic("more free slots than fit into the page\n");
938 
939 			lastPage = page;
940 			lastFreeCount = page->free_count;
941 			page = page->next;
942 		}
943 	}
944 
945 	pageLocker.Unlock();
946 	for (uint32 i = 0; i < heap->bin_count; i++)
947 		mutex_unlock(&heap->bins[i].lock);
948 	areaReadLocker.Unlock();
949 }
950 #endif // PARANOID_HEAP_VALIDATION
951 
952 
953 // #pragma mark - Heap functions
954 
955 
956 static void
957 heap_add_area(heap_allocator *heap, area_id areaID, addr_t base, size_t size)
958 {
959 	heap_area *area = (heap_area *)base;
960 	area->area = areaID;
961 
962 	base += sizeof(heap_area);
963 	size -= sizeof(heap_area);
964 
965 	uint32 pageCount = size / heap->page_size;
966 	size_t pageTableSize = pageCount * sizeof(heap_page);
967 	area->page_table = (heap_page *)base;
968 	base += pageTableSize;
969 	size -= pageTableSize;
970 
971 	// the rest is now actually usable memory (rounded to the next page)
972 	area->base = ROUNDUP(base, B_PAGE_SIZE);
973 	area->size = size & ~(B_PAGE_SIZE - 1);
974 
975 	// now we know the real page count
976 	pageCount = area->size / heap->page_size;
977 	area->page_count = pageCount;
978 
979 	// zero out the page table and fill in page indexes
980 	memset((void *)area->page_table, 0, pageTableSize);
981 	for (uint32 i = 0; i < pageCount; i++) {
982 		area->page_table[i].area = area;
983 		area->page_table[i].index = i;
984 	}
985 
986 	// add all pages up into the free pages list
987 	for (uint32 i = 1; i < pageCount; i++) {
988 		area->page_table[i - 1].next = &area->page_table[i];
989 		area->page_table[i].prev = &area->page_table[i - 1];
990 	}
991 	area->free_pages = &area->page_table[0];
992 	area->free_page_count = pageCount;
993 	area->page_table[0].prev = NULL;
994 	area->next = NULL;
995 
996 	WriteLocker areaWriteLocker(heap->area_lock);
997 	MutexLocker pageLocker(heap->page_lock);
998 	if (heap->areas == NULL) {
999 		// it's the only (empty) area in that heap
1000 		area->prev = NULL;
1001 		heap->areas = area;
1002 	} else {
1003 		// link in this area as the last one as it is completely empty
1004 		heap_area *lastArea = heap->areas;
1005 		while (lastArea->next != NULL)
1006 			lastArea = lastArea->next;
1007 
1008 		lastArea->next = area;
1009 		area->prev = lastArea;
1010 	}
1011 
1012 	// insert this area in the all_areas list so it stays ordered by base
1013 	if (heap->all_areas == NULL || heap->all_areas->base < area->base) {
1014 		area->all_next = heap->all_areas;
1015 		heap->all_areas = area;
1016 	} else {
1017 		heap_area *insert = heap->all_areas;
1018 		while (insert->all_next && insert->all_next->base > area->base)
1019 			insert = insert->all_next;
1020 
1021 		area->all_next = insert->all_next;
1022 		insert->all_next = area;
1023 	}
1024 
1025 	heap->total_pages += area->page_count;
1026 	heap->total_free_pages += area->free_page_count;
1027 
1028 	if (areaID >= 0) {
1029 		// this later on deletable area is yet empty - the empty count will be
1030 		// decremented as soon as this area is used for the first time
1031 		heap->empty_areas++;
1032 	}
1033 
1034 	pageLocker.Unlock();
1035 	areaWriteLocker.Unlock();
1036 
1037 	dprintf("heap_add_area: area %ld added to %s heap %p - usable range 0x%08lx "
1038 		"- 0x%08lx\n", area->area, heap->name, heap, area->base,
1039 		area->base + area->size);
1040 }
1041 
1042 
1043 static status_t
1044 heap_remove_area(heap_allocator *heap, heap_area *area)
1045 {
1046 	if (area->free_page_count != area->page_count) {
1047 		panic("tried removing heap area that has still pages in use");
1048 		return B_ERROR;
1049 	}
1050 
1051 	if (area->prev == NULL && area->next == NULL) {
1052 		panic("tried removing the last non-full heap area");
1053 		return B_ERROR;
1054 	}
1055 
1056 	if (heap->areas == area)
1057 		heap->areas = area->next;
1058 	if (area->prev != NULL)
1059 		area->prev->next = area->next;
1060 	if (area->next != NULL)
1061 		area->next->prev = area->prev;
1062 
1063 	if (heap->all_areas == area)
1064 		heap->all_areas = area->all_next;
1065 	else {
1066 		heap_area *previous = heap->all_areas;
1067 		while (previous) {
1068 			if (previous->all_next == area) {
1069 				previous->all_next = area->all_next;
1070 				break;
1071 			}
1072 
1073 			previous = previous->all_next;
1074 		}
1075 
1076 		if (previous == NULL)
1077 			panic("removing heap area that is not in all list");
1078 	}
1079 
1080 	heap->total_pages -= area->page_count;
1081 	heap->total_free_pages -= area->free_page_count;
1082 
1083 	dprintf("heap_remove_area: area %ld with range 0x%08lx - 0x%08lx removed "
1084 		"from %s heap %p\n", area->area, area->base, area->base + area->size,
1085 		heap->name, heap);
1086 
1087 	return B_OK;
1088 }
1089 
1090 
1091 heap_allocator *
1092 heap_create_allocator(const char *name, addr_t base, size_t size,
1093 	const heap_class *heapClass)
1094 {
1095 	heap_allocator *heap = (heap_allocator *)base;
1096 	base += sizeof(heap_allocator);
1097 	size -= sizeof(heap_allocator);
1098 
1099 	heap->name = name;
1100 	heap->page_size = heapClass->page_size;
1101 	heap->total_pages = heap->total_free_pages = heap->empty_areas = 0;
1102 	heap->areas = heap->all_areas = NULL;
1103 	heap->bins = (heap_bin *)base;
1104 
1105 #if KERNEL_HEAP_LEAK_CHECK
1106 	heap->get_caller = &get_caller;
1107 #endif
1108 
1109 	heap->bin_count = 0;
1110 	size_t binSize = 0, lastSize = 0;
1111 	uint32 count = heap->page_size / heapClass->min_bin_size;
1112 	for (; count >= heapClass->min_count_per_page; count--, lastSize = binSize) {
1113 		binSize = (heap->page_size / count) & ~(heapClass->bin_alignment - 1);
1114 		if (binSize == lastSize)
1115 			continue;
1116 		if (heap->page_size - count * binSize > heapClass->max_waste_per_page)
1117 			continue;
1118 
1119 		heap_bin *bin = &heap->bins[heap->bin_count];
1120 		mutex_init(&bin->lock, "heap bin lock");
1121 		bin->element_size = binSize;
1122 		bin->max_free_count = heap->page_size / binSize;
1123 		bin->page_list = NULL;
1124 		heap->bin_count++;
1125 
1126 		if (heap->bin_count >= 32)
1127 			panic("heap configuration invalid - max bin count reached\n");
1128 	};
1129 
1130 	base += heap->bin_count * sizeof(heap_bin);
1131 	size -= heap->bin_count * sizeof(heap_bin);
1132 
1133 	rw_lock_init(&heap->area_lock, "heap area rw lock");
1134 	mutex_init(&heap->page_lock, "heap page lock");
1135 
1136 	heap_add_area(heap, -1, base, size);
1137 	return heap;
1138 }
1139 
1140 
1141 static inline void
1142 heap_free_pages_added(heap_allocator *heap, heap_area *area, uint32 pageCount)
1143 {
1144 	area->free_page_count += pageCount;
1145 	heap->total_free_pages += pageCount;
1146 
1147 	if (area->free_page_count == pageCount) {
1148 		// we need to add ourselfs to the area list of the heap
1149 		area->prev = NULL;
1150 		area->next = heap->areas;
1151 		if (area->next)
1152 			area->next->prev = area;
1153 		heap->areas = area;
1154 	} else {
1155 		// we might need to move back in the area list
1156 		if (area->next && area->next->free_page_count < area->free_page_count) {
1157 			// move ourselfs so the list stays ordered
1158 			heap_area *insert = area->next;
1159 			while (insert->next
1160 				&& insert->next->free_page_count < area->free_page_count)
1161 				insert = insert->next;
1162 
1163 			if (area->prev)
1164 				area->prev->next = area->next;
1165 			if (area->next)
1166 				area->next->prev = area->prev;
1167 			if (heap->areas == area)
1168 				heap->areas = area->next;
1169 
1170 			area->prev = insert;
1171 			area->next = insert->next;
1172 			if (area->next)
1173 				area->next->prev = area;
1174 			insert->next = area;
1175 		}
1176 	}
1177 
1178 	if (area->free_page_count == area->page_count && area->area >= 0)
1179 		heap->empty_areas++;
1180 }
1181 
1182 
1183 static inline void
1184 heap_free_pages_removed(heap_allocator *heap, heap_area *area, uint32 pageCount)
1185 {
1186 	if (area->free_page_count == area->page_count && area->area >= 0) {
1187 		// this area was completely empty
1188 		heap->empty_areas--;
1189 	}
1190 
1191 	area->free_page_count -= pageCount;
1192 	heap->total_free_pages -= pageCount;
1193 
1194 	if (area->free_page_count == 0) {
1195 		// the area is now full so we remove it from the area list
1196 		if (area->prev)
1197 			area->prev->next = area->next;
1198 		if (area->next)
1199 			area->next->prev = area->prev;
1200 		if (heap->areas == area)
1201 			heap->areas = area->next;
1202 		area->next = area->prev = NULL;
1203 	} else {
1204 		// we might need to move forward in the area list
1205 		if (area->prev && area->prev->free_page_count > area->free_page_count) {
1206 			// move ourselfs so the list stays ordered
1207 			heap_area *insert = area->prev;
1208 			while (insert->prev
1209 				&& insert->prev->free_page_count > area->free_page_count)
1210 				insert = insert->prev;
1211 
1212 			if (area->prev)
1213 				area->prev->next = area->next;
1214 			if (area->next)
1215 				area->next->prev = area->prev;
1216 
1217 			area->prev = insert->prev;
1218 			area->next = insert;
1219 			if (area->prev)
1220 				area->prev->next = area;
1221 			if (heap->areas == insert)
1222 				heap->areas = area;
1223 			insert->prev = area;
1224 		}
1225 	}
1226 }
1227 
1228 
1229 static inline void
1230 heap_link_page(heap_page *page, heap_page **list)
1231 {
1232 	page->prev = NULL;
1233 	page->next = *list;
1234 	if (page->next)
1235 		page->next->prev = page;
1236 	*list = page;
1237 }
1238 
1239 
1240 static inline void
1241 heap_unlink_page(heap_page *page, heap_page **list)
1242 {
1243 	if (page->prev)
1244 		page->prev->next = page->next;
1245 	if (page->next)
1246 		page->next->prev = page->prev;
1247 	if (list && *list == page) {
1248 		*list = page->next;
1249 		if (page->next)
1250 			page->next->prev = NULL;
1251 	}
1252 }
1253 
1254 
1255 static heap_page *
1256 heap_allocate_contiguous_pages(heap_allocator *heap, uint32 pageCount)
1257 {
1258 	MutexLocker pageLocker(heap->page_lock);
1259 	heap_area *area = heap->areas;
1260 	while (area) {
1261 		bool found = false;
1262 		int32 first = -1;
1263 		for (uint32 i = 0; i < area->page_count; i++) {
1264 			if (area->page_table[i].in_use) {
1265 				first = -1;
1266 				continue;
1267 			}
1268 
1269 			if (first > 0) {
1270 				if ((i + 1 - first) == pageCount) {
1271 					found = true;
1272 					break;
1273 				}
1274 			} else
1275 				first = i;
1276 		}
1277 
1278 		if (!found) {
1279 			area = area->next;
1280 			continue;
1281 		}
1282 
1283 		for (uint32 i = first; i < first + pageCount; i++) {
1284 			heap_page *page = &area->page_table[i];
1285 			page->in_use = 1;
1286 			page->bin_index = heap->bin_count;
1287 
1288 			heap_unlink_page(page, &area->free_pages);
1289 
1290 			page->next = page->prev = NULL;
1291 			page->free_list = NULL;
1292 			page->allocation_id = (uint16)first;
1293 		}
1294 
1295 		heap_free_pages_removed(heap, area, pageCount);
1296 		return &area->page_table[first];
1297 	}
1298 
1299 	return NULL;
1300 }
1301 
1302 
1303 static void *
1304 heap_raw_alloc(heap_allocator *heap, size_t size, uint32 binIndex)
1305 {
1306 	TRACE(("raw_alloc: heap %p; size %lu; bin %lu\n", heap, size, binIndex));
1307 	if (binIndex < heap->bin_count) {
1308 		heap_bin *bin = &heap->bins[binIndex];
1309 		MutexLocker binLocker(bin->lock);
1310 
1311 		heap_page *page = bin->page_list;
1312 		if (page == NULL) {
1313 			MutexLocker pageLocker(heap->page_lock);
1314 			heap_area *area = heap->areas;
1315 			if (area == NULL) {
1316 				TRACE(("heap %p: no free pages to allocate %lu bytes\n", heap,
1317 					size));
1318 				return NULL;
1319 			}
1320 
1321 			// by design there are only areas in the list that still have
1322 			// free pages available
1323 			page = area->free_pages;
1324 			area->free_pages = page->next;
1325 			if (page->next)
1326 				page->next->prev = NULL;
1327 
1328 			heap_free_pages_removed(heap, area, 1);
1329 
1330 			if (page->in_use)
1331 				panic("got an in use page from the free pages list\n");
1332 			page->in_use = 1;
1333 
1334 			pageLocker.Unlock();
1335 
1336 			page->bin_index = binIndex;
1337 			page->free_count = bin->max_free_count;
1338 			page->empty_index = 0;
1339 			page->free_list = NULL;
1340 			page->next = page->prev = NULL;
1341 			bin->page_list = page;
1342 		}
1343 
1344 		// we have a page where we have a free slot
1345 		void *address = NULL;
1346 		if (page->free_list) {
1347 			// there's a previously freed entry we can use
1348 			address = page->free_list;
1349 			page->free_list = (addr_t *)*page->free_list;
1350 		} else {
1351 			// the page hasn't been fully allocated so use the next empty_index
1352 			address = (void *)(page->area->base + page->index * heap->page_size
1353 				+ page->empty_index * bin->element_size);
1354 			page->empty_index++;
1355 		}
1356 
1357 		page->free_count--;
1358 		if (page->free_count == 0) {
1359 			// the page is now full so we remove it from the page_list
1360 			bin->page_list = page->next;
1361 			if (page->next)
1362 				page->next->prev = NULL;
1363 			page->next = page->prev = NULL;
1364 		}
1365 
1366 		binLocker.Unlock();
1367 
1368 #if KERNEL_HEAP_LEAK_CHECK
1369 		heap_leak_check_info *info = (heap_leak_check_info *)((addr_t)address
1370 			+ bin->element_size - sizeof(heap_leak_check_info));
1371 		info->size = size - sizeof(heap_leak_check_info);
1372 		info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id());
1373 		info->team = (gKernelStartup ? 0 : team_get_current_team_id());
1374 		info->caller = heap->get_caller();
1375 #endif
1376 		return address;
1377 	}
1378 
1379 	uint32 pageCount = (size + heap->page_size - 1) / heap->page_size;
1380 	heap_page *firstPage = heap_allocate_contiguous_pages(heap, pageCount);
1381 	if (firstPage == NULL) {
1382 		TRACE(("heap %p: found no contiguous pages to allocate %ld bytes\n",
1383 			heap, size));
1384 		return NULL;
1385 	}
1386 
1387 #if KERNEL_HEAP_LEAK_CHECK
1388 	heap_leak_check_info *info = (heap_leak_check_info *)(firstPage->area->base
1389 		+ (firstPage->index + pageCount) * heap->page_size
1390 		- sizeof(heap_leak_check_info));
1391 	info->size = size - sizeof(heap_leak_check_info);
1392 	info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id());
1393 	info->team = (gKernelStartup ? 0 : team_get_current_team_id());
1394 	info->caller = heap->get_caller();
1395 #endif
1396 	return (void *)(firstPage->area->base + firstPage->index * heap->page_size);
1397 }
1398 
1399 
1400 #if DEBUG
1401 static bool
1402 is_valid_alignment(size_t number)
1403 {
1404 	// this cryptic line accepts zero and all powers of two
1405 	return ((~number + 1) | ((number << 1) - 1)) == ~0UL;
1406 }
1407 #endif
1408 
1409 
1410 inline bool
1411 heap_should_grow(heap_allocator *heap)
1412 {
1413 	// suggest growing if there is less than 20% of a grow size available
1414 	return heap->total_free_pages * heap->page_size < HEAP_GROW_SIZE / 5;
1415 }
1416 
1417 
1418 void *
1419 heap_memalign(heap_allocator *heap, size_t alignment, size_t size)
1420 {
1421 	TRACE(("memalign(alignment = %lu, size = %lu)\n", alignment, size));
1422 
1423 #if DEBUG
1424 	if (!is_valid_alignment(alignment))
1425 		panic("memalign() with an alignment which is not a power of 2\n");
1426 #endif
1427 
1428 #if KERNEL_HEAP_LEAK_CHECK
1429 	size += sizeof(heap_leak_check_info);
1430 #endif
1431 
1432 	if (alignment != 0) {
1433 		// TODO: The alignment is done by ensuring that the element size
1434 		// of the target bin is aligned with the requested alignment. This
1435 		// has the problem that it wastes space because a better (smaller) bin
1436 		// could possibly be selected. We should pick the best bin and check
1437 		// if there is an aligned block in the free list or if a new (page
1438 		// aligned) page has to be allocated anyway.
1439 		size = ROUNDUP(size, alignment);
1440 	}
1441 
1442 	uint32 binIndex;
1443 	for (binIndex = 0; binIndex < heap->bin_count; binIndex++) {
1444 		if (size <= heap->bins[binIndex].element_size)
1445 			break;
1446 	}
1447 
1448 	void *address = heap_raw_alloc(heap, size, binIndex);
1449 
1450 #if KERNEL_HEAP_LEAK_CHECK
1451 	size -= sizeof(heap_leak_check_info);
1452 #endif
1453 
1454 	TRACE(("memalign(): asked to allocate %lu bytes, returning pointer %p\n",
1455 		size, address));
1456 
1457 	T(Allocate((addr_t)address, size));
1458 	if (address == NULL)
1459 		return address;
1460 
1461 #if PARANOID_KERNEL_MALLOC
1462 	memset(address, 0xcc, size);
1463 #endif
1464 
1465 #if PARANOID_KERNEL_FREE
1466 	// make sure 0xdeadbeef is cleared if we do not overwrite the memory
1467 	// and the user does not clear it
1468 	if (((uint32 *)address)[1] == 0xdeadbeef)
1469 		((uint32 *)address)[1] = 0xcccccccc;
1470 #endif
1471 
1472 	return address;
1473 }
1474 
1475 
1476 status_t
1477 heap_free(heap_allocator *heap, void *address)
1478 {
1479 	if (address == NULL)
1480 		return B_OK;
1481 
1482 	ReadLocker areaReadLocker(heap->area_lock);
1483 	heap_area *area = heap->all_areas;
1484 	while (area) {
1485 		// since the all_areas list is ordered by base with the biggest
1486 		// base at the top, we need only find the first area with a base
1487 		// smaller than our address to become our only candidate for freeing
1488 		if (area->base <= (addr_t)address) {
1489 			if ((addr_t)address >= area->base + area->size) {
1490 				// none of the other areas can contain the address as the list
1491 				// is ordered
1492 				return B_ENTRY_NOT_FOUND;
1493 			}
1494 
1495 			// this area contains the allocation, we're done searching
1496 			break;
1497 		}
1498 
1499 		area = area->all_next;
1500 	}
1501 
1502 	if (area == NULL) {
1503 		// this address does not belong to us
1504 		return B_ENTRY_NOT_FOUND;
1505 	}
1506 
1507 	TRACE(("free(): asked to free pointer %p\n", address));
1508 
1509 	heap_page *page = &area->page_table[((addr_t)address - area->base)
1510 		/ heap->page_size];
1511 
1512 	TRACE(("free(): page %p: bin_index %d, free_count %d\n", page,
1513 		page->bin_index, page->free_count));
1514 
1515 	if (page->bin_index > heap->bin_count) {
1516 		panic("free(): page %p: invalid bin_index %d\n", page, page->bin_index);
1517 		return B_ERROR;
1518 	}
1519 
1520 	if (page->bin_index < heap->bin_count) {
1521 		// small allocation
1522 		heap_bin *bin = &heap->bins[page->bin_index];
1523 
1524 #if PARANOID_KERNEL_FREE
1525 		if (((uint32 *)address)[1] == 0xdeadbeef) {
1526 			// This block looks like it was freed already, walk the free list
1527 			// on this page to make sure this address doesn't exist.
1528 			MutexLocker binLocker(bin->lock);
1529 			for (addr_t *temp = page->free_list; temp != NULL;
1530 					temp = (addr_t *)*temp) {
1531 				if (temp == address) {
1532 					panic("free(): address %p already exists in page free "
1533 						"list\n", address);
1534 					return B_ERROR;
1535 				}
1536 			}
1537 		}
1538 
1539 		// the first 4 bytes are overwritten with the next free list pointer
1540 		// later
1541 		uint32 *dead = (uint32 *)address;
1542 		for (uint32 i = 1; i < bin->element_size / sizeof(uint32); i++)
1543 			dead[i] = 0xdeadbeef;
1544 #endif
1545 
1546 		MutexLocker binLocker(bin->lock);
1547 		if (((addr_t)address - area->base - page->index
1548 			* heap->page_size) % bin->element_size != 0) {
1549 			panic("free(): passed invalid pointer %p supposed to be in bin for "
1550 				"element size %ld\n", address, bin->element_size);
1551 			return B_ERROR;
1552 		}
1553 
1554 		// add the address to the page free list
1555 		*(addr_t *)address = (addr_t)page->free_list;
1556 		page->free_list = (addr_t *)address;
1557 		page->free_count++;
1558 
1559 		if (page->free_count == bin->max_free_count) {
1560 			// we are now empty, remove the page from the bin list
1561 			MutexLocker pageLocker(heap->page_lock);
1562 			heap_unlink_page(page, &bin->page_list);
1563 			page->in_use = 0;
1564 			heap_link_page(page, &area->free_pages);
1565 			heap_free_pages_added(heap, area, 1);
1566 		} else if (page->free_count == 1) {
1567 			// we need to add ourselfs to the page list of the bin
1568 			heap_link_page(page, &bin->page_list);
1569 		} else {
1570 			// we might need to move back in the free pages list
1571 			if (page->next && page->next->free_count < page->free_count) {
1572 				// move ourselfs so the list stays ordered
1573 				heap_page *insert = page->next;
1574 				while (insert->next
1575 					&& insert->next->free_count < page->free_count)
1576 					insert = insert->next;
1577 
1578 				heap_unlink_page(page, &bin->page_list);
1579 
1580 				page->prev = insert;
1581 				page->next = insert->next;
1582 				if (page->next)
1583 					page->next->prev = page;
1584 				insert->next = page;
1585 			}
1586 		}
1587 	} else {
1588 		// large allocation, just return the pages to the page free list
1589 		uint32 allocationID = page->allocation_id;
1590 		uint32 maxPages = area->page_count - page->index;
1591 		uint32 pageCount = 0;
1592 
1593 		MutexLocker pageLocker(heap->page_lock);
1594 		for (uint32 i = 0; i < maxPages; i++) {
1595 			// loop until we find the end of this allocation
1596 			if (!page[i].in_use || page[i].bin_index != heap->bin_count
1597 				|| page[i].allocation_id != allocationID)
1598 				break;
1599 
1600 			// this page still belongs to the same allocation
1601 			page[i].in_use = 0;
1602 			page[i].allocation_id = 0;
1603 
1604 			// return it to the free list
1605 			heap_link_page(&page[i], &area->free_pages);
1606 			pageCount++;
1607 		}
1608 
1609 		heap_free_pages_added(heap, area, pageCount);
1610 	}
1611 
1612 	T(Free((addr_t)address));
1613 	areaReadLocker.Unlock();
1614 
1615 	if (heap->empty_areas > 1) {
1616 		WriteLocker areaWriteLocker(heap->area_lock);
1617 		MutexLocker pageLocker(heap->page_lock);
1618 
1619 		area = heap->areas;
1620 		while (area != NULL && heap->empty_areas > 1) {
1621 			heap_area *next = area->next;
1622 			if (area->area >= 0
1623 				&& area->free_page_count == area->page_count
1624 				&& heap_remove_area(heap, area) == B_OK) {
1625 				delete_area(area->area);
1626 				heap->empty_areas--;
1627 			}
1628 
1629 			area = next;
1630 		}
1631 	}
1632 
1633 	return B_OK;
1634 }
1635 
1636 
1637 #if KERNEL_HEAP_LEAK_CHECK
1638 extern "C" void
1639 heap_set_get_caller(heap_allocator* heap, addr_t (*getCaller)())
1640 {
1641 	heap->get_caller = getCaller;
1642 }
1643 #endif
1644 
1645 
1646 static status_t
1647 heap_realloc(heap_allocator *heap, void *address, void **newAddress,
1648 	size_t newSize)
1649 {
1650 	ReadLocker areaReadLocker(heap->area_lock);
1651 	heap_area *area = heap->all_areas;
1652 	while (area) {
1653 		// since the all_areas list is ordered by base with the biggest
1654 		// base at the top, we need only find the first area with a base
1655 		// smaller than our address to become our only candidate for
1656 		// reallocating
1657 		if (area->base <= (addr_t)address) {
1658 			if ((addr_t)address >= area->base + area->size) {
1659 				// none of the other areas can contain the address as the list
1660 				// is ordered
1661 				return B_ENTRY_NOT_FOUND;
1662 			}
1663 
1664 			// this area contains the allocation, we're done searching
1665 			break;
1666 		}
1667 
1668 		area = area->all_next;
1669 	}
1670 
1671 	if (area == NULL) {
1672 		// this address does not belong to us
1673 		return B_ENTRY_NOT_FOUND;
1674 	}
1675 
1676 	TRACE(("realloc(address = %p, newSize = %lu)\n", address, newSize));
1677 
1678 	heap_page *page = &area->page_table[((addr_t)address - area->base)
1679 		/ heap->page_size];
1680 	if (page->bin_index > heap->bin_count) {
1681 		panic("realloc(): page %p: invalid bin_index %d\n", page,
1682 			page->bin_index);
1683 		return B_ERROR;
1684 	}
1685 
1686 	// find out the size of the old allocation first
1687 	size_t minSize = 0;
1688 	size_t maxSize = 0;
1689 	if (page->bin_index < heap->bin_count) {
1690 		// this was a small allocation
1691 		heap_bin *bin = &heap->bins[page->bin_index];
1692 		maxSize = bin->element_size;
1693 		if (page->bin_index > 0)
1694 			minSize = heap->bins[page->bin_index - 1].element_size + 1;
1695 	} else {
1696 		// this was a large allocation
1697 		uint32 allocationID = page->allocation_id;
1698 		uint32 maxPages = area->page_count - page->index;
1699 		maxSize = heap->page_size;
1700 
1701 		MutexLocker pageLocker(heap->page_lock);
1702 		for (uint32 i = 1; i < maxPages; i++) {
1703 			if (!page[i].in_use || page[i].bin_index != heap->bin_count
1704 				|| page[i].allocation_id != allocationID)
1705 				break;
1706 
1707 			minSize += heap->page_size;
1708 			maxSize += heap->page_size;
1709 		}
1710 	}
1711 
1712 	areaReadLocker.Unlock();
1713 
1714 #if KERNEL_HEAP_LEAK_CHECK
1715 	newSize += sizeof(heap_leak_check_info);
1716 #endif
1717 
1718 	// does the new allocation simply fit in the old allocation?
1719 	if (newSize > minSize && newSize <= maxSize) {
1720 #if KERNEL_HEAP_LEAK_CHECK
1721 		// update the size info (the info is at the end so stays where it is)
1722 		heap_leak_check_info *info = (heap_leak_check_info *)((addr_t)address
1723 			+ maxSize - sizeof(heap_leak_check_info));
1724 		info->size = newSize - sizeof(heap_leak_check_info);
1725 		newSize -= sizeof(heap_leak_check_info);
1726 #endif
1727 
1728 		T(Reallocate((addr_t)address, (addr_t)address, newSize));
1729 		*newAddress = address;
1730 		return B_OK;
1731 	}
1732 
1733 #if KERNEL_HEAP_LEAK_CHECK
1734 	// new leak check info will be created with the malloc below
1735 	newSize -= sizeof(heap_leak_check_info);
1736 #endif
1737 
1738 	// if not, allocate a new chunk of memory
1739 	*newAddress = memalign(0, newSize);
1740 	T(Reallocate((addr_t)address, (addr_t)*newAddress, newSize));
1741 	if (*newAddress == NULL) {
1742 		// we tried but it didn't work out, but still the operation is done
1743 		return B_OK;
1744 	}
1745 
1746 	// copy the old data and free the old allocation
1747 	memcpy(*newAddress, address, min_c(maxSize, newSize));
1748 	heap_free(heap, address);
1749 	return B_OK;
1750 }
1751 
1752 
1753 inline uint32
1754 heap_class_for(size_t size)
1755 {
1756 #if KERNEL_HEAP_LEAK_CHECK
1757 	// take the extra info size into account
1758 	size += sizeof(heap_leak_check_info_s);
1759 #endif
1760 
1761 	uint32 index = 0;
1762 	for (; index < HEAP_CLASS_COUNT - 1; index++) {
1763 		if (size <= sHeapClasses[index].max_allocation_size)
1764 			return index;
1765 	}
1766 
1767 	return index;
1768 }
1769 
1770 
1771 static void
1772 deferred_deleter(void *arg, int iteration)
1773 {
1774 	// move entries and deletables to on-stack lists
1775 	InterruptsSpinLocker locker(sDeferredFreeListLock);
1776 	if (sDeferredFreeList.IsEmpty() && sDeferredDeletableList.IsEmpty())
1777 		return;
1778 
1779 	DeferredFreeList entries;
1780 	entries.MoveFrom(&sDeferredFreeList);
1781 
1782 	DeferredDeletableList deletables;
1783 	deletables.MoveFrom(&sDeferredDeletableList);
1784 
1785 	locker.Unlock();
1786 
1787 	// free the entries
1788 	while (DeferredFreeListEntry* entry = entries.RemoveHead())
1789 		free(entry);
1790 
1791 	// delete the deletables
1792 	while (DeferredDeletable* deletable = deletables.RemoveHead())
1793 		delete deletable;
1794 }
1795 
1796 
1797 //	#pragma mark -
1798 
1799 
1800 static status_t
1801 heap_create_new_heap_area(heap_allocator *heap, const char *name, size_t size)
1802 {
1803 	void *address = NULL;
1804 	area_id heapArea = create_area(name, &address,
1805 		B_ANY_KERNEL_BLOCK_ADDRESS, size, B_FULL_LOCK,
1806 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
1807 	if (heapArea < B_OK) {
1808 		TRACE(("heap: couldn't allocate heap area \"%s\"\n", name));
1809 		return heapArea;
1810 	}
1811 
1812 	heap_add_area(heap, heapArea, (addr_t)address, size);
1813 #if PARANOID_HEAP_VALIDATION
1814 	heap_validate_heap(heap);
1815 #endif
1816 	return B_OK;
1817 }
1818 
1819 
1820 static int32
1821 heap_grow_thread(void *)
1822 {
1823 	while (true) {
1824 		// wait for a request to grow the heap list
1825 		if (acquire_sem(sHeapGrowSem) < B_OK)
1826 			continue;
1827 
1828 		if (sAddGrowHeap) {
1829 			// the grow heap is going to run full soon, try to allocate a new
1830 			// one to make some room.
1831 			TRACE(("heap_grower: grow heaps will run out of memory soon\n"));
1832 			if (heap_create_new_heap_area(sGrowHeap, "additional grow heap",
1833 					HEAP_DEDICATED_GROW_SIZE) != B_OK)
1834 				dprintf("heap_grower: failed to create new grow heap area\n");
1835 		}
1836 
1837 		for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) {
1838 			heap_allocator *heap = sHeaps[i];
1839 			if (sLastGrowRequest[i] > sLastHandledGrowRequest[i]
1840 				|| heap_should_grow(heap)) {
1841 				// grow this heap if it is nearly full or if a grow was
1842 				// explicitly requested for this heap (happens when a large
1843 				// allocation cannot be fulfilled due to lack of contiguous
1844 				// pages)
1845 				if (heap_create_new_heap_area(heap, "additional heap",
1846 						HEAP_GROW_SIZE) != B_OK)
1847 					dprintf("heap_grower: failed to create new heap area\n");
1848 				sLastHandledGrowRequest[i] = sLastGrowRequest[i];
1849 			}
1850 		}
1851 
1852 		// notify anyone waiting for this request
1853 		release_sem_etc(sHeapGrownNotify, -1, B_RELEASE_ALL);
1854 	}
1855 
1856 	return 0;
1857 }
1858 
1859 
1860 status_t
1861 heap_init(addr_t base, size_t size)
1862 {
1863 	for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) {
1864 		size_t partSize = size * sHeapClasses[i].initial_percentage / 100;
1865 		sHeaps[i] = heap_create_allocator(sHeapClasses[i].name, base, partSize,
1866 			&sHeapClasses[i]);
1867 		sLastGrowRequest[i] = sLastHandledGrowRequest[i] = 0;
1868 		base += partSize;
1869 	}
1870 
1871 	// set up some debug commands
1872 	add_debugger_command_etc("heap", &dump_heap_list,
1873 		"Dump infos about the kernel heap(s)",
1874 		"[(\"grow\" | \"stats\" | <heap>)]\n"
1875 		"Dump infos about the kernel heap(s). If \"grow\" is specified, only\n"
1876 		"infos about the dedicated grow heap are printed. If \"stats\" is\n"
1877 		"given as the argument, currently only the heap count is printed.\n"
1878 		"If <heap> is given, it is interpreted as the address of the heap to\n"
1879 		"print infos about.\n", 0);
1880 #if !KERNEL_HEAP_LEAK_CHECK
1881 	add_debugger_command_etc("allocations", &dump_allocations,
1882 		"Dump current heap allocations",
1883 		"[\"stats\"] [<heap>]\n"
1884 		"If no parameters are given, all current alloactions are dumped.\n"
1885 		"If the optional argument \"stats\" is specified, only the allocation\n"
1886 		"counts and no individual allocations are printed\n"
1887 		"If a specific heap address is given, only allocations of this\n"
1888 		"allocator are dumped\n", 0);
1889 #else // !KERNEL_HEAP_LEAK_CHECK
1890 	add_debugger_command_etc("allocations", &dump_allocations,
1891 		"Dump current heap allocations",
1892 		"[(\"team\" | \"thread\") <id>] [ \"caller\" <address> ] [\"stats\"]\n"
1893 		"If no parameters are given, all current alloactions are dumped.\n"
1894 		"If \"team\", \"thread\", and/or \"caller\" is specified as the first\n"
1895 		"argument, only allocations matching the team id, thread id, or \n"
1896 		"caller address given in the second argument are printed.\n"
1897 		"If the optional argument \"stats\" is specified, only the allocation\n"
1898 		"counts and no individual allocations are printed\n", 0);
1899 	add_debugger_command_etc("allocations_per_caller",
1900 		&dump_allocations_per_caller,
1901 		"Dump current heap allocations summed up per caller",
1902 		"[ \"-c\" ] [ -h <heap> ]\n"
1903 		"The current allocations will by summed up by caller (their count and\n"
1904 		"size) printed in decreasing order by size or, if \"-c\" is\n"
1905 		"specified, by allocation count. If given <heap> specifies the\n"
1906 		"address of the heap for which to print the allocations.\n", 0);
1907 #endif // KERNEL_HEAP_LEAK_CHECK
1908 	return B_OK;
1909 }
1910 
1911 
1912 status_t
1913 heap_init_post_sem()
1914 {
1915 #if PARANOID_KERNEL_MALLOC
1916 	vm_block_address_range("uninitialized heap memory",
1917 		(void *)ROUNDDOWN(0xcccccccc, B_PAGE_SIZE), B_PAGE_SIZE * 64);
1918 #endif
1919 #if PARANOID_KERNEL_FREE
1920 	vm_block_address_range("freed heap memory",
1921 		(void *)ROUNDDOWN(0xdeadbeef, B_PAGE_SIZE), B_PAGE_SIZE * 64);
1922 #endif
1923 
1924 	sHeapGrowSem = create_sem(0, "heap_grow_sem");
1925 	if (sHeapGrowSem < 0) {
1926 		panic("heap_init_post_sem(): failed to create heap grow sem\n");
1927 		return B_ERROR;
1928 	}
1929 
1930 	sHeapGrownNotify = create_sem(0, "heap_grown_notify");
1931 	if (sHeapGrownNotify < 0) {
1932 		panic("heap_init_post_sem(): failed to create heap grown notify sem\n");
1933 		return B_ERROR;
1934 	}
1935 
1936 	return B_OK;
1937 }
1938 
1939 
1940 status_t
1941 heap_init_post_thread()
1942 {
1943 	void *address = NULL;
1944 	area_id growHeapArea = create_area("dedicated grow heap", &address,
1945 		B_ANY_KERNEL_BLOCK_ADDRESS, HEAP_DEDICATED_GROW_SIZE, B_FULL_LOCK,
1946 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
1947 	if (growHeapArea < B_OK) {
1948 		panic("heap_init_post_thread(): couldn't allocate dedicate grow heap "
1949 			"area");
1950 		return growHeapArea;
1951 	}
1952 
1953 	sGrowHeap = heap_create_allocator("grow", (addr_t)address,
1954 		HEAP_DEDICATED_GROW_SIZE, &sHeapClasses[0]);
1955 	if (sGrowHeap == NULL) {
1956 		panic("heap_init_post_thread(): failed to create dedicated grow heap\n");
1957 		return B_ERROR;
1958 	}
1959 
1960 	sHeapGrowThread = spawn_kernel_thread(heap_grow_thread, "heap grower",
1961 		B_URGENT_PRIORITY, NULL);
1962 	if (sHeapGrowThread < 0) {
1963 		panic("heap_init_post_thread(): cannot create heap grow thread\n");
1964 		return sHeapGrowThread;
1965 	}
1966 
1967 	// run the deferred deleter roughly once a second
1968 	if (register_kernel_daemon(deferred_deleter, NULL, 10) != B_OK)
1969 		panic("heap_init_post_thread(): failed to init deferred deleter");
1970 
1971 	send_signal_etc(sHeapGrowThread, SIGCONT, B_DO_NOT_RESCHEDULE);
1972 	return B_OK;
1973 }
1974 
1975 
1976 //	#pragma mark - Public API
1977 
1978 
1979 void *
1980 memalign(size_t alignment, size_t size)
1981 {
1982 	if (!gKernelStartup && !are_interrupts_enabled()) {
1983 		panic("memalign(): called with interrupts disabled\n");
1984 		return NULL;
1985 	}
1986 
1987 	if (!gKernelStartup && size > HEAP_AREA_USE_THRESHOLD) {
1988 		// don't even attempt such a huge allocation - use areas instead
1989 		size_t areaSize = size + sizeof(area_allocation_info);
1990 		if (alignment != 0)
1991 			areaSize = ROUNDUP(areaSize, alignment);
1992 
1993 		void *address = NULL;
1994 		areaSize = ROUNDUP(areaSize, B_PAGE_SIZE);
1995 		area_id allocationArea = create_area("memalign area", &address,
1996 			B_ANY_KERNEL_BLOCK_ADDRESS, areaSize, B_FULL_LOCK,
1997 			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
1998 		if (allocationArea < B_OK) {
1999 			dprintf("heap: failed to create area for huge allocation\n");
2000 			return NULL;
2001 		}
2002 
2003 		area_allocation_info *info = (area_allocation_info *)address;
2004 		info->magic = kAreaAllocationMagic;
2005 		info->area = allocationArea;
2006 		info->base = address;
2007 		info->size = areaSize;
2008 		info->allocation_size = size;
2009 		info->allocation_alignment = alignment;
2010 
2011 		address = (void *)((addr_t)address + sizeof(area_allocation_info));
2012 		if (alignment != 0)
2013 			address = (void *)ROUNDUP((addr_t)address, alignment);
2014 
2015 		TRACE(("heap: allocated area %ld for huge allocation of %lu bytes\n",
2016 			allocationArea, size));
2017 
2018 		info->allocation_base = address;
2019 
2020 #if PARANOID_KERNEL_MALLOC
2021 		memset(address, 0xcc, size);
2022 #endif
2023 		return address;
2024 	}
2025 
2026 	uint32 heapClass = heap_class_for(size);
2027 	heap_allocator *heap = sHeaps[heapClass];
2028 	void *result = heap_memalign(heap, alignment, size);
2029 	if (result == NULL) {
2030 		// request an urgent grow and wait - we don't do it ourselfs here to
2031 		// serialize growing through the grow thread, as otherwise multiple
2032 		// threads hitting this situation (likely when memory ran out) would
2033 		// all add areas
2034 		sLastGrowRequest[heapClass]++;
2035 		switch_sem(sHeapGrowSem, sHeapGrownNotify);
2036 
2037 		// and then try again
2038 		result = heap_memalign(heap, alignment, size);
2039 	} else if (heap_should_grow(heap)) {
2040 		// should grow sometime soon, notify the grower
2041 		release_sem_etc(sHeapGrowSem, 1, B_DO_NOT_RESCHEDULE);
2042 	}
2043 
2044 #if PARANOID_HEAP_VALIDATION
2045 	heap_validate_heap(heap);
2046 #endif
2047 
2048 	if (result == NULL)
2049 		panic("heap: kernel heap has run out of memory\n");
2050 	return result;
2051 }
2052 
2053 
2054 void *
2055 memalign_nogrow(size_t alignment, size_t size)
2056 {
2057 	// use dedicated memory in the grow thread by default
2058 	if (thread_get_current_thread_id() == sHeapGrowThread) {
2059 		void *result = heap_memalign(sGrowHeap, alignment, size);
2060 		if (!sAddGrowHeap && heap_should_grow(sGrowHeap)) {
2061 			// hopefully the heap grower will manage to create a new heap
2062 			// before running out of private memory...
2063 			dprintf("heap: requesting new grow heap\n");
2064 			sAddGrowHeap = true;
2065 			release_sem_etc(sHeapGrowSem, 1, B_DO_NOT_RESCHEDULE);
2066 		}
2067 
2068 		if (result != NULL)
2069 			return result;
2070 	}
2071 
2072 	// try public memory, there might be something available
2073 	heap_allocator *heap = sHeaps[heap_class_for(size)];
2074 	void *result = heap_memalign(heap, alignment, size);
2075 	if (result != NULL)
2076 		return result;
2077 
2078 	// no memory available
2079 	if (thread_get_current_thread_id() == sHeapGrowThread)
2080 		panic("heap: all heaps have run out of memory while growing\n");
2081 	else
2082 		dprintf("heap: all heaps have run out of memory\n");
2083 
2084 	return NULL;
2085 }
2086 
2087 
2088 void *
2089 malloc_nogrow(size_t size)
2090 {
2091 	return memalign_nogrow(0, size);
2092 }
2093 
2094 
2095 void *
2096 malloc(size_t size)
2097 {
2098 	return memalign(0, size);
2099 }
2100 
2101 
2102 void
2103 free(void *address)
2104 {
2105 	if (!gKernelStartup && !are_interrupts_enabled()) {
2106 		panic("free(): called with interrupts disabled\n");
2107 		return;
2108 	}
2109 
2110 	for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) {
2111 		heap_allocator *heap = sHeaps[i];
2112 		if (heap_free(heap, address) == B_OK) {
2113 #if PARANOID_HEAP_VALIDATION
2114 			heap_validate_heap(heap);
2115 #endif
2116 			return;
2117 		}
2118 	}
2119 
2120 	// maybe it was allocated from the dedicated grow heap
2121 	if (heap_free(sGrowHeap, address) == B_OK)
2122 		return;
2123 
2124 	// or maybe it was a huge allocation using an area
2125 	area_info areaInfo;
2126 	area_id area = area_for(address);
2127 	if (area >= B_OK && get_area_info(area, &areaInfo) == B_OK) {
2128 		area_allocation_info *info = (area_allocation_info *)areaInfo.address;
2129 
2130 		// just make extra sure it was allocated by us
2131 		if (info->magic == kAreaAllocationMagic && info->area == area
2132 			&& info->size == areaInfo.size && info->base == areaInfo.address
2133 			&& info->allocation_size < areaInfo.size) {
2134 			delete_area(area);
2135 			TRACE(("free(): freed huge allocation by deleting area %ld\n",
2136 				area));
2137 			return;
2138 		}
2139 	}
2140 
2141 	panic("free(): free failed for address %p\n", address);
2142 }
2143 
2144 
2145 void *
2146 realloc(void *address, size_t newSize)
2147 {
2148 	if (!gKernelStartup && !are_interrupts_enabled()) {
2149 		panic("realloc(): called with interrupts disabled\n");
2150 		return NULL;
2151 	}
2152 
2153 	if (address == NULL)
2154 		return memalign(0, newSize);
2155 
2156 	if (newSize == 0) {
2157 		free(address);
2158 		return NULL;
2159 	}
2160 
2161 	void *newAddress = NULL;
2162 	for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) {
2163 		heap_allocator *heap = sHeaps[i];
2164 		if (heap_realloc(heap, address, &newAddress, newSize) == B_OK) {
2165 #if PARANOID_HEAP_VALIDATION
2166 			heap_validate_heap(heap);
2167 #endif
2168 			return newAddress;
2169 		}
2170 	}
2171 
2172 	// maybe it was allocated from the dedicated grow heap
2173 	if (heap_realloc(sGrowHeap, address, &newAddress, newSize) == B_OK)
2174 		return newAddress;
2175 
2176 	// or maybe it was a huge allocation using an area
2177 	area_info areaInfo;
2178 	area_id area = area_for(address);
2179 	if (area >= B_OK && get_area_info(area, &areaInfo) == B_OK) {
2180 		area_allocation_info *info = (area_allocation_info *)areaInfo.address;
2181 
2182 		// just make extra sure it was allocated by us
2183 		if (info->magic == kAreaAllocationMagic && info->area == area
2184 			&& info->size == areaInfo.size && info->base == areaInfo.address
2185 			&& info->allocation_size < areaInfo.size) {
2186 			size_t available = info->size - ((addr_t)info->allocation_base
2187 				- (addr_t)info->base);
2188 
2189 			if (available >= newSize) {
2190 				// there is enough room available for the newSize
2191 				TRACE(("realloc(): new size %ld fits in old area %ld with %ld "
2192 					"available\n", newSize, area, available));
2193 				return address;
2194 			}
2195 
2196 			// have to allocate/copy/free - TODO maybe resize the area instead?
2197 			newAddress = memalign(0, newSize);
2198 			if (newAddress == NULL) {
2199 				dprintf("realloc(): failed to allocate new block of %ld bytes\n",
2200 					newSize);
2201 				return NULL;
2202 			}
2203 
2204 			memcpy(newAddress, address, min_c(newSize, info->allocation_size));
2205 			delete_area(area);
2206 			TRACE(("realloc(): allocated new block %p for size %ld and deleted "
2207 				"old area %ld\n", newAddress, newSize, area));
2208 			return newAddress;
2209 		}
2210 	}
2211 
2212 	panic("realloc(): failed to realloc address %p to size %lu\n", address,
2213 		newSize);
2214 	return NULL;
2215 }
2216 
2217 
2218 void *
2219 calloc(size_t numElements, size_t size)
2220 {
2221 	void *address = memalign(0, numElements * size);
2222 	if (address != NULL)
2223 		memset(address, 0, numElements * size);
2224 
2225 	return address;
2226 }
2227 
2228 
2229 void
2230 deferred_free(void *block)
2231 {
2232 	if (block == NULL)
2233 		return;
2234 
2235 	DeferredFreeListEntry *entry = new(block) DeferredFreeListEntry;
2236 
2237 	InterruptsSpinLocker _(sDeferredFreeListLock);
2238 	sDeferredFreeList.Add(entry);
2239 }
2240 
2241 
2242 void *
2243 malloc_referenced(size_t size)
2244 {
2245 	int32 *referencedData = (int32 *)malloc(size + 4);
2246 	if (referencedData == NULL)
2247 		return NULL;
2248 
2249 	*referencedData = 1;
2250 	return referencedData + 1;
2251 }
2252 
2253 
2254 void *
2255 malloc_referenced_acquire(void *data)
2256 {
2257 	if (data != NULL) {
2258 		int32 *referencedData = (int32 *)data - 1;
2259 		atomic_add(referencedData, 1);
2260 	}
2261 
2262 	return data;
2263 }
2264 
2265 
2266 void
2267 malloc_referenced_release(void *data)
2268 {
2269 	if (data == NULL)
2270 		return;
2271 
2272 	int32 *referencedData = (int32 *)data - 1;
2273 	if (atomic_add(referencedData, -1) < 1)
2274 		free(referencedData);
2275 }
2276 
2277 
2278 DeferredDeletable::~DeferredDeletable()
2279 {
2280 }
2281 
2282 
2283 void
2284 deferred_delete(DeferredDeletable *deletable)
2285 {
2286 	if (deletable == NULL)
2287 		return;
2288 
2289 	InterruptsSpinLocker _(sDeferredFreeListLock);
2290 	sDeferredDeletableList.Add(deletable);
2291 }
2292