In the cleanup_srcu_struct(), when iterating over per-cpu's srcu_data, the timer_delete_sync(&sdp->delay_work) is called to cancel the delay timer before flush_work(&sdp->work).
However, if the timer_delete_sync() returns 1 means that it successfully deleted an pending timer before it had a chance to fire, also means that the sdp->work cannot be queued, the subsequent flush_work(&sdp->work) will returns immediately without waiting for anything, this causes SRCU callbacks to not be processed. Fix this by checking the return value of timer_delete_sync(), if it returns 1, explicitly queue sdp->work so that the following flush_work() can correctly wait for the work to complete. Signed-off-by: Zqiang <[email protected]> --- kernel/rcu/srcutree.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c index 7c2f7cc131f7..02c322b7c6f1 100644 --- a/kernel/rcu/srcutree.c +++ b/kernel/rcu/srcutree.c @@ -725,7 +725,11 @@ void cleanup_srcu_struct(struct srcu_struct *ssp) for_each_possible_cpu(cpu) { struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu); - timer_delete_sync(&sdp->delay_work); + //In most scenarios, calling srcu_barrier before cleanup + //will not trigger WARN_ON(). + if (WARN_ON(timer_delete_sync(&sdp->delay_work)) && + rcu_cpu_beenfullyonline(sdp->cpu)) + queue_work_on(sdp->cpu, rcu_gp_wq, &sdp->work); flush_work(&sdp->work); if (WARN_ON(rcu_segcblist_n_cbs(&sdp->srcu_cblist))) return; /* Forgot srcu_barrier(), so just leak it! */ -- 2.17.1

