arena/slab: warn when destroying unfreed items
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 23 Sep 2019 23:36:11 +0000 (19:36 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 8 Oct 2019 21:11:11 +0000 (17:11 -0400)
Instead of panicking.  This leaks resources, but keeps the machine
running.  It's a little hokey, since any attempt to free the objects
will run into issues.  Though if someone forgot to free it, hopefully
they won't free it before we can use the machine for some last minute
diagnostics.

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

index ca624aa..a4f9d4e 100644 (file)
@@ -218,8 +218,15 @@ void arena_destroy(struct arena *arena)
        if (arena->source)
                del_importing_arena(arena->source, arena);
 
-       for (int i = 0; i < arena->hh.nr_hash_lists; i++)
-               assert(BSD_LIST_EMPTY(&arena->alloc_hash[i]));
+       for (int i = 0; i < arena->hh.nr_hash_lists; i++) {
+               /* Marginal at best.  The qcaches are destroyed already; if
+                * someone tries to free this later, we're in trouble. */
+               if (!BSD_LIST_EMPTY(&arena->alloc_hash[i])) {
+                       warn("Arena %s has unfreed items!  Will not destroy.",
+                            arena->name);
+                       return;
+               }
+       }
        if (arena->alloc_hash != arena->static_hash)
                kfree(arena->alloc_hash);
        /* We shouldn't have any spans left.  We can tell we messed up if we had
index fecffcd..55b7776 100644 (file)
@@ -458,8 +458,16 @@ void kmem_cache_destroy(struct kmem_cache *cp)
        drain_pcpu_caches(cp);
        depot_destroy(cp);
        spin_lock_irqsave(&cp->cache_lock);
-       assert(TAILQ_EMPTY(&cp->full_slab_list));
-       assert(TAILQ_EMPTY(&cp->partial_slab_list));
+       /* This is a little debatable.  We leak the cache and whatnot, but even
+        * worse, someone has the object still, and they might free it, after
+        * we've already torn down the depot.  At best this is a marginal way to
+        * continue.  See similar code in arena.c. */
+       if (!TAILQ_EMPTY(&cp->full_slab_list) ||
+           !TAILQ_EMPTY(&cp->partial_slab_list)) {
+               warn("KMC %s has unfreed items!  Will not destroy.", cp->name);
+               spin_unlock_irqsave(&cp->cache_lock);
+               return;
+       }
        /* Clean out the empty list.  We can't use a regular FOREACH here, since
         * the link element is stored in the slab struct, which is stored on the
         * page that we are freeing. */