kmalloc_align()
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 10 Dec 2013 02:30:30 +0000 (18:30 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jan 2014 21:07:52 +0000 (13:07 -0800)
Can be freed with kfree().  Same goes for kzmalloc_align().  Alignment
must be a multiple of sizeof long.

Did some basic tests on 32 and 64 bit.  32 bit complains at compile time
(ROUNDUP).  Feel free to fix that, if someone has a nice way.  Or make
the compiler smarter to not complain about casts in branches it'll never
take.

kern/include/kmalloc.h
kern/include/ros/common.h
kern/src/kmalloc.c
kern/src/testing.c

index fd6776d..e0141f4 100644 (file)
 #define KMALLOC_ALIGNMENT 16
 #define KMALLOC_SMALLEST 32
 #define KMALLOC_LARGEST KMALLOC_SMALLEST << NUM_KMALLOC_CACHES
-#define KMALLOC_OFFSET ROUNDUP(sizeof(struct kmalloc_tag), KMALLOC_ALIGNMENT)
 
 void kmalloc_init(void);
 void* (DALLOC(size) kmalloc)(size_t size, int flags);
 void* (DALLOC(size) kzmalloc)(size_t size, int flags);
+void *kmalloc_align(size_t size, int flags, size_t align);
+void *kzmalloc_align(size_t size, int flags, size_t align);
 void* (DALLOC(size) krealloc)(void* buf, size_t size, int flags);
 void  (DFREE(addr) kfree)(void *addr);
 void kmalloc_canary_check(char *str);
 void *debug_canary;
 
-/* Flags */
-#define KMALLOC_TAG_CACHE 1
-#define KMALLOC_TAG_PAGES 2
+/* Flags to pass to kmalloc */
 /* Not implemented yet. Block until it is available. */
-#define KMALLOC_WAIT   4
+#define KMALLOC_WAIT                   4
+
+/* Kmalloc tag flags looks like this:
+ *
+ * +--------------28---------------+-----4------+
+ * |       Flag specific data      |    Flags   |
+ * +-------------------------------+------------+
+ */
+#define KMALLOC_TAG_CACHE              1       /* memory came from slabs */
+#define KMALLOC_TAG_PAGES              2       /* memory came from page allocator */
+#define KMALLOC_TAG_UNALIGN            3       /* not a real tag, jump back by offset */
+#define KMALLOC_ALIGN_SHIFT            4       /* max flag is 16 */
+#define KMALLOC_FLAG_MASK              ((1 << KMALLOC_ALIGN_SHIFT) - 1)
 
 #define KMALLOC_CANARY 0xdeadbabe
 
+/* The kmalloc align/free paths require that flags is at the end of this
+ * struct, and that it is not padded. */
 struct kmalloc_tag {
-       int flags;
-       uint32_t canary;
        union {
                struct kmem_cache *my_cache WHEN(flags == KMALLOC_TAG_CACHE);
                size_t num_pages WHEN(flags == KMALLOC_TAG_PAGES);
+               uint64_t unused_force_align;
        };
+       uint32_t canary;
+       int flags;
 };
 
 #endif //ROS_KERN_KMALLOC_H
index eb9c72f..feba4d3 100644 (file)
@@ -34,7 +34,7 @@ typedef unsigned long uintreg_t;
 #define PiB    1125899906842624ull
 #define EiB    1152921504606846976ull
 
-#define ALIGNED(p, a)  (!(((uintptr)(p)) & ((a)-1)))
+#define ALIGNED(p, a)  (!(((uintptr_t)(p)) & ((a)-1)))
 
 #define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))
 
index c1f14ef..53e9721 100644 (file)
@@ -32,8 +32,7 @@ static page_list_t LCKD(&pages_list_lock)pages_list;
 struct kmem_cache *kmalloc_caches[NUM_KMALLOC_CACHES];
 void kmalloc_init(void)
 {
-       // i want to know if we ever make the tag bigger (should be below 16 bytes)
-       static_assert(sizeof(struct kmalloc_tag) <= KMALLOC_ALIGNMENT);
+       static_assert(sizeof(struct kmalloc_tag) == 16);
        // build caches of common sizes
        size_t ksize = KMALLOC_SMALLEST;
        for (int i = 0; i < NUM_KMALLOC_CACHES; i++) {
@@ -46,7 +45,7 @@ void kmalloc_init(void)
 void *kmalloc(size_t size, int flags) 
 {
        // reserve space for bookkeeping and preserve alignment
-       size_t ksize = size + KMALLOC_OFFSET;
+       size_t ksize = size + sizeof(struct kmalloc_tag);
        void *buf;
        int cache_id;
        // determine cache to pull from
@@ -66,7 +65,7 @@ void *kmalloc(size_t size, int flags)
                tag->flags = KMALLOC_TAG_PAGES;
                tag->num_pages = num_pgs;
                tag->canary = KMALLOC_CANARY;
-               return buf + KMALLOC_OFFSET;
+               return buf + sizeof(struct kmalloc_tag);
        }
        // else, alloc from the appropriate cache
        buf = kmem_cache_alloc(kmalloc_caches[cache_id], flags);
@@ -77,7 +76,7 @@ void *kmalloc(size_t size, int flags)
        tag->flags = KMALLOC_TAG_CACHE;
        tag->my_cache = kmalloc_caches[cache_id];
        tag->canary = KMALLOC_CANARY;
-       return buf + KMALLOC_OFFSET;
+       return buf + sizeof(struct kmalloc_tag);
 }
 
 void *kzmalloc(size_t size, int flags) 
@@ -89,8 +88,44 @@ void *kzmalloc(size_t size, int flags)
        return v;
 }
 
-void *krealloc(void* buf, size_t size, int flags) {
-       struct kmalloc_tag *tag = (struct kmalloc_tag*)(buf - KMALLOC_OFFSET);
+void *kmalloc_align(size_t size, int flags, size_t align)
+{
+       void *addr, *retaddr;
+       int *tag_flags, offset;
+       /* alignment requests must be a multiple of long, even though we only need
+        * int in the current code. */
+       assert(ALIGNED(align, sizeof(long)));
+       /* must fit in the space reserved for the offset amount, which is at most
+        * 'align'. */
+       assert(align < (1 << (32 - KMALLOC_ALIGN_SHIFT)));
+       addr = kmalloc(size + align, flags);
+       if (!addr)
+               return 0;
+       if (ALIGNED(addr, align))
+               return addr;
+       retaddr = ROUNDUP(addr, align);
+       offset = retaddr - addr;
+       assert(offset < align);
+       /* we might not have room for a full tag.  we might have only 8 bytes.  but
+        * we'll at least have room for the flags part. */
+       tag_flags = (int*)(retaddr - sizeof(int));
+       *tag_flags = (offset << KMALLOC_ALIGN_SHIFT) | KMALLOC_TAG_UNALIGN;
+       return retaddr;
+}
+
+void *kzmalloc_align(size_t size, int flags, size_t align)
+{
+       void *v = kmalloc_align(size, flags, align);
+       if (!v)
+               return v;
+       memset(v, 0, size);
+       return v;
+}
+
+void *krealloc(void* buf, size_t size, int flags)
+{
+       struct kmalloc_tag *tag = (struct kmalloc_tag*)(buf -
+                                                       sizeof(struct kmalloc_tag));
        if (tag->my_cache->obj_size >= size)
                return buf;
        kfree(buf);
@@ -99,18 +134,27 @@ void *krealloc(void* buf, size_t size, int flags) {
 
 void kfree(void *addr)
 {
-       if(addr == NULL)
+       struct kmalloc_tag *tag;
+       int *tag_flags;
+       if (addr == NULL)
+               return;
+       tag_flags = (int*)(addr - sizeof(int));
+       if ((*tag_flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_UNALIGN) {
+               kfree(addr - (*tag_flags >> KMALLOC_ALIGN_SHIFT));
                return;
-       struct kmalloc_tag *tag = (struct kmalloc_tag*)(addr - KMALLOC_OFFSET);
+       }
+       tag = (struct kmalloc_tag*)(addr - sizeof(struct kmalloc_tag));
+       assert(tag_flags == &tag->flags);
        if (tag->canary != KMALLOC_CANARY){
-               printk("Canary is bogus: %08lx, expected %08lx\n", tag->canary, KMALLOC_CANARY);
+               printk("Canary is bogus: %08lx, expected %08lx\n", tag->canary,
+                      KMALLOC_CANARY);
                hexdump((void *)(addr-128), 256);
        }
        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));
+       if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_CACHE)
+               kmem_cache_free(tag->my_cache, tag);
+       else if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_PAGES) {
+               free_cont_pages(tag, LOG2_UP(tag->num_pages));
        } else 
                panic("[Italian Accent]: Che Cazzo! BO! Flag in kmalloc!!!");
 }
@@ -120,7 +164,7 @@ void kmalloc_canary_check(char *str)
        if (!debug_canary)
                return;
        struct kmalloc_tag *tag = (struct kmalloc_tag*)(debug_canary -
-                                                       KMALLOC_OFFSET);
+                                                       sizeof(struct kmalloc_tag));
        if (tag->canary != KMALLOC_CANARY)
                panic("\t\t KMALLOC CANARY CHECK FAILED %s\n", str);
 }
index 768244a..7894bf3 100644 (file)
@@ -752,7 +752,7 @@ void test_kmalloc(void)
        void *bufs[NUM_KMALLOC_CACHES + 1];     
        size_t size;
        for (int i = 0; i < NUM_KMALLOC_CACHES + 1; i++){
-               size = (KMALLOC_SMALLEST << i) - KMALLOC_OFFSET;
+               size = (KMALLOC_SMALLEST << i) - sizeof(struct kmalloc_tag);
                bufs[i] = kmalloc(size, 0);
                printk("Size %d, Addr = %p\n", size, bufs[i]);
        }