Fixes VFS open bug
[akaros.git] / kern / src / kmalloc.c
index 556d33f..1ec948f 100644 (file)
@@ -2,6 +2,7 @@
  * See the COPYRIGHT files at the top of this source tree for full 
  * license information.
  * 
+ * Barret Rhoden <brho@cs.berkeley.edu>
  * Kevin Klues <klueska@cs.berkeley.edu>    
  */
 
 #define SINIT(x) x
 #endif
 
+#ifdef __DEPUTY__
+#pragma nodeputy
+#endif
+
 #include <ros/common.h>
-#include <ros/error.h>
+#include <error.h>
 #include <pmap.h>
 #include <kmalloc.h>
 #include <stdio.h>
+#include <slab.h>
+#include <assert.h>
 
 #define kmallocdebug(args...)  //printk(args)
 
-char *RO BND(end, maxaddrpa_ptr + IVY_KERNBASE) boot_freemem;
-
 //List of physical pages used by kmalloc
-static spinlock_t pages_list_lock = 0;
+static spinlock_t pages_list_lock = SPINLOCK_INITIALIZER;
 static page_list_t LCKD(&pages_list_lock)pages_list;
 
-/*
- * Allocate n bytes of physical memory aligned on an 
- * align-byte boundary.  Align must be a power of two.
- * Return kernel virtual address.  Returned memory is uninitialized.
- *
- * If we're out of memory, boot_alloc should panic.
- * This function may ONLY be used during initialization,
- * before the page_free_list has been set up.
- */
-void* boot_alloc(uint32_t n, uint32_t align)
+struct kmem_cache *kmalloc_caches[NUM_KMALLOC_CACHES];
+void kmalloc_init(void)
 {
-       extern char (SNT RO end)[];
-       void *v;
-
-       // Initialize boot_freemem if this is the first time.
-       // 'end' is a magic symbol automatically generated by the linker,
-       // which points to the end of the kernel's bss segment -
-       // i.e., the first virtual address that the linker
-       // did _not_ assign to any kernel code or global variables.
-       if (boot_freemem == 0) {
-               boot_freemem = SINIT(TC(end));
+       // i want to know if we ever make the tag bigger (should be below 16 bytes)
+       static_assert(sizeof(struct kmalloc_tag) <= KMALLOC_ALIGNMENT);
+       // build caches of common sizes
+       size_t ksize = KMALLOC_SMALLEST;
+       for (int i = 0; i < NUM_KMALLOC_CACHES; i++) {
+               kmalloc_caches[i] = kmem_cache_create("kmalloc_cache", ksize,
+                                                     KMALLOC_ALIGNMENT, 0, 0, 0);
+               ksize <<= 1;
        }
-
-       //      Step 1: round boot_freemem up to be aligned properly
-       char RO*tmp = PTRROUNDUP(boot_freemem, align);
-       boot_freemem = SINIT(tmp);
-
-       //      Step 2: save current value of boot_freemem as allocated chunk
-       v = boot_freemem;
-       //  Step 2.5: check if we can alloc
-       if (PADDR(boot_freemem + n) > maxaddrpa)
-               panic("Out of memory in boot alloc, you fool!\n");
-       //      Step 3: increase boot_freemem to record allocation
-       boot_freemem = SINIT(boot_freemem + n);
-       //      Step 4: return allocated chunk
-       return v;
 }
 
-void* boot_calloc(uint32_t _n, size_t sz, uint32_t align)
+void *kmalloc(size_t size, int flags) 
 {
-       extern char (SNT RO end)[];
-       uint32_t n = _n *sz;
-       void *v;
-
-       // Initialize boot_freemem if this is the first time.
-       // 'end' is a magic symbol automatically generated by the linker,
-       // which points to the end of the kernel's bss segment -
-       // i.e., the first virtual address that the linker
-       // did _not_ assign to any kernel code or global variables.
-       if (boot_freemem == 0)
-               boot_freemem = SINIT(TC(end));
-
-       //      Step 1: round boot_freemem up to be aligned properly
-       char RO*tmp = PTRROUNDUP(boot_freemem, align);
-       boot_freemem = SINIT(tmp);
-
-       //      Step 2: save current value of boot_freemem as allocated chunk
-       v = boot_freemem;
-       //  Step 2.5: check if we can alloc
-       if (PADDR(boot_freemem + n) > maxaddrpa)
-               panic("Out of memory in boot alloc, you fool!\n");
-       //      Step 3: increase boot_freemem to record allocation
-       boot_freemem = SINIT(boot_freemem + n);
-       //  Step 4: zero allocated chunk
-       memset(v,0,n);
-       //      Step 5: return allocated chunk
-       return v;
+       // reserve space for bookkeeping and preserve alignment
+       size_t ksize = size + KMALLOC_OFFSET;
+       void *buf;
+       int cache_id;
+       // determine cache to pull from
+       if (ksize <= KMALLOC_SMALLEST)
+               cache_id = 0;
+       else
+               cache_id = LOG2_UP(ksize) - LOG2_UP(KMALLOC_SMALLEST);
+       // if we don't have a cache to handle it, alloc cont pages
+       if (cache_id >= NUM_KMALLOC_CACHES) {
+               size_t num_pgs = ROUNDUP(size + sizeof(struct kmalloc_tag), PGSIZE) /
+                                          PGSIZE;
+               buf = get_cont_pages(LOG2_UP(num_pgs), flags);
+               if (!buf)
+                       panic("Kmalloc failed!  Handle me!");
+               // fill in the kmalloc tag
+               struct kmalloc_tag *tag = buf;
+               tag->flags = KMALLOC_TAG_PAGES;
+               tag->num_pages = num_pgs;
+               tag->canary = KMALLOC_CANARY;
+               return buf + KMALLOC_OFFSET;
+       }
+       // else, alloc from the appropriate cache
+       buf = kmem_cache_alloc(kmalloc_caches[cache_id], flags);
+       if (!buf)
+               panic("Kmalloc failed!  Handle me!");
+       // store a pointer to the buffers kmem_cache in it's bookkeeping space
+       struct kmalloc_tag *tag = buf;
+       tag->flags = KMALLOC_TAG_CACHE;
+       tag->my_cache = kmalloc_caches[cache_id];
+       tag->canary = KMALLOC_CANARY;
+       return buf + KMALLOC_OFFSET;
 }
 
-void kmalloc_init(
+void *kzmalloc(size_t size, int flags
 {
-       LIST_INIT(&pages_list);
+       void *v = kmalloc(size, flags);
+       if (! v)
+               return v;
+       memset(v, 0, size);
+       return v;
 }
 
-void* kmalloc(size_t size, int flags) 
-{
-       if (size == 0)
-               return NULL;
-
-       int npages = ROUNDUP(size, PGSIZE) / PGSIZE;
-       
-       // Find 'npages' free consecutive pages
-       int first = -1;
-       kmallocdebug("naddrpages: %u\n", naddrpages);
-       kmallocdebug("npages: %u\n", npages);
-       for(int i=(naddrpages-1); i>=(npages-1); i--) {
-               int j;
-               for(j=i; j>=i-(npages-1); j--) {
-                       if( !page_is_free(j) )
-                               break;
-               }
-               if( j == i-(npages-1)-1 ) {
-                       first = j+1;
-                       break;
-               }
-       }
-       //If we couldn't find them, return NULL
-       if( first == -1 )
-               return NULL;
-       
-       //Otherwise go ahead and allocate them to ourselves now
-       for(int i=0; i<npages; i++) {
-               page_t* page;
-
-               page_alloc_specific(&page, first+i);
-               page_incref(page);
-               page->num_cons_links = npages-i;
-
-               spin_lock_irqsave(&pages_list_lock);
-               LIST_INSERT_HEAD(&pages_list, page, page_link);
-               spin_unlock_irqsave(&pages_list_lock);
-
-               kmallocdebug("mallocing page: %u\n", first+i);
-               kmallocdebug("at addr: %p\n", ppn2kva(first+i));
-       }
-       //And return the address of the first one
-       return ppn2kva(first);
+void *krealloc(void* buf, size_t size, int flags) {
+       struct kmalloc_tag *tag = (struct kmalloc_tag*)(buf - KMALLOC_OFFSET);
+       if (tag->my_cache->obj_size >= size)
+               return buf;
+       kfree(buf);
+       return kmalloc(size, flags);
 }
 
 void kfree(void *addr)
 {
-       kmallocdebug("incoming address: %p\n", addr);
-       page_t* page = kva2page(addr);
-       int num_links = page->num_cons_links;
-       kmallocdebug("getting page: %u\n", page2ppn(page));
-       for(int i=0; i<num_links; i++) {
-               page_t* p = ppn2page((page2ppn(page) + i));
-               LIST_REMOVE(p, page_link);
-               page_free(p);
-               kmallocdebug("freeing page: %d\n", page2ppn(p));
-       }
+       if(addr == NULL)
+               return;
+       struct kmalloc_tag *tag = (struct kmalloc_tag*)(addr - KMALLOC_OFFSET);
+       assert(tag->canary == KMALLOC_CANARY);
+       if (tag->flags & KMALLOC_TAG_CACHE)
+               kmem_cache_free(tag->my_cache, addr - KMALLOC_OFFSET);
+       else if (tag->flags & KMALLOC_TAG_PAGES) {
+               free_cont_pages(addr - KMALLOC_OFFSET, LOG2_UP(tag->num_pages));
+       } else 
+               panic("[Italian Accent]: Che Cazzo! BO! Flag in kmalloc!!!");
 }