diff options
author | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2017-04-28 15:39:34 -0700 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2017-06-08 08:25:39 -0700 |
commit | a602538e46c9c62e75f1f0be9806495c79bcda9f (patch) | |
tree | 07367b349723f12dea138a7e2fad5e845d904cb1 /kernel/rcu | |
parent | d4efe6c5ad91f9a1f2f1d66b7fbfc87e320b2abc (diff) | |
download | linux-a602538e46c9c62e75f1f0be9806495c79bcda9f.tar.gz linux-a602538e46c9c62e75f1f0be9806495c79bcda9f.tar.bz2 linux-a602538e46c9c62e75f1f0be9806495c79bcda9f.zip |
srcu: Add DEBUG_OBJECTS_RCU_HEAD functionality
This commit adds DEBUG_OBJECTS_RCU_HEAD checking to detect call_srcu()
counterparts to double-free bugs.
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'kernel/rcu')
-rw-r--r-- | kernel/rcu/srcutree.c | 19 |
1 files changed, 18 insertions, 1 deletions
diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index 0b6ea105d9f8..c6e2a4a1628b 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -762,6 +762,13 @@ static bool srcu_might_be_idle(struct srcu_struct *sp) } /* + * SRCU callback function to leak a callback. + */ +static void srcu_leak_callback(struct rcu_head *rhp) +{ +} + +/* * Enqueue an SRCU callback on the srcu_data structure associated with * the current CPU and the specified srcu_struct structure, initiating * grace-period processing if it is not already running. @@ -799,6 +806,12 @@ void __call_srcu(struct srcu_struct *sp, struct rcu_head *rhp, struct srcu_data *sdp; check_init_srcu_struct(sp); + if (debug_rcu_head_queue(rhp)) { + /* Probable double call_srcu(), so leak the callback. */ + WRITE_ONCE(rhp->func, srcu_leak_callback); + WARN_ONCE(1, "call_srcu(): Leaked duplicate callback\n"); + return; + } rhp->func = func; local_irq_save(flags); sdp = this_cpu_ptr(sp->sda); @@ -973,9 +986,12 @@ void srcu_barrier(struct srcu_struct *sp) spin_lock_irq(&sdp->lock); atomic_inc(&sp->srcu_barrier_cpu_cnt); sdp->srcu_barrier_head.func = srcu_barrier_cb; + debug_rcu_head_queue(&sdp->srcu_barrier_head); if (!rcu_segcblist_entrain(&sdp->srcu_cblist, - &sdp->srcu_barrier_head, 0)) + &sdp->srcu_barrier_head, 0)) { + debug_rcu_head_unqueue(&sdp->srcu_barrier_head); atomic_dec(&sp->srcu_barrier_cpu_cnt); + } spin_unlock_irq(&sdp->lock); } @@ -1100,6 +1116,7 @@ static void srcu_invoke_callbacks(struct work_struct *work) spin_unlock_irq(&sdp->lock); rhp = rcu_cblist_dequeue(&ready_cbs); for (; rhp != NULL; rhp = rcu_cblist_dequeue(&ready_cbs)) { + debug_rcu_head_unqueue(rhp); local_bh_disable(); rhp->func(rhp); local_bh_enable(); |