perf: Treat the kernel like [kernel.kallsyms]
[akaros.git] / user / parlib / slab.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * Kevin Klues <klueska@cs.berkeley.edu>
5  * See LICENSE for details.
6  *
7  * Slab allocator, based on the SunOS 5.4 allocator paper.
8  *
9  * Note that we don't have a hash table for buf to bufctl for the large buffer
10  * objects, so we use the same style for small objects: store the pointer to the
11  * controlling bufctl at the top of the slab object.  Fix this with TODO (BUF).
12  *
13  * Ported directly from the kernel's slab allocator. */
14
15 #include <parlib/slab.h>
16 #include <stdio.h>
17 #include <parlib/assert.h>
18 #include <sys/mman.h>
19 #include <sys/param.h>
20
21 struct kmem_cache_list kmem_caches;
22 struct spin_pdr_lock kmem_caches_lock;
23
24 /* Backend/internal functions, defined later.  Grab the lock before calling
25  * these. */
26 static void kmem_cache_grow(struct kmem_cache *cp);
27
28 /* Cache of the kmem_cache objects, needed for bootstrapping */
29 struct kmem_cache kmem_cache_cache;
30 struct kmem_cache *kmem_slab_cache, *kmem_bufctl_cache;
31
32 static void __kmem_cache_create(struct kmem_cache *kc, const char *name,
33                                 size_t obj_size, int align, int flags,
34                                 void (*ctor)(void *, size_t),
35                                 void (*dtor)(void *, size_t))
36 {
37         assert(kc);
38         assert(align);
39         spin_pdr_init(&kc->cache_lock);
40         kc->name = name;
41         kc->obj_size = obj_size;
42         kc->align = align;
43         kc->flags = flags;
44         TAILQ_INIT(&kc->full_slab_list);
45         TAILQ_INIT(&kc->partial_slab_list);
46         TAILQ_INIT(&kc->empty_slab_list);
47         kc->ctor = ctor;
48         kc->dtor = dtor;
49         kc->nr_cur_alloc = 0;
50         
51         /* put in cache list based on it's size */
52         struct kmem_cache *i, *prev = NULL;
53         spin_pdr_lock(&kmem_caches_lock);
54         /* find the kmem_cache before us in the list.  yes, this is O(n). */
55         SLIST_FOREACH(i, &kmem_caches, link) {
56                 if (i->obj_size < kc->obj_size)
57                         prev = i;
58                 else
59                         break;
60         }
61         if (prev)
62                 SLIST_INSERT_AFTER(prev, kc, link);
63         else
64                 SLIST_INSERT_HEAD(&kmem_caches, kc, link);
65         spin_pdr_unlock(&kmem_caches_lock);
66 }
67
68 void kmem_cache_init(void)
69 {
70         spin_pdr_init(&kmem_caches_lock);
71         SLIST_INIT(&kmem_caches);
72         /* We need to call the __ version directly to bootstrap the global
73          * kmem_cache_cache. */
74         __kmem_cache_create(&kmem_cache_cache, "kmem_cache",
75                             sizeof(struct kmem_cache),
76                             __alignof__(struct kmem_cache), 0, NULL, NULL);
77         /* Build the slab and bufctl caches */
78         kmem_slab_cache = kmem_cache_alloc(&kmem_cache_cache, 0);
79         __kmem_cache_create(kmem_slab_cache, "kmem_slab", sizeof(struct kmem_slab),
80                             __alignof__(struct kmem_slab), 0, NULL, NULL); 
81         kmem_bufctl_cache = kmem_cache_alloc(&kmem_cache_cache, 0);
82         __kmem_cache_create(kmem_bufctl_cache, "kmem_bufctl",
83                             sizeof(struct kmem_bufctl),
84                             __alignof__(struct kmem_bufctl), 0, NULL, NULL); 
85 }
86
87 /* Cache management */
88 struct kmem_cache *kmem_cache_create(const char *name, size_t obj_size,
89                                      int align, int flags,
90                                      void (*ctor)(void *, size_t),
91                                      void (*dtor)(void *, size_t))
92 {
93         run_once(kmem_cache_init());
94         struct kmem_cache *kc = kmem_cache_alloc(&kmem_cache_cache, 0);
95         __kmem_cache_create(kc, name, obj_size, align, flags, ctor, dtor);
96         return kc;
97 }
98
99 static void kmem_slab_destroy(struct kmem_cache *cp, struct kmem_slab *a_slab)
100 {
101         if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
102                 /* Deconstruct all the objects, if necessary */
103                 if (cp->dtor) {
104                         void *buf = a_slab->free_small_obj;
105                         for (int i = 0; i < a_slab->num_total_obj; i++) {
106                                 cp->dtor(buf, cp->obj_size);
107                                 buf += a_slab->obj_size;
108                         }
109                 }
110                 munmap((void*)ROUNDDOWN((uintptr_t)a_slab, PGSIZE), PGSIZE);
111         } else {
112                 struct kmem_bufctl *i;
113                 void *page_start = (void*)-1;
114                 /* compute how many pages are allocated, same as in grow */
115                 size_t nr_pgs = ROUNDUP(NUM_BUF_PER_SLAB * a_slab->obj_size, PGSIZE) /
116                                         PGSIZE;
117                 TAILQ_FOREACH(i, &a_slab->bufctl_freelist, link) {
118                         // Track the lowest buffer address, which is the start of the buffer
119                         page_start = MIN(page_start, i->buf_addr);
120                         /* Deconstruct all the objects, if necessary */
121                         if (cp->dtor) // TODO: (BUF)
122                                 cp->dtor(i->buf_addr, cp->obj_size);
123                         kmem_cache_free(kmem_bufctl_cache, i);
124                 }
125                 // free the pages for the slab's buffer
126                 munmap(page_start, nr_pgs * PGSIZE);
127                 // free the slab object
128                 kmem_cache_free(kmem_slab_cache, a_slab);
129         }
130 }
131
132 /* Once you call destroy, never use this cache again... o/w there may be weird
133  * races, and other serious issues.  */
134 void kmem_cache_destroy(struct kmem_cache *cp)
135 {
136         struct kmem_slab *a_slab, *next;
137
138         spin_pdr_lock(&cp->cache_lock);
139         assert(TAILQ_EMPTY(&cp->full_slab_list));
140         assert(TAILQ_EMPTY(&cp->partial_slab_list));
141         /* Clean out the empty list.  We can't use a regular FOREACH here, since the
142          * link element is stored in the slab struct, which is stored on the page
143          * that we are freeing. */
144         a_slab = TAILQ_FIRST(&cp->empty_slab_list);
145         while (a_slab) {
146                 next = TAILQ_NEXT(a_slab, link);
147                 kmem_slab_destroy(cp, a_slab);
148                 a_slab = next;
149         }
150         spin_pdr_lock(&kmem_caches_lock);
151         SLIST_REMOVE(&kmem_caches, cp, kmem_cache, link);
152         spin_pdr_unlock(&kmem_caches_lock);
153         kmem_cache_free(&kmem_cache_cache, cp); 
154         spin_pdr_unlock(&cp->cache_lock);
155 }
156
157 /* Front end: clients of caches use these */
158 void *kmem_cache_alloc(struct kmem_cache *cp, int flags)
159 {
160         void *retval = NULL;
161         spin_pdr_lock(&cp->cache_lock);
162         // look at partial list
163         struct kmem_slab *a_slab = TAILQ_FIRST(&cp->partial_slab_list);
164         //      if none, go to empty list and get an empty and make it partial
165         if (!a_slab) {
166                 if (TAILQ_EMPTY(&cp->empty_slab_list))
167                         // TODO: think about non-sleeping flags
168                         kmem_cache_grow(cp);
169                 // move to partial list
170                 a_slab = TAILQ_FIRST(&cp->empty_slab_list);
171                 TAILQ_REMOVE(&cp->empty_slab_list, a_slab, link);
172                 TAILQ_INSERT_HEAD(&cp->partial_slab_list, a_slab, link);
173         } 
174         // have a partial now (a_slab), get an item, return item
175         if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
176                 retval = a_slab->free_small_obj;
177                 /* adding the size of the cache_obj to get to the pointer at end of the
178                  * buffer pointing to the next free_small_obj */
179                 a_slab->free_small_obj = *(uintptr_t**)(a_slab->free_small_obj +
180                                                         cp->obj_size);
181         } else {
182                 // rip the first bufctl out of the partial slab's buf list
183                 struct kmem_bufctl *a_bufctl = TAILQ_FIRST(&a_slab->bufctl_freelist);
184                 TAILQ_REMOVE(&a_slab->bufctl_freelist, a_bufctl, link);
185                 retval = a_bufctl->buf_addr;
186         }
187         a_slab->num_busy_obj++;
188         // Check if we are full, if so, move to the full list
189         if (a_slab->num_busy_obj == a_slab->num_total_obj) {
190                 TAILQ_REMOVE(&cp->partial_slab_list, a_slab, link);
191                 TAILQ_INSERT_HEAD(&cp->full_slab_list, a_slab, link);
192         }
193         cp->nr_cur_alloc++;
194         spin_pdr_unlock(&cp->cache_lock);
195         return retval;
196 }
197
198 static inline struct kmem_bufctl *buf2bufctl(void *buf, size_t offset)
199 {
200         // TODO: hash table for back reference (BUF)
201         return *((struct kmem_bufctl**)(buf + offset));
202 }
203
204 void kmem_cache_free(struct kmem_cache *cp, void *buf)
205 {
206         struct kmem_slab *a_slab;
207         struct kmem_bufctl *a_bufctl;
208
209         spin_pdr_lock(&cp->cache_lock);
210         if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
211                 // find its slab
212                 a_slab = (struct kmem_slab*)(ROUNDDOWN((uintptr_t)buf, PGSIZE) +
213                                              PGSIZE - sizeof(struct kmem_slab));
214                 /* write location of next free small obj to the space at the end of the
215                  * buffer, then list buf as the next free small obj */
216                 *(uintptr_t**)(buf + cp->obj_size) = a_slab->free_small_obj;
217                 a_slab->free_small_obj = buf;
218         } else {
219                 /* Give the bufctl back to the parent slab */
220                 // TODO: (BUF) change the interface to not take an offset
221                 a_bufctl = buf2bufctl(buf, cp->obj_size);
222                 a_slab = a_bufctl->my_slab;
223                 TAILQ_INSERT_HEAD(&a_slab->bufctl_freelist, a_bufctl, link);
224         }
225         a_slab->num_busy_obj--;
226         cp->nr_cur_alloc--;
227         // if it was full, move it to partial
228         if (a_slab->num_busy_obj + 1 == a_slab->num_total_obj) {
229                 TAILQ_REMOVE(&cp->full_slab_list, a_slab, link);
230                 TAILQ_INSERT_HEAD(&cp->partial_slab_list, a_slab, link);
231         } else if (!a_slab->num_busy_obj) {
232                 // if there are none, move to from partial to empty
233                 TAILQ_REMOVE(&cp->partial_slab_list, a_slab, link);
234                 TAILQ_INSERT_HEAD(&cp->empty_slab_list, a_slab, link);
235         }
236         spin_pdr_unlock(&cp->cache_lock);
237 }
238
239 /* Back end: internal functions */
240 /* When this returns, the cache has at least one slab in the empty list.  If
241  * page_alloc fails, there are some serious issues.  This only grows by one slab
242  * at a time.
243  *
244  * Grab the cache lock before calling this.
245  *
246  * TODO: think about page colouring issues with kernel memory allocation. */
247 static void kmem_cache_grow(struct kmem_cache *cp)
248 {
249         struct kmem_slab *a_slab;
250         struct kmem_bufctl *a_bufctl;
251         void *a_page;
252         if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
253                 // Just get a single page for small slabs
254                 a_page = mmap(0, PGSIZE, PROT_READ | PROT_WRITE,
255                               MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
256                 assert(a_page != MAP_FAILED);
257                 // the slab struct is stored at the end of the page
258                 a_slab = (struct kmem_slab*)(a_page + PGSIZE -
259                                              sizeof(struct kmem_slab));
260                 // Need to add room for the next free item pointer in the object buffer.
261                 a_slab->obj_size = ROUNDUP(cp->obj_size + sizeof(uintptr_t), cp->align);
262                 a_slab->num_busy_obj = 0;
263                 a_slab->num_total_obj = (PGSIZE - sizeof(struct kmem_slab)) /
264                                         a_slab->obj_size;
265                 // TODO: consider staggering this IAW section 4.3
266                 a_slab->free_small_obj = a_page;
267                 /* Walk and create the free list, which is circular.  Each item stores
268                  * the location of the next one at the end of the block. */
269                 void *buf = a_slab->free_small_obj;
270                 for (int i = 0; i < a_slab->num_total_obj - 1; i++) {
271                         // Initialize the object, if necessary
272                         if (cp->ctor)
273                                 cp->ctor(buf, cp->obj_size);
274                         *(uintptr_t**)(buf + cp->obj_size) = buf + a_slab->obj_size;
275                         buf += a_slab->obj_size;
276                 }
277                 *((uintptr_t**)(buf + cp->obj_size)) = NULL;
278         } else {
279                 a_slab = kmem_cache_alloc(kmem_slab_cache, 0);
280                 // TODO: hash table for back reference (BUF)
281                 a_slab->obj_size = ROUNDUP(cp->obj_size + sizeof(uintptr_t), cp->align);
282                 /* Need at least nr_pgs to hold NUM_BUF objects.  Note we don't round up
283                  * to the next higher order (power of 2) number of pages, like we do in
284                  * the kernel. */
285                 size_t nr_pgs = ROUNDUP(NUM_BUF_PER_SLAB * a_slab->obj_size, PGSIZE) /
286                                          PGSIZE;
287                 void *buf = mmap(0, nr_pgs * PGSIZE, PROT_READ | PROT_WRITE,
288                                  MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
289                 assert(buf != MAP_FAILED);
290                 a_slab->num_busy_obj = 0;
291                 a_slab->num_total_obj = nr_pgs * PGSIZE / a_slab->obj_size;
292                 TAILQ_INIT(&a_slab->bufctl_freelist);
293                 /* for each buffer, set up a bufctl and point to the buffer */
294                 for (int i = 0; i < a_slab->num_total_obj; i++) {
295                         // Initialize the object, if necessary
296                         if (cp->ctor)
297                                 cp->ctor(buf, cp->obj_size);
298                         a_bufctl = kmem_cache_alloc(kmem_bufctl_cache, 0);      
299                         TAILQ_INSERT_HEAD(&a_slab->bufctl_freelist, a_bufctl, link);
300                         a_bufctl->buf_addr = buf;
301                         a_bufctl->my_slab = a_slab;
302                         // TODO: (BUF) write the bufctl reference at the bottom of the buffer.
303                         *(struct kmem_bufctl**)(buf + cp->obj_size) = a_bufctl;
304                         buf += a_slab->obj_size;
305                 }
306         }
307         // add a_slab to the empty_list
308         TAILQ_INSERT_HEAD(&cp->empty_slab_list, a_slab, link);
309 }
310
311 /* This deallocs every slab from the empty list.  TODO: think a bit more about
312  * this.  We can do things like not free all of the empty lists to prevent
313  * thrashing.  See 3.4 in the paper. */
314 void kmem_cache_reap(struct kmem_cache *cp)
315 {
316         struct kmem_slab *a_slab, *next;
317         
318         // Destroy all empty slabs.  Refer to the notes about the while loop
319         spin_pdr_lock(&cp->cache_lock);
320         a_slab = TAILQ_FIRST(&cp->empty_slab_list);
321         while (a_slab) {
322                 next = TAILQ_NEXT(a_slab, link);
323                 kmem_slab_destroy(cp, a_slab);
324                 a_slab = next;
325         }
326         spin_pdr_unlock(&cp->cache_lock);
327 }
328
329 void print_kmem_cache(struct kmem_cache *cp)
330 {
331         spin_pdr_lock(&cp->cache_lock);
332         printf("\nPrinting kmem_cache:\n---------------------\n");
333         printf("Name: %s\n", cp->name);
334         printf("Objsize: %d\n", cp->obj_size);
335         printf("Align: %d\n", cp->align);
336         printf("Flags: 0x%08x\n", cp->flags);
337         printf("Constructor: 0x%08x\n", cp->ctor);
338         printf("Destructor: 0x%08x\n", cp->dtor);
339         printf("Slab Full: 0x%08x\n", cp->full_slab_list);
340         printf("Slab Partial: 0x%08x\n", cp->partial_slab_list);
341         printf("Slab Empty: 0x%08x\n", cp->empty_slab_list);
342         printf("Current Allocations: %d\n", cp->nr_cur_alloc);
343         spin_pdr_unlock(&cp->cache_lock);
344 }
345
346 void print_kmem_slab(struct kmem_slab *slab)
347 {
348         printf("\nPrinting kmem_slab:\n---------------------\n");
349         printf("Objsize: %d (%p)\n", slab->obj_size, slab->obj_size);
350         printf("NumBusy: %d\n", slab->num_busy_obj);
351         printf("Num_total: %d\n", slab->num_total_obj);
352         if (slab->obj_size + sizeof(uintptr_t) < SLAB_LARGE_CUTOFF) {
353                 printf("Free Small obj: 0x%08x\n", slab->free_small_obj);
354                 void *buf = slab->free_small_obj;
355                 for (int i = 0; i < slab->num_total_obj; i++) {
356                         printf("Addr of buf: 0x%08x, Addr of next: 0x%08x\n", buf,
357                                *((uintptr_t**)buf));
358                         buf += slab->obj_size;
359                 }
360         } else {
361                 printf("This is a big slab!\n");
362         }
363 }
364