kmalloc_align()
[akaros.git] / kern / src / kmalloc.c
1 /* Copyright (c) 2009 The Regents of the University of California. 
2  * See the COPYRIGHT files at the top of this source tree for full 
3  * license information.
4  * 
5  * Barret Rhoden <brho@cs.berkeley.edu>
6  * Kevin Klues <klueska@cs.berkeley.edu>    
7  */
8
9 #ifdef __SHARC__
10 #pragma nosharc
11 #define SINIT(x) x
12 #endif
13
14 #ifdef __DEPUTY__
15 #pragma nodeputy
16 #endif
17
18 #include <ros/common.h>
19 #include <error.h>
20 #include <pmap.h>
21 #include <kmalloc.h>
22 #include <stdio.h>
23 #include <slab.h>
24 #include <assert.h>
25
26 #define kmallocdebug(args...)  //printk(args)
27
28 //List of physical pages used by kmalloc
29 static spinlock_t pages_list_lock = SPINLOCK_INITIALIZER;
30 static page_list_t LCKD(&pages_list_lock)pages_list;
31
32 struct kmem_cache *kmalloc_caches[NUM_KMALLOC_CACHES];
33 void kmalloc_init(void)
34 {
35         static_assert(sizeof(struct kmalloc_tag) == 16);
36         // build caches of common sizes
37         size_t ksize = KMALLOC_SMALLEST;
38         for (int i = 0; i < NUM_KMALLOC_CACHES; i++) {
39                 kmalloc_caches[i] = kmem_cache_create("kmalloc_cache", ksize,
40                                                       KMALLOC_ALIGNMENT, 0, 0, 0);
41                 ksize <<= 1;
42         }
43 }
44
45 void *kmalloc(size_t size, int flags) 
46 {
47         // reserve space for bookkeeping and preserve alignment
48         size_t ksize = size + sizeof(struct kmalloc_tag);
49         void *buf;
50         int cache_id;
51         // determine cache to pull from
52         if (ksize <= KMALLOC_SMALLEST)
53                 cache_id = 0;
54         else
55                 cache_id = LOG2_UP(ksize) - LOG2_UP(KMALLOC_SMALLEST);
56         // if we don't have a cache to handle it, alloc cont pages
57         if (cache_id >= NUM_KMALLOC_CACHES) {
58                 size_t num_pgs = ROUNDUP(size + sizeof(struct kmalloc_tag), PGSIZE) /
59                                            PGSIZE;
60                 buf = get_cont_pages(LOG2_UP(num_pgs), flags);
61                 if (!buf)
62                         panic("Kmalloc failed!  Handle me!");
63                 // fill in the kmalloc tag
64                 struct kmalloc_tag *tag = buf;
65                 tag->flags = KMALLOC_TAG_PAGES;
66                 tag->num_pages = num_pgs;
67                 tag->canary = KMALLOC_CANARY;
68                 return buf + sizeof(struct kmalloc_tag);
69         }
70         // else, alloc from the appropriate cache
71         buf = kmem_cache_alloc(kmalloc_caches[cache_id], flags);
72         if (!buf)
73                 panic("Kmalloc failed!  Handle me!");
74         // store a pointer to the buffers kmem_cache in it's bookkeeping space
75         struct kmalloc_tag *tag = buf;
76         tag->flags = KMALLOC_TAG_CACHE;
77         tag->my_cache = kmalloc_caches[cache_id];
78         tag->canary = KMALLOC_CANARY;
79         return buf + sizeof(struct kmalloc_tag);
80 }
81
82 void *kzmalloc(size_t size, int flags) 
83 {
84         void *v = kmalloc(size, flags);
85         if (! v)
86                 return v;
87         memset(v, 0, size);
88         return v;
89 }
90
91 void *kmalloc_align(size_t size, int flags, size_t align)
92 {
93         void *addr, *retaddr;
94         int *tag_flags, offset;
95         /* alignment requests must be a multiple of long, even though we only need
96          * int in the current code. */
97         assert(ALIGNED(align, sizeof(long)));
98         /* must fit in the space reserved for the offset amount, which is at most
99          * 'align'. */
100         assert(align < (1 << (32 - KMALLOC_ALIGN_SHIFT)));
101         addr = kmalloc(size + align, flags);
102         if (!addr)
103                 return 0;
104         if (ALIGNED(addr, align))
105                 return addr;
106         retaddr = ROUNDUP(addr, align);
107         offset = retaddr - addr;
108         assert(offset < align);
109         /* we might not have room for a full tag.  we might have only 8 bytes.  but
110          * we'll at least have room for the flags part. */
111         tag_flags = (int*)(retaddr - sizeof(int));
112         *tag_flags = (offset << KMALLOC_ALIGN_SHIFT) | KMALLOC_TAG_UNALIGN;
113         return retaddr;
114 }
115
116 void *kzmalloc_align(size_t size, int flags, size_t align)
117 {
118         void *v = kmalloc_align(size, flags, align);
119         if (!v)
120                 return v;
121         memset(v, 0, size);
122         return v;
123 }
124
125 void *krealloc(void* buf, size_t size, int flags)
126 {
127         struct kmalloc_tag *tag = (struct kmalloc_tag*)(buf -
128                                                         sizeof(struct kmalloc_tag));
129         if (tag->my_cache->obj_size >= size)
130                 return buf;
131         kfree(buf);
132         return kmalloc(size, flags);
133 }
134
135 void kfree(void *addr)
136 {
137         struct kmalloc_tag *tag;
138         int *tag_flags;
139         if (addr == NULL)
140                 return;
141         tag_flags = (int*)(addr - sizeof(int));
142         if ((*tag_flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_UNALIGN) {
143                 kfree(addr - (*tag_flags >> KMALLOC_ALIGN_SHIFT));
144                 return;
145         }
146         tag = (struct kmalloc_tag*)(addr - sizeof(struct kmalloc_tag));
147         assert(tag_flags == &tag->flags);
148         if (tag->canary != KMALLOC_CANARY){
149                 printk("Canary is bogus: %08lx, expected %08lx\n", tag->canary,
150                        KMALLOC_CANARY);
151                 hexdump((void *)(addr-128), 256);
152         }
153         assert(tag->canary == KMALLOC_CANARY);
154         if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_CACHE)
155                 kmem_cache_free(tag->my_cache, tag);
156         else if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_PAGES) {
157                 free_cont_pages(tag, LOG2_UP(tag->num_pages));
158         } else 
159                 panic("[Italian Accent]: Che Cazzo! BO! Flag in kmalloc!!!");
160 }
161
162 void kmalloc_canary_check(char *str)
163 {
164         if (!debug_canary)
165                 return;
166         struct kmalloc_tag *tag = (struct kmalloc_tag*)(debug_canary -
167                                                         sizeof(struct kmalloc_tag));
168         if (tag->canary != KMALLOC_CANARY)
169                 panic("\t\t KMALLOC CANARY CHECK FAILED %s\n", str);
170 }