slab: Support 'no-touch' caches
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 2 Nov 2016 16:54:44 +0000 (12:54 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 29 Nov 2016 16:27:40 +0000 (11:27 -0500)
Slab allocators that are 'no-touch' will not use their source arenas for
bookkeeping.  This means that we always use a bufctl to point to objects,
instead of appending a pointer to an object and making a small linked list.

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

index 7f1e7d0..9d077c4 100644 (file)
  * 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.
  */
 
 #pragma once
 #define NUM_BUF_PER_SLAB 8
 #define SLAB_LARGE_CUTOFF (PGSIZE / NUM_BUF_PER_SLAB)
 
+/* Cache creation flags: */
+#define KMC_NOTOUCH                            0x0001  /* Can't use source/object's memory */
+#define __KMC_USE_BUFCTL               0x1000  /* Internal use */
+
 struct kmem_slab;
 
 /* Control block for buffers for large-object slabs */
index e458176..e5d290a 100644 (file)
@@ -27,6 +27,11 @@ struct kmem_cache kmem_cache_cache[1];
 struct kmem_cache kmem_slab_cache[1];
 struct kmem_cache kmem_bufctl_cache[1];
 
+static bool __use_bufctls(struct kmem_cache *cp)
+{
+       return cp->flags & __KMC_USE_BUFCTL;
+}
+
 void __kmem_cache_create(struct kmem_cache *kc, const char *name,
                          size_t obj_size, int align, int flags,
                          struct arena *source,
@@ -51,6 +56,12 @@ void __kmem_cache_create(struct kmem_cache *kc, const char *name,
        hash_init_hh(&kc->hh);
        for (int i = 0; i < kc->hh.nr_hash_lists; i++)
                BSD_LIST_INIT(&kc->static_hash[i]);
+       /* No touch must use bufctls, even for small objects, so that it does not
+        * use the object as memory.  Note that if we have an arbitrary source,
+        * small objects, and we're 'pro-touch', the small allocation path will
+        * assume we're importing from a PGSIZE-aligned source arena. */
+       if ((obj_size > SLAB_LARGE_CUTOFF) || (flags & KMC_NOTOUCH))
+               kc->flags |= __KMC_USE_BUFCTL;
        /* put in cache list based on it's size */
        struct kmem_cache *i, *prev = NULL;
        spin_lock_irqsave(&kmem_caches_lock);
@@ -100,7 +111,7 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t obj_size,
 
 static void kmem_slab_destroy(struct kmem_cache *cp, struct kmem_slab *a_slab)
 {
-       if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
+       if (!__use_bufctls(cp)) {
                /* Deconstruct all the objects, if necessary */
                if (cp->dtor) {
                        void *buf = a_slab->free_small_obj;
@@ -250,7 +261,7 @@ void *kmem_cache_alloc(struct kmem_cache *cp, int flags)
                TAILQ_INSERT_HEAD(&cp->partial_slab_list, a_slab, link);
        }
        // have a partial now (a_slab), get an item, return item
-       if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
+       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 */
@@ -281,7 +292,7 @@ void kmem_cache_free(struct kmem_cache *cp, void *buf)
        struct kmem_bufctl *a_bufctl;
 
        spin_lock_irqsave(&cp->cache_lock);
-       if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
+       if (!__use_bufctls(cp)) {
                // find its slab
                a_slab = (struct kmem_slab*)(ROUNDDOWN((uintptr_t)buf, PGSIZE) +
                                             PGSIZE - sizeof(struct kmem_slab));
@@ -321,7 +332,8 @@ static bool kmem_cache_grow(struct kmem_cache *cp)
 {
        struct kmem_slab *a_slab;
        struct kmem_bufctl *a_bufctl;
-       if (cp->obj_size <= SLAB_LARGE_CUTOFF) {
+
+       if (!__use_bufctls(cp)) {
                // Just get a single page for small slabs
                page_t *a_page;
 
@@ -428,6 +440,8 @@ void print_kmem_slab(struct kmem_slab *slab)
        printk("Objsize: %d (%p)\n", slab->obj_size, slab->obj_size);
        printk("NumBusy: %d\n", slab->num_busy_obj);
        printk("Num_total: %d\n", slab->num_total_obj);
+       /* This will break if we have a NOTOUCH small slab.  It's debugging code, so
+        * just be careful. */
        if (slab->obj_size + sizeof(uintptr_t) < SLAB_LARGE_CUTOFF) {
                printk("Free Small obj: %p\n", slab->free_small_obj);
                void *buf = slab->free_small_obj;