slab: Stop appending a uintptr_t to small objects
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 7 Nov 2016 00:20:13 +0000 (19:20 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 29 Nov 2016 16:27:40 +0000 (11:27 -0500)
Previously, when objects were in the slab layer (i.e., on a list in the
slab), they were constructed.  Because of that, we needed to append a
uinptr_t to small objects to form a linked list so as to not use the
constructed object.

Now that constructing happens above the slab layer, we can use the
memory of the object itself for the linked list.  Other than being
simpler, this saves some space and avoids fragmentation.  (consider a
256 byte item - we were adding 8 bytes, which would make it not pack
neatly into a page).

Note that small objects are all 'pro-touch', so we're allowed to use the
memory of the resource we're allocating.  This has always been true.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/include/slab.h
kern/src/slab.c

index ee661e8..e28e98f 100644 (file)
  * address of the next free item.  The slab structure is stored at the end of
  * the page.  There is only one page per slab.
  *
- * TODO: Note, that this is a minor pain in the ass, and worth thinking about
- * before implementing.  To keep the constructor's state valid, we can't just
- * overwrite things, so we need to add an extra 4-8 bytes per object for the
- * pointer, and then pass over that data when we return the actual object's
- * address.  This also might fuck with alignment.
- *
  * Be careful with source arenas and NOTOUCH.  If a cache's source arena is not
  * page-aligned memory, you need to set NOTOUCH.  Otherwise, for small objects,
  * a slab will be constructed that uses the source for a page of objects.
index c6f4c59..ad8d007 100644 (file)
@@ -254,10 +254,9 @@ static void *__kmem_alloc_from_slab(struct kmem_cache *cp, int flags)
        // have a partial now (a_slab), get an item, return item
        if (!__use_bufctls(cp)) {
                retval = a_slab->free_small_obj;
-               /* adding the size of the cache_obj to get to the pointer at end of the
-                * buffer pointing to the next free_small_obj */
-               a_slab->free_small_obj = *(uintptr_t**)(a_slab->free_small_obj +
-                                                       cp->obj_size);
+               /* the next free_small_obj address is stored at the beginning of the
+                * current free_small_obj. */
+               a_slab->free_small_obj = *(uintptr_t**)(a_slab->free_small_obj);
        } else {
                // rip the first bufctl out of the partial slab's buf list
                struct kmem_bufctl *a_bufctl = BSD_LIST_FIRST(&a_slab->bufctl_freelist);
@@ -298,9 +297,9 @@ static void __kmem_free_to_slab(struct kmem_cache *cp, void *buf)
                // find its slab
                a_slab = (struct kmem_slab*)(ROUNDDOWN((uintptr_t)buf, PGSIZE) +
                                             PGSIZE - sizeof(struct kmem_slab));
-               /* write location of next free small obj to the space at the end of the
-                * buffer, then list buf as the next free small obj */
-               *(uintptr_t**)(buf + cp->obj_size) = a_slab->free_small_obj;
+               /* write location of next free small obj to the space at the beginning
+                * of the buffer, then list buf as the next free small obj */
+               *(uintptr_t**)buf = a_slab->free_small_obj;
                a_slab->free_small_obj = buf;
        } else {
                /* Give the bufctl back to the parent slab */
@@ -352,21 +351,22 @@ static bool kmem_cache_grow(struct kmem_cache *cp)
                // the slab struct is stored at the end of the page
                a_slab = (struct kmem_slab*)(a_page + PGSIZE
                                             - sizeof(struct kmem_slab));
-               // Need to add room for the next free item pointer in the object buffer.
-               a_slab->obj_size = ROUNDUP(cp->obj_size + sizeof(uintptr_t), cp->align);
+               /* the 'next free item' pointer will be the first word of the obj.  we
+                * used to append a uintptr_t for that. */
+               a_slab->obj_size = ROUNDUP(cp->obj_size, cp->align);
                a_slab->num_busy_obj = 0;
                a_slab->num_total_obj = (PGSIZE - sizeof(struct kmem_slab)) /
                                        a_slab->obj_size;
                // TODO: consider staggering this IAW section 4.3
                a_slab->free_small_obj = a_page;
                /* Walk and create the free list, which is circular.  Each item stores
-                * the location of the next one at the end of the block. */
+                * the location of the next one at the beginning of the block. */
                void *buf = a_slab->free_small_obj;
                for (int i = 0; i < a_slab->num_total_obj - 1; i++) {
-                       *(uintptr_t**)(buf + cp->obj_size) = buf + a_slab->obj_size;
+                       *(uintptr_t**)buf = buf + a_slab->obj_size;
                        buf += a_slab->obj_size;
                }
-               *((uintptr_t**)(buf + cp->obj_size)) = NULL;
+               *((uintptr_t**)buf) = NULL;
        } else {
                void *buf;