Tracks arenas and slabs on tailqs
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 9 Nov 2016 16:12:52 +0000 (11:12 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 29 Nov 2016 16:27:40 +0000 (11:27 -0500)
Both lists are protected by the arenas_and_slabs qlock.  The all_arenas
list will be useful for #mem.  The all_kmem_caches might not be useful,
since all caches always have a source arena.  It's fine for diagnostics
for now.

The important thing is that the existence and importing links are all
managed by the same lock, so things like reclaim and #mem device ops can
happen without worrying about the read-mostly structure changing.

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

index 84c08bf..f23ed8e 100644 (file)
@@ -82,6 +82,8 @@ struct arena {
        struct kmem_cache_tailq         __importing_slabs;
 };
 
+extern struct arena_tailq all_arenas;
+
 /* Arena allocation styles, or'd with MEM_FLAGS */
 #define ARENA_BESTFIT                  0x100
 #define ARENA_INSTANTFIT               0x200
index cb188c6..6291f2a 100644 (file)
@@ -100,7 +100,7 @@ TAILQ_HEAD(kmem_slab_list, kmem_slab);
 
 /* Actual cache */
 struct kmem_cache {
-       SLIST_ENTRY(kmem_cache) link;
+       TAILQ_ENTRY(kmem_cache) all_kmc_link;
        struct kmem_pcpu_cache *pcpu_caches;
        struct kmem_depot depot;
        spinlock_t cache_lock;
@@ -122,9 +122,7 @@ struct kmem_cache {
        TAILQ_ENTRY(kmem_cache) import_link;
 };
 
-/* List of all kmem_caches, sorted in order of size */
-SLIST_HEAD(kmem_cache_list, kmem_cache);
-extern struct kmem_cache_list kmem_caches;
+extern struct kmem_cache_tailq all_kmem_caches;
 
 /* Cache management */
 struct kmem_cache *kmem_cache_create(const char *name, size_t obj_size,
index 04f3185..d6647f1 100644 (file)
  *   help us get out of OOM.  So we might block when we're at low-mem, not at 0.
  *   We probably should have a sorted list of desired amounts, and unblockers
  *   poke the CV if the first waiter is likely to succeed.
- * - qcaching
- * - We'll need some linkage between sources and parent arenas, with callbacks
- *   or something when the base arena starts to run low on memory.  Once an
- *   arena (whether base or o/w) gets the "time to free up memory" call, it can
- *   call into any of its children, to include slabs and whatever else.
+ * - Reclaim: have a ktask that sleeps on a rendez.  We poke it, even from IRQ
+ *   context.  It qlocks arenas_and_slabs_lock, then does the reclaim.
  *
  * FAQ:
  * - Does allocating memory from an arena require it to take a btag?  Yes -
@@ -82,8 +79,7 @@
 #include <slab.h>
 #include <kthread.h>
 
-static struct arena_tailq all_arenas = TAILQ_HEAD_INITIALIZER(all_arenas);
-static spinlock_t all_arenas_lock = SPINLOCK_INITIALIZER;
+struct arena_tailq all_arenas = TAILQ_HEAD_INITIALIZER(all_arenas);
 qlock_t arenas_and_slabs_lock = QLOCK_INITIALIZER(arenas_and_slabs_lock);
 
 struct arena *base_arena;
@@ -176,9 +172,9 @@ static void arena_init(struct arena *arena, char *name, size_t quantum,
 
        if (source)
                add_importing_arena(source, arena);
-       spin_lock(&all_arenas_lock);
+       qlock(&arenas_and_slabs_lock);
        TAILQ_INSERT_TAIL(&all_arenas, arena, next);
-       spin_unlock(&all_arenas_lock);
+       qunlock(&arenas_and_slabs_lock);
 }
 
 struct arena *arena_create(char *name, void *base, size_t size, size_t quantum,
@@ -209,9 +205,9 @@ void arena_destroy(struct arena *arena)
 {
        struct btag *bt_i, *temp;
 
-       spin_lock(&all_arenas_lock);
+       qlock(&arenas_and_slabs_lock);
        TAILQ_REMOVE(&all_arenas, arena, next);
-       spin_unlock(&all_arenas_lock);
+       qunlock(&arenas_and_slabs_lock);
        if (arena->source)
                del_importing_arena(arena->source, arena);
 
index 77e137b..b38e4d8 100644 (file)
@@ -94,8 +94,9 @@
 uint64_t resize_timeout_ns = 1000000000;
 unsigned int resize_threshold = 1;
 
-struct kmem_cache_list kmem_caches = SLIST_HEAD_INITIALIZER(kmem_caches);
-spinlock_t kmem_caches_lock = SPINLOCK_INITIALIZER_IRQSAVE;
+/* Protected by the arenas_and_slabs_lock. */
+struct kmem_cache_tailq all_kmem_caches =
+               TAILQ_HEAD_INITIALIZER(all_kmem_caches);
 
 /* Backend/internal functions, defined later.  Grab the lock before calling
  * these. */
@@ -294,21 +295,9 @@ void __kmem_cache_create(struct kmem_cache *kc, const char *name,
         * could be creating on this call! */
        kc->pcpu_caches = build_pcpu_caches();
        add_importing_slab(kc->source, kc);
-       /* put in cache list based on it's size */
-       struct kmem_cache *i, *prev = NULL;
-       spin_lock_irqsave(&kmem_caches_lock);
-       /* find the kmem_cache before us in the list.  yes, this is O(n). */
-       SLIST_FOREACH(i, &kmem_caches, link) {
-               if (i->obj_size < kc->obj_size)
-                       prev = i;
-               else
-                       break;
-       }
-       if (prev)
-               SLIST_INSERT_AFTER(prev, kc, link);
-       else
-               SLIST_INSERT_HEAD(&kmem_caches, kc, link);
-       spin_unlock_irqsave(&kmem_caches_lock);
+       qlock(&arenas_and_slabs_lock);
+       TAILQ_INSERT_TAIL(&all_kmem_caches, kc, all_kmc_link);
+       qunlock(&arenas_and_slabs_lock);
 }
 
 static void __mag_ctor(void *obj, size_t _ign)
@@ -414,6 +403,9 @@ void kmem_cache_destroy(struct kmem_cache *cp)
 {
        struct kmem_slab *a_slab, *next;
 
+       qlock(&arenas_and_slabs_lock);
+       TAILQ_REMOVE(&all_kmem_caches, cp, all_kmc_link);
+       qunlock(&arenas_and_slabs_lock);
        del_importing_slab(cp->source, cp);
        drain_pcpu_caches(cp);
        depot_destroy(cp);
@@ -429,11 +421,8 @@ void kmem_cache_destroy(struct kmem_cache *cp)
                kmem_slab_destroy(cp, a_slab);
                a_slab = next;
        }
-       spin_lock_irqsave(&kmem_caches_lock);
-       SLIST_REMOVE(&kmem_caches, cp, kmem_cache, link);
-       spin_unlock_irqsave(&kmem_caches_lock);
-       kmem_cache_free(kmem_cache_cache, cp);
        spin_unlock_irqsave(&cp->cache_lock);
+       kmem_cache_free(kmem_cache_cache, cp);
 }
 
 static void __try_hash_resize(struct kmem_cache *cp)