aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2013-04-23 16:20:57 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2013-08-18 20:40:03 -0400
commitae15018456c44b742d352af323e0b89eae4a6383 (patch)
tree784bd33ca97df210d581b59b9c3d8118ef057181
parentb778ae25366e6f3891fe51306f56a3bca211975d (diff)
rcu: Make call_rcu() leak callbacks for debug-object errors
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> Reviewed-by: Josh Triplett <josh@joshtriplett.org>
-rw-r--r--kernel/rcu.h10
-rw-r--r--kernel/rcutree.c14
2 files changed, 20 insertions, 4 deletions
diff --git a/kernel/rcu.h b/kernel/rcu.h
index 0a90ccc65bfb..77131966c4ad 100644
--- a/kernel/rcu.h
+++ b/kernel/rcu.h
@@ -67,12 +67,15 @@
67 67
68extern struct debug_obj_descr rcuhead_debug_descr; 68extern struct debug_obj_descr rcuhead_debug_descr;
69 69
70static inline void debug_rcu_head_queue(struct rcu_head *head) 70static inline int debug_rcu_head_queue(struct rcu_head *head)
71{ 71{
72 debug_object_activate(head, &rcuhead_debug_descr); 72 int r1;
73
74 r1 = debug_object_activate(head, &rcuhead_debug_descr);
73 debug_object_active_state(head, &rcuhead_debug_descr, 75 debug_object_active_state(head, &rcuhead_debug_descr,
74 STATE_RCU_HEAD_READY, 76 STATE_RCU_HEAD_READY,
75 STATE_RCU_HEAD_QUEUED); 77 STATE_RCU_HEAD_QUEUED);
78 return r1;
76} 79}
77 80
78static inline void debug_rcu_head_unqueue(struct rcu_head *head) 81static 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)
83 debug_object_deactivate(head, &rcuhead_debug_descr); 86 debug_object_deactivate(head, &rcuhead_debug_descr);
84} 87}
85#else /* !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ 88#else /* !CONFIG_DEBUG_OBJECTS_RCU_HEAD */
86static inline void debug_rcu_head_queue(struct rcu_head *head) 89static inline int debug_rcu_head_queue(struct rcu_head *head)
87{ 90{
91 return 0;
88} 92}
89 93
90static inline void debug_rcu_head_unqueue(struct rcu_head *head) 94static inline void debug_rcu_head_unqueue(struct rcu_head *head)
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index a7bf517b0482..91840566e294 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -2305,6 +2305,13 @@ static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp,
2305} 2305}
2306 2306
2307/* 2307/*
2308 * RCU callback function to leak a callback.
2309 */
2310static void rcu_leak_callback(struct rcu_head *rhp)
2311{
2312}
2313
2314/*
2308 * Helper function for call_rcu() and friends. The cpu argument will 2315 * Helper function for call_rcu() and friends. The cpu argument will
2309 * normally be -1, indicating "currently running CPU". It may specify 2316 * normally be -1, indicating "currently running CPU". It may specify
2310 * a CPU only if that CPU is a no-CBs CPU. Currently, only _rcu_barrier() 2317 * a CPU only if that CPU is a no-CBs CPU. Currently, only _rcu_barrier()
@@ -2318,7 +2325,12 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
2318 struct rcu_data *rdp; 2325 struct rcu_data *rdp;
2319 2326
2320 WARN_ON_ONCE((unsigned long)head & 0x3); /* Misaligned rcu_head! */ 2327 WARN_ON_ONCE((unsigned long)head & 0x3); /* Misaligned rcu_head! */
2321 debug_rcu_head_queue(head); 2328 if (debug_rcu_head_queue(head)) {
2329 /* Probable double call_rcu(), so leak the callback. */
2330 ACCESS_ONCE(head->func) = rcu_leak_callback;
2331 WARN_ONCE(1, "__call_rcu(): Leaked duplicate callback\n");
2332 return;
2333 }
2322 head->func = func; 2334 head->func = func;
2323 head->next = NULL; 2335 head->next = NULL;
2324 2336