arena: allow "self-sourced" arenas 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>
diff --git a/kern/drivers/dev/mem.c b/kern/drivers/dev/mem.c index 5379c50..36b9856 100644 --- a/kern/drivers/dev/mem.c +++ b/kern/drivers/dev/mem.c
@@ -298,8 +298,11 @@ 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 @@ 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); }
diff --git a/kern/include/arena.h b/kern/include/arena.h index a6e64d5..0f49c74 100644 --- a/kern/include/arena.h +++ b/kern/include/arena.h
@@ -90,6 +90,8 @@ #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
diff --git a/kern/src/arena.c b/kern/src/arena.c index cbf1711..6f6e400 100644 --- a/kern/src/arena.c +++ b/kern/src/arena.c
@@ -199,8 +199,11 @@ 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 @@ * 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 @@ 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);
diff --git a/kern/src/ktest/kt_arena.c b/kern/src/ktest/kt_arena.c index 29c3978..0b0be7f 100644 --- a/kern/src/ktest/kt_arena.c +++ b/kern/src/ktest/kt_arena.c
@@ -430,6 +430,48 @@ 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 @@ 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);