892360636b8cbb8e22b085c98d31e1a15f614a38
[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         /* we want at least a 16 byte alignment of the tag so that the bufs kmalloc
36          * returns are 16 byte aligned.  we used to check the actual size == 16,
37          * since we adjusted the KMALLOC_SMALLEST based on that. */
38         static_assert(ALIGNED(sizeof(struct kmalloc_tag), 16));
39         /* build caches of common sizes.  this size will later include the tag and
40          * the actual returned buffer. */
41         size_t ksize = KMALLOC_SMALLEST;
42         for (int i = 0; i < NUM_KMALLOC_CACHES; i++) {
43                 kmalloc_caches[i] = kmem_cache_create("kmalloc_cache", ksize,
44                                                       KMALLOC_ALIGNMENT, 0, 0, 0);
45                 ksize <<= 1;
46         }
47 }
48
49 void *kmalloc(size_t size, int flags) 
50 {
51         // reserve space for bookkeeping and preserve alignment
52         size_t ksize = size + sizeof(struct kmalloc_tag);
53         void *buf;
54         int cache_id;
55         // determine cache to pull from
56         if (ksize <= KMALLOC_SMALLEST)
57                 cache_id = 0;
58         else
59                 cache_id = LOG2_UP(ksize) - LOG2_UP(KMALLOC_SMALLEST);
60         // if we don't have a cache to handle it, alloc cont pages
61         if (cache_id >= NUM_KMALLOC_CACHES) {
62                 size_t num_pgs = ROUNDUP(size + sizeof(struct kmalloc_tag), PGSIZE) /
63                                            PGSIZE;
64                 buf = get_cont_pages(LOG2_UP(num_pgs), flags);
65                 if (!buf)
66                         panic("Kmalloc failed!  Handle me!");
67                 // fill in the kmalloc tag
68                 struct kmalloc_tag *tag = buf;
69                 tag->flags = KMALLOC_TAG_PAGES;
70                 tag->num_pages = num_pgs;
71                 tag->canary = KMALLOC_CANARY;
72                 return buf + sizeof(struct kmalloc_tag);
73         }
74         // else, alloc from the appropriate cache
75         buf = kmem_cache_alloc(kmalloc_caches[cache_id], flags);
76         if (!buf)
77                 panic("Kmalloc failed!  Handle me!");
78         // store a pointer to the buffers kmem_cache in it's bookkeeping space
79         struct kmalloc_tag *tag = buf;
80         tag->flags = KMALLOC_TAG_CACHE;
81         tag->my_cache = kmalloc_caches[cache_id];
82         tag->canary = KMALLOC_CANARY;
83         return buf + sizeof(struct kmalloc_tag);
84 }
85
86 void *kzmalloc(size_t size, int flags) 
87 {
88         void *v = kmalloc(size, flags);
89         if (!v)
90                 return v;
91         memset(v, 0, size);
92         return v;
93 }
94
95 void *kmalloc_align(size_t size, int flags, size_t align)
96 {
97         void *addr, *retaddr;
98         int *tag_flags, offset;
99         /* alignment requests must be a multiple of long, even though we only need
100          * int in the current code. */
101         assert(ALIGNED(align, sizeof(long)));
102         /* must fit in the space reserved for the offset amount, which is at most
103          * 'align'. */
104         assert(align < (1 << (32 - KMALLOC_ALIGN_SHIFT)));
105         addr = kmalloc(size + align, flags);
106         if (!addr)
107                 return 0;
108         if (ALIGNED(addr, align))
109                 return addr;
110         retaddr = ROUNDUP(addr, align);
111         offset = retaddr - addr;
112         assert(offset < align);
113         /* we might not have room for a full tag.  we might have only 8 bytes.  but
114          * we'll at least have room for the flags part. */
115         tag_flags = (int*)(retaddr - sizeof(int));
116         *tag_flags = (offset << KMALLOC_ALIGN_SHIFT) | KMALLOC_TAG_UNALIGN;
117         return retaddr;
118 }
119
120 void *kzmalloc_align(size_t size, int flags, size_t align)
121 {
122         void *v = kmalloc_align(size, flags, align);
123         if (!v)
124                 return v;
125         memset(v, 0, size);
126         return v;
127 }
128
129 static struct kmalloc_tag *__get_km_tag(void *buf)
130 {
131         struct kmalloc_tag *tag = (struct kmalloc_tag*)(buf -
132                                                     sizeof(struct kmalloc_tag));
133         if (tag->canary != KMALLOC_CANARY){
134                 printk("__get_km_tag bad canary: %08lx, expected %08lx\n", tag->canary,
135                        KMALLOC_CANARY);
136                 hexdump((void *)(buf - sizeof(struct kmalloc_tag)), 256);
137         }
138         return tag;
139 }
140
141 /* If we kmalloc_aligned, the buf we got back (and are now trying to perform
142  * some operation on) might not be the original, underlying, unaligned buf.
143  *
144  * This returns the underlying, unaligned buf, or 0 if the buf was not realigned
145  * in the first place. */
146 static void *__get_unaligned_orig_buf(void *buf)
147 {
148         int *tag_flags = (int*)(buf - sizeof(int));
149         if ((*tag_flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_UNALIGN)
150                 return (buf - (*tag_flags >> KMALLOC_ALIGN_SHIFT));
151         else
152                 return 0;
153 }
154
155 void *krealloc(void* buf, size_t size, int flags)
156 {
157         void *nbuf;
158         size_t osize = 0;
159         struct kmalloc_tag *tag;
160
161         if (buf){
162                 if (__get_unaligned_orig_buf(buf))
163                         panic("krealloc of a kmalloc_align not supported");
164                 tag = __get_km_tag(buf);
165                 /* whatever we got from either a slab or the page allocator is meant for
166                  * both the buf+size as well as the kmalloc tag */
167                 if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_CACHE) {
168                         osize = tag->my_cache->obj_size - sizeof(struct kmalloc_tag);
169                 } else if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_PAGES) {
170                         osize = LOG2_UP(tag->num_pages) * PGSIZE
171                                 - sizeof(struct kmalloc_tag);
172                 } else {
173                         panic("Probably a bad tag, flags %p\n", tag->flags);
174                 }
175                 if (osize >= size)
176                         return buf;
177         }
178
179         nbuf = kmalloc(size, flags);
180
181         /* would be more interesting to user error(...) here. */
182         /* but in any event, NEVER destroy buf! */
183         if (! nbuf)
184                 return NULL;
185
186         if (osize)
187                 memmove(nbuf, buf, osize);
188
189         if (buf)
190                 kfree(buf);
191
192         return nbuf;
193 }
194
195 void kfree(void *addr)
196 {
197         struct kmalloc_tag *tag;
198         void *orig_buf;
199         if (addr == NULL)
200                 return;
201         if ((orig_buf = __get_unaligned_orig_buf(addr))) {
202                 kfree(orig_buf);
203                 return;
204         }
205         tag = __get_km_tag(addr);
206         if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_CACHE)
207                 kmem_cache_free(tag->my_cache, tag);
208         else if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_PAGES) {
209                 free_cont_pages(tag, LOG2_UP(tag->num_pages));
210         } else 
211                 panic("[Italian Accent]: Che Cazzo! BO! Flag in kmalloc!!!");
212 }
213
214 void kmalloc_canary_check(char *str)
215 {
216         if (!debug_canary)
217                 return;
218         struct kmalloc_tag *tag = (struct kmalloc_tag*)(debug_canary -
219                                                         sizeof(struct kmalloc_tag));
220         if (tag->canary != KMALLOC_CANARY)
221                 panic("\t\t KMALLOC CANARY CHECK FAILED %s\n", str);
222 }