arena: allow "self-sourced" arenas
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 2 Oct 2019 16:52:10 +0000 (12:52 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 8 Oct 2019 21:11:11 +0000 (17:11 -0400)
These arenas generate their own resources dynamically and arbitrarily.
They do not source from another arena, such as base, but they also are
not given their segments in advance with arena_add().  Instead, they
create resources when asked.

It is similar to calling arena_add() in response to a failed allocation,
except the allocation never fails.

The arena alloc function pointer lets you do whatever you want.  The
reason for self-sourcing instead of pulling from a dummy arena is that
the self-source provides a pointer to the arena in the allocation
function.  With that pointer, you now have your original arena, which
could be embedded in another structure.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/drivers/dev/mem.c
kern/include/arena.h
kern/src/arena.c
kern/src/ktest/kt_arena.c

index 5379c50..36b9856 100644 (file)
@@ -298,8 +298,11 @@ static void fetch_arena_and_kids(struct arena *arena, struct sized_alloc *sza,
        struct kmem_cache *kc_i;
 
        fetch_arena_line(arena, sza, indent);
-       TAILQ_FOREACH(a_i, &arena->__importing_arenas, import_link)
+       TAILQ_FOREACH(a_i, &arena->__importing_arenas, import_link) {
+               if (a_i == arena)
+                       continue;
                fetch_arena_and_kids(a_i, sza, indent + 1);
+       }
        TAILQ_FOREACH(kc_i, &arena->__importing_slabs, import_link)
                fetch_slab_line(kc_i, sza, indent + 1);
 }
@@ -327,7 +330,7 @@ static struct sized_alloc *build_kmemstat(void)
                sza_printf(sza, "-");
        sza_printf(sza, "\n");
        TAILQ_FOREACH(a_i, &all_arenas, next) {
-               if (a_i->source)
+               if (a_i->source && a_i->source != a_i)
                        continue;
                fetch_arena_and_kids(a_i, sza, 0);
        }
index a6e64d5..0f49c74 100644 (file)
@@ -90,6 +90,8 @@ extern struct arena_tailq all_arenas;
 #define ARENA_NEXTFIT          0x400
 #define ARENA_ALLOC_STYLES (ARENA_BESTFIT | ARENA_INSTANTFIT | ARENA_NEXTFIT)
 
+/* Magic source, for arenas that dynamically generate their contents. */
+#define ARENA_SELF_SOURCE (void*)(-2)
 /* Creates an area, with initial segment [@base, @base + @size).  Allocs are in
  * units of @quantum.  If @source is provided, the arena will alloc new segments
  * from @source, calling @afunc to alloc and @ffunc to free.  Uses a slab
index cbf1711..6f6e400 100644 (file)
@@ -199,8 +199,11 @@ static void arena_init(struct arena *arena, const char *name, size_t quantum,
        arena->qcache_max = qcache_max;
        arena->afunc = afunc;
        arena->ffunc = ffunc;
-       arena->source = source;
-       if (source) {
+       if (source == ARENA_SELF_SOURCE)
+               arena->source = arena;
+       else
+               arena->source = source;
+       if (arena->source) {
                assert(afunc && ffunc);
                /* When we import, there may be a quantum mismatch such that our
                 * source hands us spans that are not sufficient for our
@@ -213,7 +216,7 @@ static void arena_init(struct arena *arena, const char *name, size_t quantum,
                 * if all s allocs (our spans) are also divided by a->q, in
                 * which case we don't need to import extra.  This is true when
                 * a->q divides s->q.  (s->q is a multiple of a->q). */
-               if (source->quantum % arena->quantum)
+               if (arena->source->quantum % arena->quantum)
                        arena->import_scale = 1;
        }
        arena->amt_total_segs = 0;
@@ -236,8 +239,8 @@ static void arena_init(struct arena *arena, const char *name, size_t quantum,
        strlcpy(arena->name, name, ARENA_NAME_SZ);
        setup_qcaches(arena, quantum, qcache_max);
 
-       if (source)
-               add_importing_arena(source, arena);
+       if (arena->source)
+               add_importing_arena(arena->source, arena);
        qlock(&arenas_and_slabs_lock);
        TAILQ_INSERT_TAIL(&all_arenas, arena, next);
        qunlock(&arenas_and_slabs_lock);
index 29c3978..0b0be7f 100644 (file)
@@ -430,6 +430,48 @@ static bool test_accounting(void)
        return true;
 }
 
+static void *tssaf(struct arena *a, size_t amt, int flags)
+{
+       static uintptr_t store = PGSIZE;
+       void *ret;
+
+       ret = (void*)store;
+       store += ROUNDUP(amt, a->quantum);
+
+       return ret;
+}
+
+static void tssff(struct arena *a, void *obj, size_t amt)
+{
+}
+
+static bool test_self_source(void)
+{
+       struct arena *s, *a;
+       void *o1, *o2;
+
+       s = arena_create(__func__, NULL, 0, PGSIZE, tssaf, tssff,
+                        ARENA_SELF_SOURCE, 0, MEM_WAIT);
+       o1 = arena_alloc(s, 1, MEM_WAIT);
+       o2 = arena_alloc(s, 1, MEM_WAIT);
+       KT_ASSERT(o1 != o2);
+       arena_free(s, o1, 1);
+       arena_free(s, o2, 1);
+
+       a = arena_create("test_self_source-import", NULL, 0, 1,
+                        arena_alloc, arena_free, s, 0, MEM_WAIT);
+       o1 = arena_alloc(a, 1, MEM_WAIT);
+       o2 = arena_alloc(a, 1, MEM_WAIT);
+       KT_ASSERT(o1 != o2);
+       arena_free(a, o1, 1);
+       arena_free(a, o2, 1);
+
+       arena_destroy(a);
+       arena_destroy(s);
+
+       return true;
+}
+
 static struct ktest ktests[] = {
        KTEST_REG(nextfit,              CONFIG_KTEST_ARENA),
        KTEST_REG(bestfit,              CONFIG_KTEST_ARENA),
@@ -447,6 +489,7 @@ static struct ktest ktests[] = {
        KTEST_REG(xalloc,               CONFIG_KTEST_ARENA),
        KTEST_REG(xalloc_minmax,        CONFIG_KTEST_ARENA),
        KTEST_REG(accounting,           CONFIG_KTEST_ARENA),
+       KTEST_REG(self_source,          CONFIG_KTEST_ARENA),
 };
 
 static int num_ktests = sizeof(ktests) / sizeof(struct ktest);