Don't chan_release() from an RCU callback
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 20 Jul 2018 14:20:56 +0000 (10:20 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 20 Jul 2018 14:25:54 +0000 (10:25 -0400)
RCU callbacks are not allowed to block.  chan_release(), which is triggered
by decreffing a chan, can be triggered from an RCU callback.

Here's an example of a callchain that would have panicked:

 #01 [<0xffffffffc200b7c2>] in sem_down  (block here)
 #02 [<0xffffffffc200c221>] in cv_wait
 #03 [<0xffffffffc204ee05>] in rendez_sleep (this actually panics now)
 #04 [<0xffffffffc2039fa4>] in __qbread
 #05 [<0xffffffffc207c014>] in doread
 #06 [<0xffffffffc207c901>] in mntrpcread
 #07 [<0xffffffffc207d1a5>] in mountio
 #08 [<0xffffffffc207d3b5>] in mountrpc
 #09 [<0xffffffffc207dbfd>] in mntclunk
 #10 [<0xffffffffc2031448>] in chan_release
 #11 [<0xffffffffc2030bcb>] in kref_put
 #12 [<0xffffffffc2078be0>] in gtfs_tf_free
 #13 [<0xffffffffc2042125>] in __tf_free
 #14 [<0xffffffffc204f7e2>] in rcu_mgmt_ktask
 #15 [<0xffffffffc200acc0>] in __ktask_wrapper
 #16 [<0xffffffffc205981f>] in process_routine_kmsg
 #17 [<0xffffffffc20534a8>] in __smp_idle

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

index 9e42ff9..b7d6a1d 100644 (file)
@@ -158,6 +158,13 @@ static void chan_release(struct kref *kref)
 {
        struct chan *c = container_of(kref, struct chan, ref);
        ERRSTACK(1);
+
+       /* We can be called from RCU callbacks, but close methods can block.  In
+        * those cases, we need to defer our work to a kernel message. */
+       if (in_rcu_cb_ctx(this_pcpui_ptr())) {
+               run_as_rkm(chan_release, kref);
+               return;
+       }
        /* this style discards the error from close().  picture it as
         * if (waserror()) { } else { close(); } chanfree_no_matter_what();  */
        if (!waserror()) {