Mon 14 Oct 23:06:38 CEST 2024
This commit is contained in:
		
							parent
							
								
									dc7fb184f8
								
							
						
					
					
						commit
						dffe6dee58
					
				
							
								
								
									
										225
									
								
								kernel/kmalloc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								kernel/kmalloc.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,225 @@ | ||||||
|  | /*
 | ||||||
|  | Copyright (C) 2016-2019 The University of Notre Dame | ||||||
|  | This software is distributed under the GNU General Public License. | ||||||
|  | See the file LICENSE for details. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include "kmalloc.h" | ||||||
|  | #include "console.h" | ||||||
|  | #include "kernel/types.h" | ||||||
|  | #include "memorylayout.h" | ||||||
|  | 
 | ||||||
|  | #define KUNIT sizeof(struct kmalloc_chunk) | ||||||
|  | 
 | ||||||
|  | #define KMALLOC_STATE_FREE 0xa1a1a1a1 | ||||||
|  | #define KMALLOC_STATE_USED 0xbfbfbfbf | ||||||
|  | 
 | ||||||
|  | struct kmalloc_chunk { | ||||||
|  | 	int state; | ||||||
|  | 	int length; | ||||||
|  | 	struct kmalloc_chunk *next; | ||||||
|  | 	struct kmalloc_chunk *prev; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct kmalloc_chunk *head = 0; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Initialize the linked list by creating a single chunk at | ||||||
|  | a given start address and length.  The chunk is initially | ||||||
|  | free and has no next or previous chunks. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | void kmalloc_init(char *start, int length) | ||||||
|  | { | ||||||
|  | 	head = (struct kmalloc_chunk *) start; | ||||||
|  | 	head->state = KMALLOC_STATE_FREE; | ||||||
|  | 	head->length = length; | ||||||
|  | 	head->next = 0; | ||||||
|  | 	head->prev = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Split a large chunk into two, such that the current chunk | ||||||
|  | has the desired length, and the next chunk has the remainder. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | static void ksplit(struct kmalloc_chunk *c, int length) | ||||||
|  | { | ||||||
|  | 	struct kmalloc_chunk *n = (struct kmalloc_chunk *) ((char *) c + length); | ||||||
|  | 
 | ||||||
|  | 	n->state = KMALLOC_STATE_FREE; | ||||||
|  | 	n->length = c->length - length; | ||||||
|  | 	n->prev = c; | ||||||
|  | 	n->next = c->next; | ||||||
|  | 
 | ||||||
|  | 	if(c->next) | ||||||
|  | 		c->next->prev = n; | ||||||
|  | 
 | ||||||
|  | 	c->next = n; | ||||||
|  | 	c->length = length; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Allocate a chunk of memory of the given length. | ||||||
|  | To avoid fragmentation, round up the length to | ||||||
|  | a multiple of the chunk size.  Then, search fo | ||||||
|  | a chunk of the desired size, and split it if necessary. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | void *kmalloc(int length) | ||||||
|  | { | ||||||
|  | 	// round up length to a multiple of KUNIT
 | ||||||
|  | 	int extra = length % KUNIT; | ||||||
|  | 	if(extra) | ||||||
|  | 		length += (KUNIT - extra); | ||||||
|  | 
 | ||||||
|  | 	// then add one more unit to accommodate the chunk header
 | ||||||
|  | 	length += KUNIT; | ||||||
|  | 
 | ||||||
|  | 	struct kmalloc_chunk *c = head; | ||||||
|  | 
 | ||||||
|  | 	while(1) { | ||||||
|  | 		if(!c) { | ||||||
|  | 			printf("kmalloc: out of memory!\n"); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		if(c->state == KMALLOC_STATE_FREE && c->length >= length) | ||||||
|  | 			break; | ||||||
|  | 		c = c->next; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// split the chunk if the remainder is greater than two units
 | ||||||
|  | 	if((c->length-length) > 2 * KUNIT) { | ||||||
|  | 		ksplit(c, length); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c->state = KMALLOC_STATE_USED; | ||||||
|  | 
 | ||||||
|  | 	// return a pointer to the memory following the chunk header
 | ||||||
|  | 	return (c + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Attempt to merge a chunk with its successor, | ||||||
|  | if it exists and both are in the free state. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | static void kmerge(struct kmalloc_chunk *c) | ||||||
|  | { | ||||||
|  | 	if(!c) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if(c->state != KMALLOC_STATE_FREE) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if(c->next && c->next->state == KMALLOC_STATE_FREE) { | ||||||
|  | 		c->length += c->next->length; | ||||||
|  | 		if(c->next->next) { | ||||||
|  | 			c->next->next->prev = c; | ||||||
|  | 		} | ||||||
|  | 		c->next = c->next->next; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | Free memory by marking the chunk as de-allocated, | ||||||
|  | then attempting to merge it with the predecessor and successor. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | void kfree(void *ptr) | ||||||
|  | { | ||||||
|  | 	struct kmalloc_chunk *c = (struct kmalloc_chunk *) ptr; | ||||||
|  | 	c--; | ||||||
|  | 
 | ||||||
|  | 	if(c->state != KMALLOC_STATE_USED) { | ||||||
|  | 		printf("invalid kfree(%x)\n", ptr); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c->state = KMALLOC_STATE_FREE; | ||||||
|  | 
 | ||||||
|  | 	kmerge(c); | ||||||
|  | 	kmerge(c->prev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void kmalloc_debug() | ||||||
|  | { | ||||||
|  | 	struct kmalloc_chunk *c; | ||||||
|  | 
 | ||||||
|  | 	printf("state ptr      prev     next     length\n"); | ||||||
|  | 
 | ||||||
|  | 	for(c = head; c; c = c->next) { | ||||||
|  | 		if(c->state == KMALLOC_STATE_FREE) { | ||||||
|  | 			printf("F"); | ||||||
|  | 		} else if(c->state == KMALLOC_STATE_USED) { | ||||||
|  | 			printf("U"); | ||||||
|  | 		} else { | ||||||
|  | 			printf("kmalloc list corrupted at %x!\n", c); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		printf("     %x %x %x %d\n", c, c->prev, c->next, c->length); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Testing
 | ||||||
|  | 
 | ||||||
|  | static void setup(void) | ||||||
|  | { | ||||||
|  | 	kmalloc_init((char *) KMALLOC_START, KMALLOC_LENGTH); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void tear_down(void) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int kmalloc_test_single_alloc(void) | ||||||
|  | { | ||||||
|  | 	char *ptr = kmalloc(128); | ||||||
|  | 	struct kmalloc_chunk *next = 0; | ||||||
|  | 	int res = (unsigned long) ptr == (unsigned long) head + sizeof(struct kmalloc_chunk); | ||||||
|  | 	res &= head->state == KMALLOC_STATE_USED; | ||||||
|  | 	res &= head->length == 128 + sizeof(struct kmalloc_chunk); | ||||||
|  | 	res &= (char *) head->next == (char *) KMALLOC_START + head->length; | ||||||
|  | 	next = head->next; | ||||||
|  | 	res &= next->state == KMALLOC_STATE_FREE; | ||||||
|  | 	res &= next->length == KMALLOC_LENGTH - head->length; | ||||||
|  | 
 | ||||||
|  | 	return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int kmalloc_test_single_alloc_and_free(void) | ||||||
|  | { | ||||||
|  | 	char *ptr = kmalloc(128); | ||||||
|  | 	int res; | ||||||
|  | 	kfree(ptr); | ||||||
|  | 	res = head->state == KMALLOC_STATE_FREE; | ||||||
|  | 	res &= head->next == 0; | ||||||
|  | 	res &= head->length == KMALLOC_LENGTH; | ||||||
|  | 
 | ||||||
|  | 	return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int kmalloc_test(void) | ||||||
|  | { | ||||||
|  | 	int (*tests[]) (void) = { | ||||||
|  | 	kmalloc_test_single_alloc, kmalloc_test_single_alloc_and_free,}; | ||||||
|  | 
 | ||||||
|  | 	int i = 0; | ||||||
|  | 	for(i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { | ||||||
|  | 		printf("running test %d...", i); | ||||||
|  | 		int res; | ||||||
|  | 
 | ||||||
|  | 		setup(); | ||||||
|  | 		res = tests[i] (); | ||||||
|  | 		tear_down(); | ||||||
|  | 
 | ||||||
|  | 		if(!res) { | ||||||
|  | 			printf("failed\n"); | ||||||
|  | 			printf("\ntest %d failed.\n"); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		printf("succeeded\n"); | ||||||
|  | 	} | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user