kmalloc: initial implementation
This commit is contained in:
parent
d7292f1fe2
commit
ccbee1d142
27
include/kmalloc.h
Normal file
27
include/kmalloc.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com>
|
||||||
|
|
||||||
|
This file is part of Aedrix.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KMALLOC_H
|
||||||
|
#define KMALLOC_H
|
||||||
|
|
||||||
|
void* kmalloc(unsigned int bytes);
|
||||||
|
void kfree(void *p);
|
||||||
|
|
||||||
|
#endif /* KMALLOC_H */
|
155
kernel/kmalloc.c
Normal file
155
kernel/kmalloc.c
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com>
|
||||||
|
|
||||||
|
This file is part of Aedrix.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kmalloc.h>
|
||||||
|
#include <mm.h>
|
||||||
|
#include <list.h>
|
||||||
|
#include <print.h>
|
||||||
|
|
||||||
|
struct kmalloc_region {
|
||||||
|
struct dlist_node list;
|
||||||
|
void *end;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KMALLOC_MIN_ALIGNMENT 4
|
||||||
|
#define KMALLOC_REGION_SIZE (sizeof(struct kmalloc_region) + (sizeof(struct kmalloc_region) % KMALLOC_MIN_ALIGNMENT ? KMALLOC_MIN_ALIGNMENT - sizeof(struct kmalloc_region) % KMALLOC_MIN_ALIGNMENT : 0)) //round actual struct size up so its aligned
|
||||||
|
#define KMALLOC_MAX_TRIES 3 //Max number of times the heap can be grown for a single request
|
||||||
|
|
||||||
|
|
||||||
|
int curr_page_power = 0;
|
||||||
|
struct dlist_node kmalloc_free_regions;
|
||||||
|
|
||||||
|
void kmalloc_init() {
|
||||||
|
init_list(&kmalloc_free_regions);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kmalloc_region *find_free_region(unsigned int bytes) {
|
||||||
|
struct kmalloc_region *it;
|
||||||
|
for_each_list(it, &kmalloc_free_regions, struct kmalloc_region, list) {
|
||||||
|
unsigned int size = (char *)it->end - (char*)it + 1;
|
||||||
|
if (size >= KMALLOC_REGION_SIZE + bytes) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (struct kmalloc_region *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kmalloc_region *join_regions(struct kmalloc_region *first, struct kmalloc_region *second) {
|
||||||
|
first->end = second->end;
|
||||||
|
remove(&second->list);
|
||||||
|
|
||||||
|
//TODO free pages here if we get a big enough free space
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coalesce(struct kmalloc_region *region) {
|
||||||
|
struct kmalloc_region *prev = 0, *next = 0;
|
||||||
|
|
||||||
|
if (region->list.next != &kmalloc_free_regions)
|
||||||
|
next = container(region->list.next, struct kmalloc_region, list);
|
||||||
|
if (region->list.prev != &kmalloc_free_regions)
|
||||||
|
prev = container(region->list.prev, struct kmalloc_region, list);
|
||||||
|
|
||||||
|
if (next && (char *)next == (char *)region->end + 1) {
|
||||||
|
region = join_regions(region, next);
|
||||||
|
coalesce(region);
|
||||||
|
}
|
||||||
|
if (prev && (char *)region == (char *)prev->end + 1) {
|
||||||
|
region = join_regions(prev, region);
|
||||||
|
coalesce(region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_region(struct kmalloc_region *region) {
|
||||||
|
struct kmalloc_region *it;
|
||||||
|
for_each_list(it, &kmalloc_free_regions, struct kmalloc_region, list) {
|
||||||
|
if (region < it) {
|
||||||
|
insert_before(&it->list, ®ion->list);
|
||||||
|
goto coalesce;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_before(&kmalloc_free_regions, ®ion->list);
|
||||||
|
|
||||||
|
coalesce:
|
||||||
|
coalesce(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *_kmalloc(unsigned int bytes, unsigned int tries);
|
||||||
|
|
||||||
|
void *grow_and_retry(unsigned int bytes, unsigned int tries) {
|
||||||
|
struct page *p;
|
||||||
|
|
||||||
|
if ((p = mm_get_free_pages(curr_page_power))) {
|
||||||
|
struct kmalloc_region *region = (struct kmalloc_region *)p->address;
|
||||||
|
//TODO don't throw away p, but keep it in a list (allocate a little at the beginning of this chunk for a list element), so we can free pages later if we want to
|
||||||
|
region->end = (char *)region + MM_PAGE_SIZE * (1 << curr_page_power) - 1;
|
||||||
|
add_region(region);
|
||||||
|
curr_page_power++;
|
||||||
|
return _kmalloc(bytes, tries);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *_kmalloc(unsigned int bytes, unsigned int tries) {
|
||||||
|
struct kmalloc_region *region;
|
||||||
|
|
||||||
|
if (tries > KMALLOC_MAX_TRIES)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (bytes % KMALLOC_MIN_ALIGNMENT)
|
||||||
|
bytes += KMALLOC_MIN_ALIGNMENT - (bytes % KMALLOC_MIN_ALIGNMENT);
|
||||||
|
|
||||||
|
if ((region = find_free_region(bytes))) {
|
||||||
|
//if there's enough space leftover in the region after this allocation
|
||||||
|
//for another, split the region.
|
||||||
|
if ((unsigned int)((char *)region->end - (char *)region)
|
||||||
|
>= 2*KMALLOC_REGION_SIZE + KMALLOC_MIN_ALIGNMENT + bytes) {
|
||||||
|
struct kmalloc_region *leftovers;
|
||||||
|
leftovers = (struct kmalloc_region *)((char *)region + KMALLOC_REGION_SIZE + bytes);
|
||||||
|
leftovers->end = region->end;
|
||||||
|
region->end = (char *)leftovers - 1;
|
||||||
|
insert_after(®ion->list, &leftovers->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(®ion->list);
|
||||||
|
return (char *)region + KMALLOC_REGION_SIZE;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return grow_and_retry(bytes, tries + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *kmalloc(unsigned int bytes) {
|
||||||
|
return _kmalloc(bytes, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kfree(void *p) {
|
||||||
|
struct kmalloc_region *region;
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
print("Error: kfree was passed a null pointer. Ignoring. This indicates a possible memory leak.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
region = (struct kmalloc_region *)((char *)p - KMALLOC_REGION_SIZE);
|
||||||
|
add_region(region);
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <atags.h>
|
#include <atags.h>
|
||||||
|
#include <kmalloc.h>
|
||||||
#include <mmu.h>
|
#include <mmu.h>
|
||||||
#include <mm.h>
|
#include <mm.h>
|
||||||
#include <print.h>
|
#include <print.h>
|
||||||
@ -41,8 +42,11 @@ void video(void) {
|
|||||||
console_init(&myfb);
|
console_init(&myfb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_memory() {
|
void test_mm() {
|
||||||
struct page *p, *q;
|
struct page *p, *q;
|
||||||
|
|
||||||
|
print("\ntest_mm():\n");
|
||||||
|
|
||||||
p = mm_get_free_pages(0);
|
p = mm_get_free_pages(0);
|
||||||
if (p)
|
if (p)
|
||||||
print("%x, %x\n", p, p->address);
|
print("%x, %x\n", p, p->address);
|
||||||
@ -69,9 +73,47 @@ void test_memory() {
|
|||||||
print("%x, %x\n", p, p->address);
|
print("%x, %x\n", p, p->address);
|
||||||
else
|
else
|
||||||
print("Error: failed to allocate memory for p\n");
|
print("Error: failed to allocate memory for p\n");
|
||||||
|
mm_put_free_pages(p);
|
||||||
|
mm_put_free_pages(q);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_kmalloc() {
|
||||||
|
void *a, *b, *c, *d;
|
||||||
|
|
||||||
|
print("\ntest_kmalloc():\n");
|
||||||
|
|
||||||
|
a = kmalloc(4);
|
||||||
|
print("a: %x\n", a);
|
||||||
|
b = kmalloc(13);
|
||||||
|
print("b: %x\n", b);
|
||||||
|
c = kmalloc(4);
|
||||||
|
print("c: %x\n", c);
|
||||||
|
d = kmalloc(25);
|
||||||
|
print("d: %x\n", d);
|
||||||
|
|
||||||
|
kfree(c);
|
||||||
|
kfree(b);
|
||||||
|
kfree(a);
|
||||||
|
kfree(d);
|
||||||
|
|
||||||
|
a = kmalloc(13);
|
||||||
|
print("a: %x\n", a);
|
||||||
|
b = kmalloc(4);
|
||||||
|
print("b: %x\n", b);
|
||||||
|
c = kmalloc(25);
|
||||||
|
print("c: %x\n", c);
|
||||||
|
d = kmalloc(7);
|
||||||
|
print("d: %x\n", d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_memory() {
|
||||||
|
test_mm();
|
||||||
|
test_kmalloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void kmalloc_init();
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
char *lower, *upper;
|
char *lower, *upper;
|
||||||
struct atag *atags;
|
struct atag *atags;
|
||||||
@ -84,6 +126,7 @@ int main(void) {
|
|||||||
|
|
||||||
//setup memory
|
//setup memory
|
||||||
mm_init();
|
mm_init();
|
||||||
|
kmalloc_init();
|
||||||
|
|
||||||
if (atags_first_mem_region(&atags)) {
|
if (atags_first_mem_region(&atags)) {
|
||||||
print("Error: atags must contain at least one memory region\n");
|
print("Error: atags must contain at least one memory region\n");
|
||||||
|
Loading…
Reference in New Issue
Block a user