]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
rcu: Make call_rcu() leak callbacks for debug-object errors
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Tue, 23 Apr 2013 20:20:57 +0000 (13:20 -0700)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Mon, 27 May 2013 11:59:18 +0000 (04:59 -0700)
If someone does a duplicate call_rcu(), the worst thing the second
call_rcu() could do would be to actually queue the callback the second
time because doing so corrupts whatever list the callback was already
queued on.  This commit therefore makes __call_rcu() check the new
return value from debug-objects and leak the callback upon error.
This commit also substitutes rcu_leak_callback() for whatever callback
function was previously in place in order to avoid freeing the callback
out from under any readers that might still be referencing it.

These changes increase the probability that the debug-objects error
messages will actually make it somewhere visible.

Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Sedat Dilek <sedat.dilek@gmail.com>
Cc: Davidlohr Bueso <davidlohr.bueso@hp.com>
Cc: Rik van Riel <riel@surriel.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Tested-by: Sedat Dilek <sedat.dilek@gmail.com>
kernel/rcu.h
kernel/rcutree.c

index 7f8e7590e3e5d414141f3f778caaf58f8b0f6717..ad6c3f521326254a5c475cca5aa3639a251f448d 100644 (file)
 
 extern struct debug_obj_descr rcuhead_debug_descr;
 
-static inline void debug_rcu_head_queue(struct rcu_head *head)
+static inline int debug_rcu_head_queue(struct rcu_head *head)
 {
-       debug_object_activate(head, &rcuhead_debug_descr);
+       int r1;
+
+       r1 = debug_object_activate(head, &rcuhead_debug_descr);
        debug_object_active_state(head, &rcuhead_debug_descr,
                                  STATE_RCU_HEAD_READY,
                                  STATE_RCU_HEAD_QUEUED);
+       return r1;
 }
 
 static inline void debug_rcu_head_unqueue(struct rcu_head *head)
@@ -83,8 +86,9 @@ static inline void debug_rcu_head_unqueue(struct rcu_head *head)
        debug_object_deactivate(head, &rcuhead_debug_descr);
 }
 #else  /* !CONFIG_DEBUG_OBJECTS_RCU_HEAD */
-static inline void debug_rcu_head_queue(struct rcu_head *head)
+static inline int debug_rcu_head_queue(struct rcu_head *head)
 {
+       return 0;
 }
 
 static inline void debug_rcu_head_unqueue(struct rcu_head *head)
index 00d4de0102f724d6c1993c84ac301ff87e78d2cf..b02fd704a29ae7b4fecc15c2efc758c5b72d15a4 100644 (file)
@@ -2275,6 +2275,13 @@ static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp,
        }
 }
 
+/*
+ * RCU callback function to leak a callback.
+ */
+static void rcu_leak_callback(struct rcu_head *rhp)
+{
+}
+
 /*
  * Helper function for call_rcu() and friends.  The cpu argument will
  * normally be -1, indicating "currently running CPU".  It may specify
@@ -2289,7 +2296,12 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
        struct rcu_data *rdp;
 
        WARN_ON_ONCE((unsigned long)head & 0x3); /* Misaligned rcu_head! */
-       debug_rcu_head_queue(head);
+       if (debug_rcu_head_queue(head)) {
+               /* Probable double call_rcu(), so leak the callback. */
+               ACCESS_ONCE(head->func) = rcu_leak_callback;
+               WARN_ONCE(1, "__call_rcu(): Leaked duplicate callback\n");
+               return;
+       }
        head->func = func;
        head->next = NULL;