kmalloc: initial implementation
This commit is contained in:
		
							
								
								
									
										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 <kmalloc.h> | ||||
| #include <mmu.h> | ||||
| #include <mm.h> | ||||
| #include <print.h> | ||||
| @@ -41,8 +42,11 @@ void video(void) { | ||||
| 	console_init(&myfb); | ||||
| } | ||||
|  | ||||
| void test_memory() { | ||||
| void test_mm() { | ||||
| 	struct page *p, *q; | ||||
|  | ||||
| 	print("\ntest_mm():\n"); | ||||
|  | ||||
| 	p = mm_get_free_pages(0); | ||||
| 	if (p) | ||||
| 		print("%x, %x\n", p, p->address); | ||||
| @@ -69,8 +73,46 @@ void test_memory() { | ||||
| 		print("%x, %x\n", p, p->address); | ||||
| 	else | ||||
| 		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) { | ||||
| 	char *lower, *upper; | ||||
| @@ -84,6 +126,7 @@ int main(void) { | ||||
|  | ||||
| 	//setup memory | ||||
| 	mm_init(); | ||||
| 	kmalloc_init(); | ||||
|  | ||||
| 	if (atags_first_mem_region(&atags)) { | ||||
| 		print("Error: atags must contain at least one memory region\n"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user