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