diff options
Diffstat (limited to 'kalloc.c')
-rw-r--r-- | kalloc.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/kalloc.c b/kalloc.c new file mode 100644 index 0000000..5ea38fd --- /dev/null +++ b/kalloc.c @@ -0,0 +1,158 @@ +/* + * physical memory allocator, intended to be used to allocate + * memory for user processes. allocates in 4096-byte "pages". + * free list is sorted and combines adjacent pages into + * long runs, to make it easier to allocate big segments. + * one reason the page size is 4k is that the x86 segment size + * granularity is 4k. + */ + +#include "param.h" +#include "types.h" +#include "defs.h" + +struct run { + struct run *next; + int len; // bytes +}; +struct run *freelist; + +void ktest(); + +/* + * initialize free list of physical pages. this code + * cheats by just considering the one megabyte of pages + * after _end. + */ +void +kinit() +{ + extern int end; + unsigned mem; + char *start; + + start = (char *) &end; + start = (char *) (((unsigned)start + PAGE) & ~(PAGE-1)); + mem = 256; // XXX + cprintf("mem = %d\n", mem * PAGE); + kfree(start, mem * PAGE); + ktest(); +} + +void +kfree(char *cp, int len) +{ + struct run **rr; + struct run *p = (struct run *) cp; + struct run *pend = (struct run *) (cp + len); + + if(len % PAGE) + panic("kfree"); + + rr = &freelist; + while(*rr){ + struct run *rend = (struct run *) ((char *)(*rr) + (*rr)->len); + if(p >= *rr && p < rend) + panic("freeing free page"); + if(pend == *rr){ + p->len = len + (*rr)->len; + p->next = (*rr)->next; + *rr = p; + return; + } + if(pend < *rr){ + p->len = len; + p->next = *rr; + *rr = p; + return; + } + if(p == rend){ + (*rr)->len += len; + if((*rr)->next && (*rr)->next == pend){ + (*rr)->len += (*rr)->next->len; + (*rr)->next = (*rr)->next->next; + } + return; + } + rr = &((*rr)->next); + } + p->len = len; + p->next = 0; + *rr = p; +} + +/* + * allocate n bytes of physical memory. + * returns a kernel-segment pointer. + * returns 0 if there's no run that's big enough. + */ +char * +kalloc(int n) +{ + struct run **rr; + + if(n % PAGE) + panic("kalloc"); + + rr = &freelist; + while(*rr){ + struct run *r = *rr; + if(r->len == n){ + *rr = r->next; + return (char *) r; + } + if(r->len > n){ + char *p = (char *)r + (r->len - n); + r->len -= n; + return p; + } + rr = &(*rr)->next; + } + return 0; +} + +void +ktest() +{ + char *p1, *p2, *p3; + + // test coalescing + p1 = kalloc(4 * PAGE); + kfree(p1 + 3*PAGE, PAGE); + kfree(p1 + 2*PAGE, PAGE); + kfree(p1, PAGE); + kfree(p1 + PAGE, PAGE); + p2 = kalloc(4 * PAGE); + if(p2 != p1) + panic("ktest"); + kfree(p2, 4 * PAGE); + + // test finding first run that fits + p1 = kalloc(1 * PAGE); + p2 = kalloc(1 * PAGE); + kfree(p1, PAGE); + p3 = kalloc(2 * PAGE); + kfree(p2, PAGE); + kfree(p3, 2 * PAGE); + + // test running out of memory + p1 = 0; + while(1){ + p2 = kalloc(PAGE); + if(p2 == 0) + break; + *(char **)p2 = p1; + p1 = p2; + } + while(p1){ + p2 = *(char **)p1; + kfree(p1, PAGE); + p1 = p2; + } + p1 = kalloc(PAGE * 20); + if(p1 == 0) + panic("ktest2"); + kfree(p1, PAGE * 20); + + cprintf("ktest ok\n"); +} |