Remove the old console input code; use qio
[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 #include <ros/common.h>
9 #include <error.h>
10 #include <pmap.h>
11 #include <kmalloc.h>
12 #include <stdio.h>
13 #include <slab.h>
14 #include <assert.h>
15
16 #define kmallocdebug(args...)  //printk(args)
17
18 //List of physical pages used by kmalloc
19 static spinlock_t pages_list_lock = SPINLOCK_INITIALIZER;
20 static page_list_t pages_list;
21
22 struct kmem_cache *kmalloc_caches[NUM_KMALLOC_CACHES];
23
24 static void __kfree_release(struct kref *kref);
25
26 void kmalloc_init(void)
27 {
28         /* we want at least a 16 byte alignment of the tag so that the bufs kmalloc
29          * returns are 16 byte aligned.  we used to check the actual size == 16,
30          * since we adjusted the KMALLOC_SMALLEST based on that. */
31         static_assert(ALIGNED(sizeof(struct kmalloc_tag), 16));
32         /* build caches of common sizes.  this size will later include the tag and
33          * the actual returned buffer. */
34         size_t ksize = KMALLOC_SMALLEST;
35         for (int i = 0; i < NUM_KMALLOC_CACHES; i++) {
36                 kmalloc_caches[i] = kmem_cache_create("kmalloc_cache", ksize,
37                                                       KMALLOC_ALIGNMENT, 0, 0, 0);
38                 ksize <<= 1;
39         }
40 }
41
42 void *kmalloc(size_t size, int flags)
43 {
44         // reserve space for bookkeeping and preserve alignment
45         size_t ksize = size + sizeof(struct kmalloc_tag);
46         void *buf;
47         int cache_id;
48         // determine cache to pull from
49         if (ksize <= KMALLOC_SMALLEST)
50                 cache_id = 0;
51         else
52                 cache_id = LOG2_UP(ksize) - LOG2_UP(KMALLOC_SMALLEST);
53         // if we don't have a cache to handle it, alloc cont pages
54         if (cache_id >= NUM_KMALLOC_CACHES) {
55                 size_t num_pgs = ROUNDUP(size + sizeof(struct kmalloc_tag), PGSIZE) /
56                                            PGSIZE;
57                 buf = get_cont_pages(LOG2_UP(num_pgs), flags);
58                 if (!buf)
59                         panic("Kmalloc failed!  Handle me!");
60                 // fill in the kmalloc tag
61                 struct kmalloc_tag *tag = buf;
62                 tag->flags = KMALLOC_TAG_PAGES;
63                 tag->num_pages = num_pgs;
64                 tag->canary = KMALLOC_CANARY;
65                 kref_init(&tag->kref, __kfree_release, 1);
66                 return buf + sizeof(struct kmalloc_tag);
67         }
68         // else, alloc from the appropriate cache
69         buf = kmem_cache_alloc(kmalloc_caches[cache_id], flags);
70         if (!buf)
71                 panic("Kmalloc failed!  Handle me!");
72         // store a pointer to the buffers kmem_cache in it's bookkeeping space
73         struct kmalloc_tag *tag = buf;
74         tag->flags = KMALLOC_TAG_CACHE;
75         tag->my_cache = kmalloc_caches[cache_id];
76         tag->canary = KMALLOC_CANARY;
77         kref_init(&tag->kref, __kfree_release, 1);
78         return buf + sizeof(struct kmalloc_tag);
79 }
80
81 void *kzmalloc(size_t size, int flags)
82 {
83         void *v = kmalloc(size, flags);
84         if (!v)
85                 return v;
86         memset(v, 0, size);
87         return v;
88 }
89
90 void *kmalloc_align(size_t size, int flags, size_t align)
91 {
92         void *addr, *retaddr;
93         int *tag_flags, offset;
94         /* alignment requests must be a multiple of long, even though we only need
95          * int in the current code. */
96         assert(ALIGNED(align, sizeof(long)));
97         /* must fit in the space reserved for the offset amount, which is at most
98          * 'align'. */
99         assert(align < (1 << (32 - KMALLOC_ALIGN_SHIFT)));
100         assert(IS_PWR2(align));
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 static struct kmalloc_tag *__get_km_tag(void *buf)
126 {
127         struct kmalloc_tag *tag = (struct kmalloc_tag*)(buf -
128                                                     sizeof(struct kmalloc_tag));
129         if (tag->canary != KMALLOC_CANARY){
130                 printk("__get_km_tag bad canary: %08lx@%p, buf %p, expected %08lx\n",
131                        tag->canary, &tag->canary, buf, KMALLOC_CANARY);
132                 hexdump((void *)(buf - sizeof(struct kmalloc_tag)), 256);
133                 panic("Bad canary");
134         }
135         return tag;
136 }
137
138 /* If we kmalloc_aligned, the buf we got back (and are now trying to perform
139  * some operation on) might not be the original, underlying, unaligned buf.
140  *
141  * This returns the underlying, unaligned buf, or 0 if the buf was not realigned
142  * in the first place. */
143 static void *__get_unaligned_orig_buf(void *buf)
144 {
145         int *tag_flags = (int*)(buf - sizeof(int));
146         if ((*tag_flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_UNALIGN)
147                 return (buf - (*tag_flags >> KMALLOC_ALIGN_SHIFT));
148         else
149                 return 0;
150 }
151
152 void *krealloc(void* buf, size_t size, int flags)
153 {
154         void *nbuf;
155         size_t osize = 0;
156         struct kmalloc_tag *tag;
157
158         if (buf){
159                 if (__get_unaligned_orig_buf(buf))
160                         panic("krealloc of a kmalloc_align not supported");
161                 tag = __get_km_tag(buf);
162                 /* whatever we got from either a slab or the page allocator is meant for
163                  * both the buf+size as well as the kmalloc tag */
164                 if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_CACHE) {
165                         osize = tag->my_cache->obj_size - sizeof(struct kmalloc_tag);
166                 } else if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_PAGES) {
167                         osize = LOG2_UP(tag->num_pages) * PGSIZE
168                                 - sizeof(struct kmalloc_tag);
169                 } else {
170                         panic("Probably a bad tag, flags %p\n", tag->flags);
171                 }
172                 if (osize >= size)
173                         return buf;
174         }
175
176         nbuf = kmalloc(size, flags);
177
178         /* would be more interesting to user error(...) here. */
179         /* but in any event, NEVER destroy buf! */
180         if (! nbuf)
181                 return NULL;
182
183         if (osize)
184                 memmove(nbuf, buf, osize);
185
186         if (buf)
187                 kfree(buf);
188
189         return nbuf;
190 }
191
192 /* Grabs a reference on a buffer.  Release with kfree().
193  *
194  * Note that a krealloc on a buffer with ref > 1 that needs a new, underlying
195  * buffer will result in two buffers existing.  In this case, the krealloc is a
196  * kmalloc and a kfree, but that kfree does not completely free since the
197  * original ref > 1. */
198 void kmalloc_incref(void *buf)
199 {
200         void *orig_buf = __get_unaligned_orig_buf(buf);
201         buf = orig_buf ? orig_buf : buf;
202         /* if we want a smaller tag, we can extract the code from kref and manually
203          * set the release method in kfree. */
204         kref_get(&__get_km_tag(buf)->kref, 1);
205 }
206
207 int kmalloc_refcnt(void *buf)
208 {
209         void *orig_buf = __get_unaligned_orig_buf(buf);
210         buf = orig_buf ? orig_buf : buf;
211         return kref_refcnt(&__get_km_tag(buf)->kref);
212 }
213
214 static void __kfree_release(struct kref *kref)
215 {
216         struct kmalloc_tag *tag = container_of(kref, struct kmalloc_tag, kref);
217         if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_CACHE)
218                 kmem_cache_free(tag->my_cache, tag);
219         else if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_PAGES)
220                 free_cont_pages(tag, LOG2_UP(tag->num_pages));
221         else
222                 panic("Bad flag 0x%x in %s", tag->flags, __FUNCTION__);
223 }
224
225 void kfree(void *buf)
226 {
227         void *orig_buf;
228         if (buf == NULL)
229                 return;
230         orig_buf = __get_unaligned_orig_buf(buf);
231         buf = orig_buf ? orig_buf : buf;
232         kref_put(&__get_km_tag(buf)->kref);
233 }
234
235 void kmalloc_canary_check(char *str)
236 {
237         if (!debug_canary)
238                 return;
239         struct kmalloc_tag *tag = (struct kmalloc_tag*)(debug_canary -
240                                                         sizeof(struct kmalloc_tag));
241         if (tag->canary != KMALLOC_CANARY)
242                 panic("\t\t KMALLOC CANARY CHECK FAILED %s\n", str);
243 }
244
245 struct sized_alloc *sized_kzmalloc(size_t size, int flags)
246 {
247         struct sized_alloc *sza;
248
249         sza = kzmalloc(sizeof(struct sized_alloc) + size, flags);
250         if (!sza)
251                 return NULL;
252         sza->buf = sza + 1;
253         sza->size = size;
254         return sza;
255 }