Complete rework of the heap implementation. Freelists are now part of the pages

and pages are now kept in lists as well. This allows to return free pages once
a bin does not need them anymore. Partially filled pages are kept in a sorted
linked list so that allocation will always happen on the fullest page - this
favours having full pages and makes it more likely lightly used pages will get
completely empty so they can be returned. Generally this now goes more in the
direction of a slab allocator.
The allocation logic has been extracted, so a heap is now simply attachable to
a region of memory. This allows for multiple heaps and for dynamic growing. In
case the allocator runs out of free pages, an asynchronous growing thread is
notified to create a new area and attach a new heap to it.
By default the kernel heap is now set to 16MB and grows by 8MB each time all
heaps run full.
This should solve quite a few issues, like certain bins just claiming all pages
so that even if there is free space nothing can be allocated. Also it obviously
does aways with filling the heap page by page until it overgrows.
I think this is now a well performing and scalable allocator we can live with
for quite some time. It is well tested under emulation and real hardware and
performs as expected. If problems come up there is an extensive sanity checker
that can be enabled by PARANOID_VALIDATION that covers most aspects of the
allocator. For normal operation this is not necessary though and is therefore
disabled by default.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23939 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2008-02-10 21:00:13 +00:00
parent a0c4a29faf
commit 5c4d1c5e21
5 changed files with 934 additions and 794 deletions
@@ -5,18 +5,17 @@
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#ifndef _KERNEL_MEMHEAP_H
#define _KERNEL_MEMHEAP_H
#ifndef _KERNEL_HEAP_H
#define _KERNEL_HEAP_H
#include <OS.h>
struct kernel_args;
// allocate 16MB initial heap for the kernel
#define INITIAL_HEAP_SIZE 16 * 1024 * 1024
// grow by another 8MB each time the heap runs out of memory
#define HEAP_GROW_SIZE 8 * 1024 * 1024
#define HEAP_SIZE 0x02000000
// 32 MB heap for the kernel (!)
#ifdef __cplusplus
extern "C" {
@@ -25,8 +24,8 @@ extern "C" {
void *memalign(size_t alignment, size_t size);
status_t heap_init(addr_t heapBase, size_t heapSize);
status_t heap_init_post_sem(struct kernel_args *args);
status_t heap_init_post_thread(struct kernel_args *args);
status_t heap_init_post_sem();
status_t heap_init_post_thread();
#ifdef __cplusplus
}
+1 -1
View File
@@ -21,7 +21,7 @@ KernelMergeObject kernel_core.o :
condition_variable.cpp
cpu.c
elf.cpp
heap.c
heap.cpp
image.c
int.c
kernel_daemon.c
-770
View File
@@ -1,770 +0,0 @@
/*
* Copyright 2002-2006, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <KernelExport.h>
#include <kernel.h>
#include <vm.h>
#include <lock.h>
#include <int.h>
#include <memheap.h>
#include <malloc.h>
#include <debug.h>
#include <arch/cpu.h>
#include <boot/kernel_args.h>
#include <string.h>
//#define TRACE_HEAP
#ifdef TRACE_HEAP
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
/* prevent freeing pointers that were not allocated by kmalloc or are already freeed */
#if KDEBUG > 1
# define PARANOID_POINTER_CHECK 1
#else
# define PARANOID_POINTER_CHECK 0
#endif
/* initialize newly allocated memory with something non zero */
#define PARANOID_KMALLOC 1
/* check if freed pointers are already freed, and fill freed memory with 0xdeadbeef */
#define PARANOID_KFREE 1
/* use a back and front wall around each allocation */
#define USE_WALL 0
#define USE_CHECKING_WALL 0
#define WALL_SIZE 8
// must be a multiple of 4
#if USE_CHECKING_WALL
# define WALL_CHECK_FREQUENCY 1 /* every tenth second */
#endif
#if USE_WALL
struct front_wall {
# if USE_CHECKING_WALL
struct list_link link;
# endif
size_t alignment;
size_t size;
uint32 wall[WALL_SIZE / 4];
};
struct back_wall {
uint32 wall[WALL_SIZE / 4];
};
#endif // USE_WALL
// heap stuff
// ripped mostly from nujeffos
struct heap_page {
uint16 bin_index : 5;
uint16 free_count : 9;
uint16 cleaning : 1;
uint16 in_use : 1;
} PACKED;
// used for bin==bin_count allocations
#define allocation_id free_count
static vint32 current_alloc_id = 0;
static struct heap_page *heap_alloc_table;
static addr_t heap_base_ptr;
static addr_t heap_base;
static addr_t heap_size;
struct heap_bin {
uint32 element_size;
uint32 grow_size;
uint32 alloc_count;
void *free_list;
uint32 free_count;
char *raw_list;
uint32 raw_count;
};
static struct heap_bin bins[] = {
{16, B_PAGE_SIZE, 0, 0, 0, 0, 0},
{32, B_PAGE_SIZE, 0, 0, 0, 0, 0},
{64, B_PAGE_SIZE, 0, 0, 0, 0, 0},
{128, B_PAGE_SIZE, 0, 0, 0, 0, 0},
{256, B_PAGE_SIZE, 0, 0, 0, 0, 0},
{512, B_PAGE_SIZE, 0, 0, 0, 0, 0},
{1024, B_PAGE_SIZE, 0, 0, 0, 0, 0},
{2048, B_PAGE_SIZE, 0, 0, 0, 0, 0},
{0x1000, 0x1000, 0, 0, 0, 0, 0},
{0x2000, 0x2000, 0, 0, 0, 0, 0},
{0x3000, 0x3000, 0, 0, 0, 0, 0},
{0x4000, 0x4000, 0, 0, 0, 0, 0},
{0x5000, 0x5000, 0, 0, 0, 0, 0},
{0x6000, 0x6000, 0, 0, 0, 0, 0},
{0x7000, 0x7000, 0, 0, 0, 0, 0},
{0x8000, 0x8000, 0, 0, 0, 0, 0},
{0x9000, 0x9000, 0, 0, 0, 0, 0},
{0xa000, 0xa000, 0, 0, 0, 0, 0},
{0xb000, 0xb000, 0, 0, 0, 0, 0},
{0xc000, 0xc000, 0, 0, 0, 0, 0},
{0xd000, 0xd000, 0, 0, 0, 0, 0},
{0xe000, 0xe000, 0, 0, 0, 0, 0},
{0xf000, 0xf000, 0, 0, 0, 0, 0},
{0x10000, 0x10000, 0, 0, 0, 0, 0} // 64k
};
static const int bin_count = sizeof(bins) / sizeof(struct heap_bin);
static mutex heap_lock;
#if USE_CHECKING_WALL
sem_id sWallCheckLock = -1;
struct list sWalls;
#endif
#if PARANOID_POINTER_CHECK
#define PTRCHECKLIST_ENTRIES 32768
static void *ptrchecklist[PTRCHECKLIST_ENTRIES]; /* automatically initialized to zero */
static void
ptrchecklist_store(void *ptr)
{
int i;
mutex_lock(&heap_lock);
for (i = 0; i != PTRCHECKLIST_ENTRIES; i++)
if (ptrchecklist[i] == NULL) {
ptrchecklist[i] = ptr;
break;
}
mutex_unlock(&heap_lock);
if (i == PTRCHECKLIST_ENTRIES)
panic("Sorry, out of entries, increase PTRCHECKLIST_ENTRIES in heap.c\n");
}
static bool
ptrchecklist_remove(void *ptr)
{
int i;
bool found = false;
mutex_lock(&heap_lock);
for (i = 0; i != PTRCHECKLIST_ENTRIES; i++)
if (ptrchecklist[i] == ptr) {
ptrchecklist[i] = NULL;
found = true;
break;
}
mutex_unlock(&heap_lock);
return found;
}
#endif /* PARANOID_POINTER_CHECK */
#if USE_WALL
static size_t
wall_size(size_t alignment)
{
if (alignment == 0)
return sizeof(struct front_wall) + sizeof(struct back_wall);
return 2 * alignment;
}
static void *
get_base_address_from_wall(struct front_wall *wall)
{
if (wall->alignment == 0)
return (void *)wall;
return (void *)((addr_t)wall + sizeof(struct front_wall) - wall->alignment);
}
static struct front_wall *
get_wall(void *address)
{
return (struct front_wall *)((addr_t)address - sizeof(struct front_wall));
}
static void
set_wall(struct front_wall *wall, size_t size, size_t alignment)
{
struct back_wall *backWall;
uint32 i;
size -= wall_size(alignment);
wall->alignment = alignment;
wall->size = size;
for (i = 0; i < WALL_SIZE / sizeof(uint32); i++) {
wall->wall[i] = 0xabadcafe;
}
backWall = (struct back_wall *)((addr_t)wall + sizeof(struct front_wall) + size);
for (i = 0; i < WALL_SIZE / sizeof(uint32); i++) {
backWall->wall[i] = 0xabadcafe;
}
}
static void *
add_wall(void *address, size_t size, size_t alignment)
{
struct front_wall *wall;
if (alignment == 0)
address = (uint8 *)address + sizeof(struct front_wall);
else
address = (uint8 *)address + alignment;
wall = get_wall(address);
set_wall(wall, size, alignment);
#if USE_CHECKING_WALL
acquire_sem(sWallCheckLock);
list_add_link_to_tail(&sWalls, &wall->link);
release_sem(sWallCheckLock);
#endif
return address;
}
void check_wall(void *address);
void
check_wall(void *address)
{
struct front_wall *frontWall = get_wall(address);
struct back_wall *backWall;
uint32 i;
for (i = 0; i < WALL_SIZE / 4; i++) {
if (frontWall->wall[i] != 0xabadcafe) {
panic("free: front wall %i (%p) was overwritten (allocation at %p, %lu bytes): %08lx\n",
i, frontWall, address, frontWall->size, frontWall->wall[i]);
}
}
backWall = (struct back_wall *)((uint8 *)address + frontWall->size);
for (i = 0; i < WALL_SIZE / 4; i++) {
if (backWall->wall[i] != 0xabadcafe) {
panic("free: back wall %i (%p) was overwritten (allocation at %p, %lu bytes): %08lx\n",
i, backWall, address, frontWall->size, backWall->wall[i]);
}
}
}
#endif /* USE_WALL */
#if USE_CHECKING_WALL
static void
check_wall_daemon(void *arg, int iteration)
{
struct front_wall *wall = NULL;
acquire_sem(sWallCheckLock);
while ((wall = list_get_next_item(&sWalls, wall)) != NULL) {
check_wall((uint8 *)wall + sizeof(struct front_wall));
}
release_sem(sWallCheckLock);
}
#endif /* USE_CHECKING_WALL */
static void
dump_bin(int bin_index)
{
struct heap_bin *bin = &bins[bin_index];
unsigned int *temp;
dprintf("%d:\tesize %lu\tgrow_size %lu\talloc_count %lu\tfree_count %lu\traw_count %lu\traw_list %p\n",
bin_index, bin->element_size, bin->grow_size, bin->alloc_count, bin->free_count, bin->raw_count, bin->raw_list);
dprintf("free_list: ");
for(temp = bin->free_list; temp != NULL; temp = (unsigned int *)*temp) {
dprintf("%p ", temp);
}
dprintf("NULL\n");
}
static int
dump_bin_list(int argc, char **argv)
{
int i;
dprintf("%d heap bins at %p:\n", bin_count, bins);
for (i = 0; i < bin_count; i++) {
dump_bin(i);
}
return 0;
}
/* called from vm_init. The heap should already be mapped in at this point, we just
* do a little housekeeping to set up the data structure.
*/
status_t
heap_init(addr_t heapBase, size_t heapSize)
{
const unsigned int page_entries = B_PAGE_SIZE / sizeof(struct heap_page);
// set some global pointers
heap_alloc_table = (struct heap_page *)heapBase;
heap_size = ((uint64)heapSize * page_entries / (page_entries + 1)) & ~(B_PAGE_SIZE-1);
heap_base = (unsigned int)heap_alloc_table + PAGE_ALIGN(heap_size / page_entries);
heap_base_ptr = heap_base;
TRACE(("heap_alloc_table = %p, heap_base = 0x%lx, heap_size = 0x%lx\n",
heap_alloc_table, heap_base, heap_size));
// zero out the heap alloc table at the base of the heap
memset((void *)heap_alloc_table, 0, (heap_size / B_PAGE_SIZE) * sizeof(struct heap_page));
// pre-init the mutex to at least fall through any semaphore calls
heap_lock.sem = -1;
heap_lock.holder = -1;
#if USE_CHECKING_WALL
list_init(&sWalls);
#endif
// set up some debug commands
add_debugger_command("heap_bindump", &dump_bin_list, "dump stats about bin usage");
return B_OK;
}
status_t
heap_init_post_sem(kernel_args *args)
{
if (mutex_init(&heap_lock, "heap_mutex") < 0)
panic("error creating heap mutex\n");
#if USE_CHECKING_WALL
sWallCheckLock = create_sem(1, "check wall");
#endif
return B_OK;
}
status_t
heap_init_post_thread(kernel_args *args)
{
#if USE_CHECKING_WALL
register_kernel_daemon(check_wall_daemon, NULL, WALL_CHECK_FREQUENCY);
#endif
return B_OK;
}
static inline int32
next_alloc_id(void)
{
return atomic_add(&current_alloc_id, 1) & ((1<<(9-1))-1);
}
static char *
raw_alloc(unsigned int size, int bin_index)
{
unsigned int new_heap_ptr;
char *retval;
struct heap_page *page;
addr_t addr;
new_heap_ptr = heap_base_ptr + PAGE_ALIGN(size);
if (new_heap_ptr > heap_base + heap_size) {
panic("heap overgrew itself!\n");
return NULL;
}
for (addr = heap_base_ptr; addr < new_heap_ptr; addr += B_PAGE_SIZE) {
page = &heap_alloc_table[(addr - heap_base) / B_PAGE_SIZE];
page->in_use = 1;
page->cleaning = 0;
page->bin_index = bin_index;
if (bin_index < bin_count && bins[bin_index].element_size < B_PAGE_SIZE)
page->free_count = B_PAGE_SIZE / bins[bin_index].element_size;
else
page->free_count = 1;
}
retval = (char *)heap_base_ptr;
heap_base_ptr = new_heap_ptr;
return retval;
}
#if DEBUG
static bool
is_valid_alignment(size_t number)
{
// this cryptic line accepts zero and all powers of two
return ((~number + 1) | ((number << 1) - 1)) == ~0UL;
}
#endif
// #pragma mark -
void *
memalign(size_t alignment, size_t size)
{
void *address = NULL;
int bin_index;
unsigned int i;
struct heap_page *page;
TRACE(("memalign(alignment = %lu, size = %lu\n", alignment, size));
#if DEBUG
if (!is_valid_alignment(alignment))
panic("memalign() with an alignment which is not a power of 2\n");
#endif
if (!kernel_startup && !are_interrupts_enabled())
panic("malloc: called with interrupts disabled\n");
mutex_lock(&heap_lock);
#if USE_WALL
if (alignment > 0) {
// make the alignment big enough to contain the front wall
while (alignment < sizeof(struct front_wall))
alignment *= 2;
}
size += wall_size(alignment);
#else
// ToDo: that code "aligns" the buffer because the bins are always
// aligned on their bin size
if (size < alignment)
size = alignment;
#endif
for (bin_index = 0; bin_index < bin_count; bin_index++)
if (size <= bins[bin_index].element_size)
break;
if (bin_index == bin_count) {
int32 alloc_id;
alloc_id = next_alloc_id();
// try to find freed blocks first...
if (size < (heap_base_ptr - heap_base) / 10) { // but don't try too hard
int first = -1;
page = heap_alloc_table;
for (i = 0; i < (heap_base_ptr - heap_base) / B_PAGE_SIZE; i++) {
if (page[i].in_use) {
first = -1;
continue;
}
if (first > 0) {
if ((1 + i - first) * B_PAGE_SIZE > size)
break;
} else
first = i;
}
if (first > -1)
address = (void *)(heap_base + first * B_PAGE_SIZE);
}
if (address == NULL)
address = raw_alloc(size, bin_index);
page = &heap_alloc_table[((unsigned int)address - heap_base) / B_PAGE_SIZE];
for (i = 0; i < (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE; i++) {
page[i].in_use = 1;
page[i].cleaning = 0;
page[i].bin_index = bin_count;
page[i].allocation_id = alloc_id;
}
} else {
if (bins[bin_index].free_list != NULL) {
address = bins[bin_index].free_list;
bins[bin_index].free_list = (void *)(*(unsigned int *)bins[bin_index].free_list);
bins[bin_index].free_count--;
#if PARANOID_KFREE
// Ensure that the 0xdeadbeef is cleared so we do not walk this
// freelist if the user does not clear/use these bytes.
((uint32 *)address)[1] = 0;
#endif
} else {
if (bins[bin_index].raw_count == 0) {
bins[bin_index].raw_list = raw_alloc(bins[bin_index].grow_size, bin_index);
bins[bin_index].raw_count = bins[bin_index].grow_size / bins[bin_index].element_size;
}
bins[bin_index].raw_count--;
address = bins[bin_index].raw_list;
bins[bin_index].raw_list += bins[bin_index].element_size;
}
bins[bin_index].alloc_count++;
page = &heap_alloc_table[((unsigned int)address - heap_base) / B_PAGE_SIZE];
page[0].free_count--;
TRACE(("kmalloc0: page %p: bin_index %d, free_count %d\n", page, page->bin_index, page->free_count));
for(i = 1; i < bins[bin_index].element_size / B_PAGE_SIZE; i++) {
page[i].free_count--;
TRACE(("kmalloc1: page 0x%x: bin_index %d, free_count %d\n", page[i], page[i].bin_index, page[i].free_count));
}
}
mutex_unlock(&heap_lock);
TRACE(("kmalloc: asked to allocate size %d, returning ptr = %p\n", size, address));
#if PARANOID_KMALLOC
memset(address, 0xcc, size);
#endif
#if PARANOID_POINTER_CHECK
ptrchecklist_store(address);
#endif
#if USE_WALL
address = add_wall(address, size, alignment);
#endif
return address;
}
void *
malloc(size_t size)
{
return memalign(0, size);
}
void
free(void *address)
{
struct heap_page *page;
struct heap_bin *bin;
unsigned int i;
if (!kernel_startup && !are_interrupts_enabled())
panic("free: called with interrupts disabled\n");
if (address == NULL)
return;
if ((addr_t)address < heap_base || (addr_t)address >= (heap_base + heap_size))
panic("free: asked to free invalid address %p\n", address);
#if USE_WALL
{
struct front_wall *wall = get_wall(address);
#if USE_CHECKING_WALL
acquire_sem(sWallCheckLock);
list_remove_link(&wall->link);
release_sem(sWallCheckLock);
#endif
check_wall(address);
address = get_base_address_from_wall(wall);
}
#endif
#if PARANOID_POINTER_CHECK
if (!ptrchecklist_remove(address))
panic("free(): asked to free invalid pointer %p\n", address);
#endif
mutex_lock(&heap_lock);
TRACE(("free(): asked to free at ptr = %p\n", address));
page = &heap_alloc_table[((unsigned)address - heap_base) / B_PAGE_SIZE];
TRACE(("free(): page %p: bin_index %d, free_count %d\n", page, page->bin_index, page->free_count));
if (page[0].bin_index > bin_count)
panic("free(): page %p: invalid bin_index %d\n", page, page->bin_index);
if (page[0].bin_index < bin_count) {
bin = &bins[page[0].bin_index];
if (bin->element_size <= B_PAGE_SIZE && (addr_t)address % bin->element_size != 0)
panic("kfree: passed invalid pointer %p! Supposed to be in bin for esize 0x%lx\n", address, bin->element_size);
for (i = 0; i < bin->element_size / B_PAGE_SIZE; i++) {
if (page[i].bin_index != page[0].bin_index)
panic("free(): not all pages in allocation match bin_index\n");
page[i].free_count++;
}
#if PARANOID_KFREE
if (((uint32 *)address)[1] == 0xdeadbeef) {
// This block looks like it was freed already, walk the free list
// on this bin to make sure this address doesn't exist.
unsigned int *temp;
for (temp = bin->free_list; temp != NULL; temp = (unsigned int *)*temp) {
if (temp == (unsigned int *)address)
panic("free(): address %p already exists in bin free list\n", address);
}
}
// mark the free space as freed
{
uint32 i;
uint32 *dead = (uint32 *)address;
if (bin->element_size % 4 != 0)
panic("free(): didn't expect a bin element size that is not a multiple of 4\n");
// the first 4 bytes are overwritten with the next free list pointer later
for (i = 1; i < bin->element_size / 4; i++)
dead[i] = 0xdeadbeef;
}
#endif
*(unsigned int *)address = (unsigned int)bin->free_list;
bin->free_list = address;
bin->alloc_count--;
bin->free_count++;
} else {
// this chunk has been allocated via raw_alloc() directly
// TODO: since the heap must be replaced anyway, we don't
// free this allocation anymore... (tracking them would
// require some extra stuff)
for (i = 1; i <= (heap_base_ptr - heap_base) / B_PAGE_SIZE; i++) {
if (!page[i].in_use)
break;
if (page[i].bin_index != bin_count)
break;
if (page[i].allocation_id != page[0].allocation_id)
break;
page[i].in_use = 0;
page[i].cleaning = 0;
page[i].allocation_id = 0;
}
page[0].in_use = 0;
page[0].cleaning = 0;
page[0].allocation_id = 0;
}
mutex_unlock(&heap_lock);
}
/** Naive implementation of realloc() - it's very simple but
* it's there and working.
* It takes the bin of the current allocation if the new size
* fits in and is larger than the size of the next smaller bin.
* If not, it allocates a new chunk of memory, and copies and
* frees the old buffer.
*/
void *
realloc(void *address, size_t newSize)
{
void *newAddress = NULL;
size_t maxSize = 0, minSize;
if (!kernel_startup && !are_interrupts_enabled())
panic("realloc(): called with interrupts disabled\n");
if (address != NULL && ((addr_t)address < heap_base || (addr_t)address >= (heap_base + heap_size)))
panic("realloc(): asked to realloc invalid address %p\n", address);
if (newSize == 0) {
free(address);
return NULL;
}
// find out the size of the old allocation first
if (address != NULL) {
struct heap_page *page;
#if USE_WALL
struct front_wall *wall = get_wall(address);
#endif
mutex_lock(&heap_lock);
page = &heap_alloc_table[((unsigned)address - heap_base) / B_PAGE_SIZE];
TRACE(("realloc(): page %p: bin_index %d, free_count %d\n", page, page->bin_index, page->free_count));
if (page[0].bin_index > bin_count)
panic("realloc(): page %p: invalid bin_index %d\n", page, page->bin_index);
if (page[0].bin_index < bin_count) {
maxSize = bins[page[0].bin_index].element_size;
minSize = page[0].bin_index > 0 ? bins[page[0].bin_index - 1].element_size : 0;
} else {
int i;
for (i = 1; (addr_t)&page[i] < heap_base; i++) {
if (!page[i].in_use)
break;
if (page[i].bin_index != bin_count)
break;
if (page[i].allocation_id != page[0].allocation_id)
break;
}
minSize = 0;
maxSize = i * B_PAGE_SIZE;
}
mutex_unlock(&heap_lock);
#if USE_WALL
newSize += wall_size(wall->alignment);
#endif
// does the new allocation simply fit in the bin?
if (newSize > minSize && newSize <= maxSize) {
#if USE_WALL
check_wall(address);
// we need to move the back wall, and honour the
// alignment so that the address stays the same
set_wall(wall, newSize, wall->alignment);
#endif
return address;
}
}
// if not, allocate a new chunk of memory
newAddress = malloc(newSize);
if (newAddress == NULL)
return NULL;
// copy the old data and free the old allocation
if (address) {
// we do have the maxSize of the bin at this point
memcpy(newAddress, address, min(maxSize, newSize));
free(address);
}
return newAddress;
}
void *
calloc(size_t numElements, size_t size)
{
void *address = malloc(numElements * size);
if (address != NULL)
memset(address, 0, numElements * size);
return address;
}
+921
View File
@@ -0,0 +1,921 @@
/*
* Copyright 2008, Michael Lotz, [email protected].
* Distributed under the terms of the MIT License.
*
* Copyright 2002-2006, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <debug.h>
#include <heap.h>
#include <int.h>
#include <kernel.h>
#include <lock.h>
#include <malloc.h>
#include <signal.h>
#include <string.h>
#include <vm.h>
//#define TRACE_HEAP
#ifdef TRACE_HEAP
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// initialize newly allocated memory with something non zero
#define PARANOID_KMALLOC 1
// check for double free, and fill freed memory with 0xdeadbeef
#define PARANOID_KFREE 1
// validate sanity of the heap after each operation (slow!)
#define PARANOID_VALIDATION 0
typedef struct heap_page_s {
uint16 index;
uint16 bin_index : 5;
uint16 free_count : 10;
uint16 in_use : 1;
heap_page_s * next;
heap_page_s * prev;
uint16 empty_index;
addr_t * free_list;
} heap_page;
// used for bin == bin_count allocations
#define allocation_id free_count
typedef struct heap_bin_s {
uint32 element_size;
uint16 max_free_count;
heap_page * page_list; // sorted so that the desired page is always first
} heap_bin;
typedef struct heap_allocator_s {
addr_t base;
size_t size;
mutex lock;
vint32 large_alloc_id;
uint32 bin_count;
uint32 page_count;
heap_page * free_pages;
heap_bin * bins;
heap_page * page_table;
heap_allocator_s * next;
} heap_allocator;
static heap_allocator *sHeapList = NULL;
static heap_allocator *sLastGrowRequest = NULL;
static sem_id sHeapGrowSem = -1;
static sem_id sHeapGrownNotify = -1;
static void
dump_page(heap_page *page)
{
uint32 count = 0;
for (addr_t *temp = page->free_list; temp != NULL; temp = (addr_t *)*temp)
count++;
dprintf("\t\tpage %p: bin_index: %u; free_count: %u; empty_index: %u; free_list %p (%lu entr%s)\n",
page, page->bin_index, page->free_count, page->empty_index,
page->free_list, count, count == 1 ? "y" : "ies");
}
static void
dump_bin(heap_bin *bin)
{
dprintf("\telement_size: %lu; max_free_count: %u; page_list %p;\n",
bin->element_size, bin->max_free_count, bin->page_list);
for (heap_page *temp = bin->page_list; temp != NULL; temp = temp->next)
dump_page(temp);
}
static void
dump_allocator(heap_allocator *heap)
{
uint32 count = 0;
for (heap_page *page = heap->free_pages; page != NULL; page = page->next)
count++;
dprintf("allocator %p: base: 0x%08lx; size: %lu; bin_count: %lu; free_pages: %p (%lu entr%s)\n", heap,
heap->base, heap->size, heap->bin_count, heap->free_pages, count,
count == 1 ? "y" : "ies");
for (uint32 i = 0; i < heap->bin_count; i++)
dump_bin(&heap->bins[i]);
dprintf("\n");
}
static int
dump_heap_list(int argc, char **argv)
{
heap_allocator *heap = sHeapList;
while (heap) {
dump_allocator(heap);
heap = heap->next;
}
return 0;
}
#if PARANOID_VALIDATION
static void
heap_validate_heap(heap_allocator *heap)
{
mutex_lock(&heap->lock);
// validate the free pages list
uint32 freePageCount = 0;
heap_page *lastPage = NULL;
heap_page *page = heap->free_pages;
while (page) {
if ((addr_t)page < (addr_t)&heap->page_table[0]
|| (addr_t)page >= (addr_t)&heap->page_table[heap->page_count])
panic("free page is not part of the page table\n");
if (page->index >= heap->page_count)
panic("free page has invalid index\n");
if ((addr_t)&heap->page_table[page->index] != (addr_t)page)
panic("free page index does not lead to target page\n");
if (page->prev != lastPage)
panic("free page entry has invalid prev link\n");
if (page->in_use)
panic("free page marked as in use\n");
lastPage = page;
page = page->next;
freePageCount++;
}
// validate the page table
uint32 usedPageCount = 0;
for (uint32 i = 0; i < heap->page_count; i++) {
if (heap->page_table[i].in_use)
usedPageCount++;
}
if (freePageCount + usedPageCount != heap->page_count) {
panic("free pages and used pages do not add up (%lu + %lu != %lu)\n",
freePageCount, usedPageCount, heap->page_count);
}
// validate the bins
for (uint32 i = 0; i < heap->bin_count; i++) {
heap_bin *bin = &heap->bins[i];
lastPage = NULL;
page = bin->page_list;
int32 lastFreeCount = 0;
while (page) {
if ((addr_t)page < (addr_t)&heap->page_table[0]
|| (addr_t)page >= (addr_t)&heap->page_table[heap->page_count])
panic("used page is not part of the page table\n");
if (page->index >= heap->page_count)
panic("used page has invalid index\n");
if ((addr_t)&heap->page_table[page->index] != (addr_t)page)
panic("used page index does not lead to target page\n");
if (page->prev != lastPage)
panic("used page entry has invalid prev link (%p vs %p bin %lu)\n",
page->prev, lastPage, i);
if (!page->in_use)
panic("used page marked as not in use\n");
if (page->bin_index != i)
panic("used page with bin index %u in page list of bin %lu\n",
page->bin_index, i);
if (page->free_count < lastFreeCount)
panic("ordering of bin page list broken\n");
// validate the free list
uint32 freeSlotsCount = 0;
addr_t *element = page->free_list;
addr_t pageBase = heap->base + page->index * B_PAGE_SIZE;
while (element) {
if ((addr_t)element < pageBase
|| (addr_t)element >= pageBase + B_PAGE_SIZE)
panic("free list entry out of page range\n");
if (((addr_t)element - pageBase) % bin->element_size != 0)
panic("free list entry not on a element boundary\n");
element = (addr_t *)*element;
freeSlotsCount++;
}
uint32 slotCount = bin->max_free_count;
if (page->empty_index > slotCount)
panic("empty index beyond slot count (%u with %lu slots)\n",
page->empty_index, slotCount);
freeSlotsCount += (slotCount - page->empty_index);
if (freeSlotsCount > slotCount)
panic("more free slots than fit into the page\n");
lastPage = page;
lastFreeCount = page->free_count;
page = page->next;
}
}
mutex_unlock(&heap->lock);
}
#endif
heap_allocator *
heap_attach(addr_t base, size_t size, bool postSem)
{
heap_allocator *heap = (heap_allocator *)base;
base += sizeof(heap_allocator);
size -= sizeof(heap_allocator);
size_t binSizes[] = { 16, 32, 64, 96, 128, 192, 256, 384, 512, 1024, 2048, B_PAGE_SIZE };
uint32 binCount = sizeof(binSizes) / sizeof(binSizes[0]);
heap->bin_count = binCount;
heap->bins = (heap_bin *)base;
base += binCount * sizeof(heap_bin);
size -= binCount * sizeof(heap_bin);
for (uint32 i = 0; i < binCount; i++) {
heap_bin *bin = &heap->bins[i];
bin->element_size = binSizes[i];
bin->max_free_count = B_PAGE_SIZE / binSizes[i];
bin->page_list = NULL;
}
uint32 pageCount = size / B_PAGE_SIZE;
size_t pageTableSize = pageCount * sizeof(heap_page);
heap->page_table = (heap_page *)base;
base += pageTableSize;
size -= pageTableSize;
// the rest is now actually usable memory (rounded to the next page)
heap->base = (addr_t)(base + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
heap->size = (size_t)(size / B_PAGE_SIZE) * B_PAGE_SIZE;
// now we know the real page count
pageCount = heap->size / B_PAGE_SIZE;
heap->page_count = pageCount;
// zero out the heap alloc table at the base of the heap
memset((void *)heap->page_table, 0, pageTableSize);
for (uint32 i = 0; i < pageCount; i++)
heap->page_table[i].index = i;
// add all pages up into the free pages list
for (uint32 i = 1; i < pageCount; i++) {
heap->page_table[i - 1].next = &heap->page_table[i];
heap->page_table[i].prev = &heap->page_table[i - 1];
}
heap->free_pages = &heap->page_table[0];
heap->page_table[0].prev = NULL;
if (postSem) {
if (mutex_init(&heap->lock, "heap_mutex") < 0) {
panic("heap_attach(): error creating heap mutex\n");
return NULL;
}
} else {
// pre-init the mutex to at least fall through any semaphore calls
heap->lock.sem = -1;
heap->lock.holder = -1;
}
heap->next = NULL;
dprintf("heap_attach: attached to %p - usable range 0x%08lx - 0x%08lx\n",
heap, heap->base, heap->base + heap->size);
return heap;
}
static inline uint32
heap_next_alloc_id(heap_allocator *heap)
{
return atomic_add(&heap->large_alloc_id, 1) & ((1 << 9) - 1);
}
static inline void
heap_link_page(heap_page *page, heap_page **list)
{
page->prev = NULL;
page->next = *list;
if (page->next)
page->next->prev = page;
*list = page;
}
static inline void
heap_unlink_page(heap_page *page, heap_page **list)
{
if (page->prev)
page->prev->next = page->next;
if (page->next)
page->next->prev = page->prev;
if (list && *list == page) {
*list = page->next;
if (page->next)
page->next->prev = NULL;
}
}
static void *
heap_raw_alloc(heap_allocator *heap, size_t size, uint32 binIndex)
{
heap_bin *bin = NULL;
if (binIndex < heap->bin_count)
bin = &heap->bins[binIndex];
if (bin && bin->page_list != NULL) {
// we have a page where we have a free slot
void *address = NULL;
heap_page *page = bin->page_list;
if (page->free_list) {
// there's a previously freed entry we can use
address = page->free_list;
page->free_list = (addr_t *)*page->free_list;
} else {
// the page hasn't been fully allocated so use the next empty_index
address = (void *)(heap->base + page->index * B_PAGE_SIZE
+ page->empty_index * bin->element_size);
page->empty_index++;
}
page->free_count--;
if (page->free_count == 0) {
// the page is now full so we remove it from the page_list
bin->page_list = page->next;
if (page->next)
page->next->prev = NULL;
page->next = page->prev = NULL;
}
return address;
}
// we don't have anything free right away, we must allocate a new page
if (heap->free_pages == NULL) {
// there are no free pages anymore, we ran out of memory
TRACE(("heap %p: no free pages to allocate %lu bytes\n", heap, size));
return NULL;
}
if (bin) {
// small allocation, just grab the next free page
heap_page *page = heap->free_pages;
heap->free_pages = page->next;
if (page->next)
page->next->prev = NULL;
page->in_use = 1;
page->bin_index = binIndex;
page->free_count = bin->max_free_count - 1;
page->empty_index = 1;
page->free_list = NULL;
page->next = page->prev = NULL;
if (page->free_count > 0) {
// by design there are no other pages in the bins page list
bin->page_list = page;
}
// we return the first slot in this page
return (void *)(heap->base + page->index * B_PAGE_SIZE);
}
// large allocation, we must search for contiguous slots
bool found = false;
int32 first = -1;
for (uint32 i = 0; i < heap->page_count; i++) {
if (heap->page_table[i].in_use) {
first = -1;
continue;
}
if (first > 0) {
if ((1 + i - first) * B_PAGE_SIZE >= size) {
found = true;
break;
}
} else
first = i;
}
if (!found) {
TRACE(("heap %p: found no contiguous pages to allocate %ld bytes\n", heap, size));
return NULL;
}
uint32 allocationID = heap_next_alloc_id(heap);
uint32 pageCount = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
for (uint32 i = first; i < first + pageCount; i++) {
heap_page *page = &heap->page_table[i];
page->in_use = 1;
page->bin_index = binIndex;
heap_unlink_page(page, &heap->free_pages);
page->next = page->prev = NULL;
page->free_list = NULL;
page->allocation_id = allocationID;
}
return (void *)(heap->base + first * B_PAGE_SIZE);
}
#if DEBUG
static bool
is_valid_alignment(size_t number)
{
// this cryptic line accepts zero and all powers of two
return ((~number + 1) | ((number << 1) - 1)) == ~0UL;
}
#endif
static void *
heap_memalign(heap_allocator *heap, size_t alignment, size_t size,
bool *shouldGrow)
{
TRACE(("memalign(alignment = %lu, size = %lu)\n", alignment, size));
#if DEBUG
if (!is_valid_alignment(alignment))
panic("memalign() with an alignment which is not a power of 2\n");
#endif
mutex_lock(&heap->lock);
// ToDo: that code "aligns" the buffer because the bins are always
// aligned on their bin size
if (size < alignment)
size = alignment;
uint32 binIndex;
for (binIndex = 0; binIndex < heap->bin_count; binIndex++) {
if (size <= heap->bins[binIndex].element_size)
break;
}
void *address = heap_raw_alloc(heap, size, binIndex);
TRACE(("memalign(): asked to allocate %lu bytes, returning pointer %p\n", size, address));
if (heap->next == NULL) {
// suggest growing if we are the last heap and we have
// less than three free pages left
*shouldGrow = (heap->free_pages == NULL
|| heap->free_pages->next == NULL
|| heap->free_pages->next->next == NULL);
}
mutex_unlock(&heap->lock);
if (address == NULL)
return address;
#if PARANOID_KFREE
// make sure 0xdeadbeef is cleared if we do not overwrite the memory
// and the user does not clear it
((uint32 *)address)[1] = 0xcccccccc;
#endif
#if PARANOID_KMALLOC
memset(address, 0xcc, size);
#endif
return address;
}
static status_t
heap_free(heap_allocator *heap, void *address)
{
if (address == NULL)
return B_OK;
if ((addr_t)address < heap->base
|| (addr_t)address >= heap->base + heap->size) {
// this address does not belong to us
return B_ENTRY_NOT_FOUND;
}
mutex_lock(&heap->lock);
TRACE(("free(): asked to free at ptr = %p\n", address));
heap_page *page = &heap->page_table[((addr_t)address - heap->base) / B_PAGE_SIZE];
TRACE(("free(): page %p: bin_index %d, free_count %d\n", page, page->bin_index, page->free_count));
if (page->bin_index > heap->bin_count) {
panic("free(): page %p: invalid bin_index %d\n", page, page->bin_index);
mutex_unlock(&heap->lock);
return B_ERROR;
}
if (page->bin_index < heap->bin_count) {
// small allocation
heap_bin *bin = &heap->bins[page->bin_index];
if (((addr_t)address - heap->base - page->index * B_PAGE_SIZE) % bin->element_size != 0) {
panic("free(): passed invalid pointer %p supposed to be in bin for element size %ld\n", address, bin->element_size);
mutex_unlock(&heap->lock);
return B_ERROR;
}
#if PARANOID_KFREE
if (((uint32 *)address)[1] == 0xdeadbeef) {
// This block looks like it was freed already, walk the free list
// on this page to make sure this address doesn't exist.
for (addr_t *temp = page->free_list; temp != NULL; temp = (addr_t *)*temp) {
if (temp == address) {
panic("free(): address %p already exists in page free list\n", address);
mutex_unlock(&heap->lock);
return B_ERROR;
}
}
}
uint32 *dead = (uint32 *)address;
if (bin->element_size % 4 != 0) {
panic("free(): didn't expect a bin element size that is not a multiple of 4\n");
mutex_unlock(&heap->lock);
return B_ERROR;
}
// the first 4 bytes are overwritten with the next free list pointer later
for (uint32 i = 1; i < bin->element_size / sizeof(uint32); i++)
dead[i] = 0xdeadbeef;
#endif
// add the address to the page free list
*(addr_t *)address = (addr_t)page->free_list;
page->free_list = (addr_t *)address;
page->free_count++;
if (page->free_count == bin->max_free_count) {
// we are now empty, remove the page from the bin list
heap_unlink_page(page, &bin->page_list);
page->in_use = 0;
heap_link_page(page, &heap->free_pages);
} else if (page->free_count == 1) {
// we need to add ourselfs to the page list of the bin
heap_link_page(page, &bin->page_list);
} else {
// we might need to move back in the free pages list
if (page->next && page->next->free_count < page->free_count) {
// move ourselfs so the list stays ordered
heap_page *insert = page->next;
while (insert->next
&& insert->next->free_count < page->free_count)
insert = insert->next;
heap_unlink_page(page, &bin->page_list);
page->prev = insert;
page->next = insert->next;
if (page->next)
page->next->prev = page;
insert->next = page;
}
}
} else {
// large allocation, just return the pages to the page free list
uint32 allocationID = page->allocation_id;
uint32 maxPages = heap->page_count - page->index;
for (uint32 i = 0; i < maxPages; i++) {
// loop until we find the end of this allocation
if (!page[i].in_use || page[i].bin_index != heap->bin_count
|| page[i].allocation_id != allocationID)
break;
// this page still belongs to the same allocation
page[i].in_use = 0;
page[i].allocation_id = 0;
// return it to the free list
heap_link_page(&page[i], &heap->free_pages);
}
}
mutex_unlock(&heap->lock);
return B_OK;
}
static status_t
heap_realloc(heap_allocator *heap, void *address, void **newAddress,
size_t newSize)
{
if ((addr_t)address < heap->base
|| (addr_t)address >= heap->base + heap->size) {
// this address does not belong to us
return B_ENTRY_NOT_FOUND;
}
mutex_lock(&heap->lock);
TRACE(("realloc(address = %p, newSize = %lu)\n", address, newSize));
heap_page *page = &heap->page_table[((addr_t)address - heap->base) / B_PAGE_SIZE];
if (page->bin_index > heap->bin_count) {
panic("realloc(): page %p: invalid bin_index %d\n", page, page->bin_index);
mutex_unlock(&heap->lock);
return B_ERROR;
}
// find out the size of the old allocation first
size_t minSize = 0;
size_t maxSize = 0;
if (page->bin_index < heap->bin_count) {
// this was a small allocation
heap_bin *bin = &heap->bins[page->bin_index];
maxSize = bin->element_size;
if (page->bin_index > 0)
minSize = heap->bins[page->bin_index - 1].element_size + 1;
} else {
// this was a large allocation
uint32 allocationID = page->allocation_id;
uint32 maxPages = heap->page_count - page->index;
maxSize = B_PAGE_SIZE;
for (uint32 i = 1; i < maxPages; i++) {
if (!page[i].in_use || page[i].bin_index != heap->bin_count
|| page[i].allocation_id != allocationID)
break;
minSize += B_PAGE_SIZE;
maxSize += B_PAGE_SIZE;
}
}
mutex_unlock(&heap->lock);
// does the new allocation simply fit in the old allocation?
if (newSize > minSize && newSize <= maxSize) {
*newAddress = address;
return B_OK;
}
// if not, allocate a new chunk of memory
*newAddress = malloc(newSize);
if (*newAddress == NULL) {
// we tried but it didn't work out, but still the operation is done
return B_OK;
}
// copy the old data and free the old allocation
memcpy(*newAddress, address, min_c(maxSize, newSize));
free(address);
return B_OK;
}
// #pragma mark -
static int32
heap_grow_thread(void *)
{
heap_allocator *heap = sHeapList;
while (true) {
// wait for a request to grow the heap list
if (acquire_sem(sHeapGrowSem) < B_OK)
continue;
// find the last heap
while (heap->next)
heap = heap->next;
if (sLastGrowRequest != heap) {
// we have already grown since the latest request, just ignore
continue;
}
TRACE(("heap_grower: kernel heap will run out of memory soon, allocating new one\n"));
void *heapAddress = NULL;
area_id heapArea = create_area("additional heap", &heapAddress,
B_ANY_KERNEL_BLOCK_ADDRESS, HEAP_GROW_SIZE, B_FULL_LOCK,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (heapArea < B_OK) {
panic("heap_grower: couldn't allocate additional heap area\n");
continue;
}
heap_allocator *newHeap = heap_attach((addr_t)heapAddress,
HEAP_GROW_SIZE, true);
if (newHeap == NULL) {
panic("heap_grower: could not attach additional heap!\n");
delete_area(heapArea);
continue;
}
#if PARANOID_VALIDATION
heap_validate_heap(newHeap);
#endif
heap->next = newHeap;
TRACE(("heap_grower: new heap linked in\n"));
// notify anyone waiting for this request
release_sem_etc(sHeapGrownNotify, -1, B_RELEASE_ALL);
}
return 0;
}
status_t
heap_init(addr_t base, size_t size)
{
sHeapList = heap_attach(base, size, false);
// set up some debug commands
add_debugger_command("heap", &dump_heap_list, "dump stats about the kernel heap(s)");
return B_OK;
}
status_t
heap_init_post_sem()
{
// create the lock for the initial heap
if (mutex_init(&sHeapList->lock, "heap_mutex") < B_OK) {
panic("heap_init_post_sem(): error creating heap mutex\n");
return B_ERROR;
}
sHeapGrowSem = create_sem(0, "heap_grow_sem");
if (sHeapGrowSem < 0) {
panic("heap_init_post_sem(): failed to create heap grow sem\n");
return B_ERROR;
}
sHeapGrownNotify = create_sem(0, "heap_grown_notify");
if (sHeapGrownNotify < 0) {
panic("heap_init_post_sem(): failed to create heap grown notify sem\n");
return B_ERROR;
}
return B_OK;
}
status_t
heap_init_post_thread()
{
thread_id thread = spawn_kernel_thread(heap_grow_thread, "heap grower",
B_URGENT_PRIORITY, NULL);
if (thread < 0) {
panic("heap_init_post_thread(): cannot create heap grow thread\n");
return B_ERROR;
}
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
return B_OK;
}
// #pragma mark -
void *
memalign(size_t alignment, size_t size)
{
if (!kernel_startup && !are_interrupts_enabled()) {
panic("memalign(): called with interrupts disabled\n");
return NULL;
}
if (size > (HEAP_GROW_SIZE * 3) / 4) {
// don't even attempt such a huge allocation
panic("heap: huge allocation of %lu bytes asked!\n", size);
return NULL;
}
heap_allocator *heap = sHeapList;
while (heap) {
bool shouldGrow = false;
void *result = heap_memalign(heap, alignment, size, &shouldGrow);
if (heap->next == NULL && (shouldGrow || result == NULL)) {
// the last heap will or has run out of memory, notify the grower
sLastGrowRequest = heap;
if (result == NULL) {
// urgent request, do the request and wait for at max 250ms
release_sem(sHeapGrowSem);
acquire_sem_etc(sHeapGrownNotify, 1, B_RELATIVE_TIMEOUT, 250000);
} else {
// not so urgent, just notify the grower
release_sem_etc(sHeapGrowSem, 1, B_DO_NOT_RESCHEDULE);
}
}
if (result == NULL) {
heap = heap->next;
continue;
}
#if PARANOID_VALIDATION
heap_validate_heap(heap);
#endif
return result;
}
panic("heap: kernel heap has run out of memory\n");
return NULL;
}
void *
malloc(size_t size)
{
return memalign(0, size);
}
void
free(void *address)
{
if (!kernel_startup && !are_interrupts_enabled()) {
panic("free(): called with interrupts disabled\n");
return;
}
heap_allocator *heap = sHeapList;
while (heap) {
if (heap_free(heap, address) == B_OK) {
#if PARANOID_VALIDATION
heap_validate_heap(heap);
#endif
return;
}
heap = heap->next;
}
panic("free(): free failed for address %p\n", address);
}
void *
realloc(void *address, size_t newSize)
{
if (!kernel_startup && !are_interrupts_enabled()) {
panic("realloc(): called with interrupts disabled\n");
return NULL;
}
if (address == NULL)
return malloc(newSize);
if (newSize == 0) {
free(address);
return NULL;
}
heap_allocator *heap = sHeapList;
while (heap) {
void *newAddress = NULL;
if (heap_realloc(heap, address, &newAddress, newSize) == B_OK) {
#if PARANOID_VALIDATION
heap_validate_heap(heap);
#endif
return newAddress;
}
heap = heap->next;
}
panic("realloc(): failed to realloc address %p to size %lu\n", address, newSize);
return NULL;
}
void *
calloc(size_t numElements, size_t size)
{
void *address = memalign(0, numElements * size);
if (address != NULL)
memset(address, 0, numElements * size);
return address;
}
+4 -14
View File
@@ -25,7 +25,7 @@
#include <vm_cache.h>
#include <vm_low_memory.h>
#include <file_cache.h>
#include <memheap.h>
#include <heap.h>
#include <condition_variable.h>
#include <debug.h>
#include <console.h>
@@ -3480,16 +3480,8 @@ vm_init(kernel_args *args)
vm_page_init_num_pages(args);
sAvailableMemory = vm_page_num_pages() * B_PAGE_SIZE;
// reduce the heap size if we have not so much RAM
size_t heapSize = HEAP_SIZE;
if (sAvailableMemory < 100 * 1024 * 1024)
heapSize /= 4;
else if (sAvailableMemory < 200 * 1024 * 1024)
heapSize /= 2;
else if (sAvailableMemory >= 1024 * 1024 * 1024)
heapSize *= 2;
// map in the new heap and initialize it
size_t heapSize = INITIAL_HEAP_SIZE;
addr_t heapBase = vm_allocate_early(args, heapSize, heapSize,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
TRACE(("heap at 0x%lx\n", heapBase));
@@ -3607,8 +3599,7 @@ vm_init_post_sem(kernel_args *args)
mutex_init(&sMappingLock, "page mappings");
slab_init_post_sem();
return heap_init_post_sem(args);
return heap_init_post_sem();
}
@@ -3618,8 +3609,7 @@ vm_init_post_thread(kernel_args *args)
vm_page_init_post_thread(args);
vm_daemon_init();
vm_low_memory_init_post_thread();
return heap_init_post_thread(args);
return heap_init_post_thread();
}