radix: Use call_rcu() to free r_nodes
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 8 Jun 2018 16:42:11 +0000 (12:42 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 8 Jun 2018 16:42:11 +0000 (12:42 -0400)
The radix code can be called from call_rcu().  That means it can't use
blocking RCU primitives, such as synchronize_rcu().  Alternatively, we
could have a racy destruction primitive, but that didn't seem worth the
effort.

For those curious, you could trigger this bug by deleting a moderately
sized file that was in the page cache.  e.g.

$ get_html; rm bin/get_html

You'll get a panic / BT similar to this:

 #02 [<0xffffffffc2058932>] in synchronize_rcu
 #03 [<0xffffffffc205741f>] in __radix_remove_slot
 #04 [<0xffffffffc2057566>] in rnode_for_each
 #05 [<0xffffffffc20574e8>] in rnode_for_each
 #06 [<0xffffffffc20578db>] in radix_for_each_slot
 #07 [<0xffffffffc204f9af>] in pm_destroy
 #08 [<0xffffffffc203fdcb>] in cleanup_fs_file
 #09 [<0xffffffffc204a418>] in __tf_free
 #10 [<0xffffffffc204a4a0>] in __tf_free_rcu
 #11 [<0xffffffffc2057f92>] in rcu_exec_cb
 #12 [<0xffffffffc2058232>] in rcu_mgmt_ktask

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

index 260c2ee..af521a7 100644 (file)
 #include <ros/common.h>
 #include <kthread.h>
 #include <atomic.h>
+#include <rcu.h>
 
 struct radix_node {
+       struct rcu_head                         rcu;
        void                                            *items[NR_RNODE_SLOTS];
        unsigned int                            num_items;
        bool                                            leaf;
index 8390efa..c8cdec2 100644 (file)
@@ -109,6 +109,13 @@ int radix_insert(struct radix_tree *tree, unsigned long key, void *item,
        return 0;
 }
 
+static void __rnode_free_rcu(struct rcu_head *head)
+{
+       struct radix_node *r_node = container_of(head, struct radix_node, rcu);
+
+       kmem_cache_free(radix_kcache, r_node);
+}
+
 /* Removes an item from it's parent's structure, freeing the parent if there is
  * nothing left, potentially recursively. */
 static void __radix_remove_slot(struct radix_node *r_node,
@@ -125,8 +132,7 @@ static void __radix_remove_slot(struct radix_node *r_node,
                        __radix_remove_slot(r_node->parent, r_node->my_slot);
                else                    /* we're the last node, attached to the actual tree */
                        *(r_node->my_slot) = 0;
-               synchronize_rcu();
-               kmem_cache_free(radix_kcache, r_node);
+               call_rcu(&r_node->rcu, __rnode_free_rcu);
        }
 }