RCU flavor-consolidation changes for v4.20/v5.0
Subsystems (4)
cover RCU flavor-consolidation changes for v4.20/v5.0
Hello!
This series contains the RCU flavor consolidation, along with some initial cleanup work enabled by that consolidation (and some that became apparent while cleaning up):
1. Refactor rcu_{nmi,irq}_{enter,exit}(), saving a branch on the
idle entry/exit hotpaths, courtesy of Byungchul Park,
2. Defer reporting RCU-preempt quiescent states when disabled.
This is the key commit that consolidates the RCU-bh and RCU-sched flavors into RCU, however, the RCU-bh and RCU-sched flavors still exist independently as well at this point.
3. Test extended "rcu" read-side critical sections. This commit
causes rcutorture to test RCU's new-found ability to act as the combination of RCU, RCU-bh, and RCU-sched.
4. Allow processing deferred QSes for exiting RCU-preempt readers.
This is a optimization.
5. Remove now-unused ->b.exp_need_qs field from the rcu_special union.
6. Add warning to detect half-interrupts. Test the claim that
the Linux kernel no longer does half-interrupts.
7. Apply RCU-bh QSes to RCU-sched and RCU-preempt when safe, that is,
make the consolidated RCU inherit RCU-bh's denial-of-service avoidance mechanism.
8. Report expedited grace periods at context-switch time. This is
an optimization enabled by the RCU flavor consolidation.
9. Define RCU-bh update API in terms of RCU. This commit gets rid
of the RCU-bh update mechanism.
10. Update comments and help text to account for the removal of
the RCU-bh update mechanism.
11. Drop "wake" parameter from rcu_report_exp_rdp().
12. Fix typo in rcu_get_gp_kthreads_prio() header comment.
13. Define RCU-sched API in terms of RCU for Tree RCU PREEMPT builds.
Although this commit gets rid of the RCU-sched update mechanism from PREEMPT builds, it of course remains as the sole RCU flavor for !PREEMPT && !SMP builds.
14. Express Tiny RCU updates in terms of RCU rather than RCU-sched.
This will enable additional cleanups and code savings.
15. Remove RCU_STATE_INITIALIZER() in favor of just using an open-coded
initializer for the sole remaining rcu_state structure.
16. Eliminate rcu_state structure's ->call field, as it is now always
just call_rcu().
17. Remove rcu_state structure's ->rda field, as there is now only one
set of per-CPU rcu_data structures.
Thanx, Paul
------------------------------------------------------------------------
Documentation/RCU/Design/Requirements/Requirements.html | 50 - include/linux/rcupdate.h | 48 - include/linux/rcupdate_wait.h | 6 include/linux/rcutiny.h | 61 + include/linux/rcutree.h | 31 include/linux/sched.h | 6 kernel/rcu/Kconfig | 10 kernel/rcu/rcutorture.c | 1 kernel/rcu/tiny.c | 163 +-- kernel/rcu/tree.c | 655 +++++----------- kernel/rcu/tree.h | 44 - kernel/rcu/tree_exp.h | 256 +++--- kernel/rcu/tree_plugin.h | 523 ++++++------ kernel/rcu/update.c | 2 kernel/softirq.c | 3 15 files changed, 836 insertions(+), 1023 deletions(-)
PATCH 1/19 rcu: Refactor rcu_{nmi,irq}_{enter,exit}()
From: Byungchul Park <[email protected]>
When entering or exiting irq or NMI handlers, the current code uses ->dynticks_nmi_nesting to detect if it is in the outermost handler, that is, the one interrupting or returning to an RCU-idle context (the idle loop or nohz_full usermode execution). When entering the outermost handler via an interrupt (as opposed to NMI), it is necessary to invoke rcu_dynticks_task_exit() just before the CPU is marked non-idle from an RCU perspective and to invoke rcu_cleanup_after_idle() just after the CPU is marked non-idle. Similarly, when exiting the outermost handler via an interrupt, it is necessary to invoke rcu_prepare_for_idle() just before marking the CPU idle and to invoke rcu_dynticks_task_enter() just after marking the CPU idle.
The decision to execute these four functions is currently taken in rcu_irq_enter() and rcu_irq_exit() as follows:
rcu_irq_enter()
/* A conditional branch with ->dynticks_nmi_nesting */
rcu_nmi_enter()
/* A conditional branch with ->dynticks */
/* A conditional branch with ->dynticks_nmi_nesting */ rcu_irq_exit()
/* A conditional branch with ->dynticks_nmi_nesting */
rcu_nmi_exit()
/* A conditional branch with ->dynticks_nmi_nesting */
/* A conditional branch with ->dynticks_nmi_nesting */ rcu_nmi_enter()
/* A conditional branch with ->dynticks */
rcu_nmi_exit()
/* A conditional branch with ->dynticks_nmi_nesting */
This works, but the conditional branches in rcu_irq_enter() and rcu_irq_exit() are redundant with those in rcu_nmi_enter() and rcu_nmi_exit(), respectively. Redundant branches are not something we want in the to/from-idle fastpaths, so this commit refactors rcu_{nmi,irq}_{enter,exit}() so they use a common inlined function passed a constant argument as follows:
rcu_irq_enter() inlining rcu_nmi_enter_common(irq=true)
/* A conditional branch with ->dynticks */
rcu_irq_exit() inlining rcu_nmi_exit_common(irq=true)
/* A conditional branch with ->dynticks_nmi_nesting */
rcu_nmi_enter() inlining rcu_nmi_enter_common(irq=false)
/* A conditional branch with ->dynticks */
rcu_nmi_exit() inlining rcu_nmi_exit_common(irq=false)
/* A conditional branch with ->dynticks_nmi_nesting */
The combination of the constant function argument and the inlining allows the compiler to discard the conditionals that previously controlled execution of rcu_dynticks_task_exit(), rcu_cleanup_after_idle(), rcu_prepare_for_idle(), and rcu_dynticks_task_enter(). This reduces both the to-idle and from-idle path lengths by two conditional branches each, and improves readability as well.
This commit also changes order of execution from this:
rcu_dynticks_task_exit(); rcu_dynticks_eqs_exit(); trace_rcu_dyntick(); rcu_cleanup_after_idle();
To this:
rcu_dynticks_task_exit(); rcu_dynticks_eqs_exit(); rcu_cleanup_after_idle(); trace_rcu_dyntick();
In other words, the calls to trace_rcu_dyntick() and trace_rcu_dyntick() are reversed. This has no functional effect because the real concern is whether a given call is before or after the call to rcu_dynticks_eqs_exit(), and this patch does not change that. Before the call to rcu_dynticks_eqs_exit(), RCU is not yet watching the current CPU and after that call RCU is watching.
A similar switch in calling order happens on the idle-entry path, with similar lack of effect for the same reasons.
--- kernel/rcu/tree.c | 61 +++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 20 deletions(-)
kernel/rcu/tree.c +41 -20
@@ -771,17 +771,18 @@ void rcu_user_enter(void)#endif /* CONFIG_NO_HZ_FULL */ /** -* rcu_nmi_exit - inform RCU of exit from NMI context +* rcu_nmi_exit_common - inform RCU of exit from NMI context +* @irq: Is this call from rcu_irq_exit? * * If we are returning from the outermost NMI handler that interrupted an * RCU-idle period, update rdtp->dynticks and rdtp->dynticks_nmi_nesting * to let the RCU grace-period handling know that the CPU is back to * being RCU-idle. * -* If you add or remove a call to rcu_nmi_exit(), be sure to test +* If you add or remove a call to rcu_nmi_exit_common(), be sure to test * with CONFIG_RCU_EQS_DEBUG=y. */ -void rcu_nmi_exit (void ) +static __always_inline void rcu_nmi_exit_common(bool irq) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks);
@@ -807,7 +808,22 @@ void rcu_nmi_exit(void)/* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */ trace_rcu_dyntick (TPS ("Startirq" ), rdtp->dynticks_nmi_nesting,0 , rdtp->dynticks);WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );/* Avoid store tearing. */ + +if (irq) +rcu_prepare_for_idle (); +rcu_dynticks_eqs_enter (); + +if (irq) +rcu_dynticks_task_enter (); +} + +/** +* rcu_nmi_exit - inform RCU of exit from NMI context +*/ +void rcu_nmi_exit (void ) +{ +rcu_nmi_exit_common (false); }/**
@@ -831,14 +847,8 @@ void rcu_nmi_exit(void)*/ void rcu_irq_exit (void ) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -lockdep_assert_irqs_disabled (); -if (rdtp->dynticks_nmi_nesting ==1 ) -rcu_prepare_for_idle (); -rcu_nmi_exit (); -if (rdtp->dynticks_nmi_nesting ==0 ) -rcu_dynticks_task_enter (); +rcu_nmi_exit_common (true); }/*
@@ -921,7 +931,8 @@ void rcu_user_exit(void)#endif /* CONFIG_NO_HZ_FULL */ /** -* rcu_nmi_enter - inform RCU of entry to NMI context +* rcu_nmi_enter_common - inform RCU of entry to NMI context +* @irq: Is this call from rcu_irq_enter? * * If the CPU was idle from RCU's viewpoint, update rdtp->dynticks and * rdtp->dynticks_nmi_nesting to let the RCU grace-period handling know
@@ -929,10 +940,10 @@ void rcu_user_exit(void)* long as the nesting level does not overflow an int. (You will probably * run out of stack space first.) * -* If you add or remove a call to rcu_nmi_enter(), be sure to test +* If you add or remove a call to rcu_nmi_enter_common(), be sure to test * with CONFIG_RCU_EQS_DEBUG=y. */ -void rcu_nmi_enter (void ) +static __always_inline void rcu_nmi_enter_common(bool irq) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks);long incby =2 ;
@@ -949,7 +960,15 @@ void rcu_nmi_enter(void)* period (observation due to Andy Lutomirski). */ if (rcu_dynticks_curr_cpu_in_eqs ()) { + +if (irq) +rcu_dynticks_task_exit (); +rcu_dynticks_eqs_exit (); + +if (irq) +rcu_cleanup_after_idle (); + incby =1 ; } trace_rcu_dyntick(incby == 1 ? TPS("Endirq") : TPS("++="),
@@ -960,6 +979,14 @@ void rcu_nmi_enter(void)barrier (); } +/** +* rcu_nmi_enter - inform RCU of entry to NMI context +*/ +void rcu_nmi_enter (void ) +{ +rcu_nmi_enter_common (false); +} +/** * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle *
@@ -984,14 +1011,8 @@ void rcu_nmi_enter(void)*/ void rcu_irq_enter (void ) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -lockdep_assert_irqs_disabled (); -if (rdtp->dynticks_nmi_nesting ==0 ) -rcu_dynticks_task_exit (); -rcu_nmi_enter (); -if (rdtp->dynticks_nmi_nesting ==1 ) -rcu_cleanup_after_idle (); +rcu_nmi_enter_common (true); }/*
Signature · 1 line
2.17.1
Re: [PATCH tip/core/rcu 01/19] rcu: Refactor rcu_{nmi,irq}_{enter,exit}()
Quoted 7 lines · On Wed, 29 Aug 2018 15:20:29 -0700 "Paul E. McKenney"…
This commit also changes order of execution from this:
rcu_dynticks_task_exit(); rcu_dynticks_eqs_exit(); trace_rcu_dyntick(); rcu_cleanup_after_idle();
Quoted 8 lines
To this:
rcu_dynticks_task_exit(); rcu_dynticks_eqs_exit(); rcu_cleanup_after_idle(); trace_rcu_dyntick();In other words, the calls to
trace_rcu_dyntick()andtrace_rcu_dyntick()
How is trace_rcu_dyntick() and trace_rcu_dyntick reversed ? ;-)
Quoted 26 lines
are reversed. This has no functional effect because the real concern is whether a given call is before or after the call to
rcu_dynticks_eqs_exit(), and this patch does not change that. Before the call torcu_dynticks_eqs_exit(), RCU is not yet watching the current CPU and after that call RCU is watching.A similar switch in calling order happens on the idle-entry path, with similar lack of effect for the same reasons.
Suggested-by Paul E. McKenney <[email protected]>Signed-off-by Byungchul Park <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 61 +++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 20 deletions(-)kernel/rcu/tree.c +1
-1@@ -771,17 +771,18 @@ void rcu_user_enter(void)#endif /* CONFIG_NO_HZ_FULL */ /** -* rcu_nmi_exit - inform RCU of exit from NMI context +* rcu_nmi_exit_common - inform RCU of exit from NMI context
kernel/rcu/tree.c void rcu_user_enter(void) · line 783
Quoted 10 lines
kernel/rcu/tree.c +2
-1+* @irq: Is this call from rcu_irq_exit? * * If we are returning from the outermost NMI handler that interrupted an * RCU-idle period, update rdtp->dynticks and rdtp->dynticks_nmi_nesting * to let the RCU grace-period handling know that the CPU is back to * being RCU-idle. * -* If you add or remove a call to rcu_nmi_exit(), be sure to test +* If you add or remove a call to rcu_nmi_exit_common(), be sure to test * with CONFIG_RCU_EQS_DEBUG=y.
As this is a static function, this description doesn't make sense. You need to move the description down to the new rcu_nmi_exit() below.
Other than that...
-- Steve
Quoted 115 lines
kernel/rcu/tree.c +38
-18*/ -void rcu_nmi_exit (void ) +static __always_inline void rcu_nmi_exit_common(bool irq) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks);@@ -807,7 +808,22 @@ void rcu_nmi_exit(void)/* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */ trace_rcu_dyntick (TPS ("Startirq" ), rdtp->dynticks_nmi_nesting,0 , rdtp->dynticks);WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );/* Avoid store tearing. */ + +if (irq) +rcu_prepare_for_idle (); +rcu_dynticks_eqs_enter (); + +if (irq) +rcu_dynticks_task_enter (); +} + +/** +* rcu_nmi_exit - inform RCU of exit from NMI context +*/ +void rcu_nmi_exit (void ) +{ +rcu_nmi_exit_common (false); }/** @@ -831,14 +847,8 @@ void rcu_nmi_exit(void)*/ void rcu_irq_exit (void ) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -lockdep_assert_irqs_disabled (); -if (rdtp->dynticks_nmi_nesting ==1 ) -rcu_prepare_for_idle (); -rcu_nmi_exit (); -if (rdtp->dynticks_nmi_nesting ==0 ) -rcu_dynticks_task_enter (); +rcu_nmi_exit_common (true); }/* @@ -921,7 +931,8 @@ void rcu_user_exit(void)#endif /* CONFIG_NO_HZ_FULL */ /** -* rcu_nmi_enter - inform RCU of entry to NMI context +* rcu_nmi_enter_common - inform RCU of entry to NMI context +* @irq: Is this call from rcu_irq_enter? * * If the CPU was idle from RCU's viewpoint, update rdtp->dynticks and * rdtp->dynticks_nmi_nesting to let the RCU grace-period handling know @@ -929,10 +940,10 @@ void rcu_user_exit(void)* long as the nesting level does not overflow an int. (You will probably * run out of stack space first.) * -* If you add or remove a call to rcu_nmi_enter(), be sure to test +* If you add or remove a call to rcu_nmi_enter_common(), be sure to test * with CONFIG_RCU_EQS_DEBUG=y. */ -void rcu_nmi_enter (void ) +static __always_inline void rcu_nmi_enter_common(bool irq) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks);long incby =2 ;@@ -949,7 +960,15 @@ void rcu_nmi_enter(void)* period (observation due to Andy Lutomirski). */ if (rcu_dynticks_curr_cpu_in_eqs ()) { + +if (irq) +rcu_dynticks_task_exit (); +rcu_dynticks_eqs_exit (); + +if (irq) +rcu_cleanup_after_idle (); + incby =1 ; } trace_rcu_dyntick(incby == 1 ? TPS("Endirq") : TPS("++="),@@ -960,6 +979,14 @@ void rcu_nmi_enter(void)barrier (); } +/** +* rcu_nmi_enter - inform RCU of entry to NMI context +*/ +void rcu_nmi_enter (void ) +{ +rcu_nmi_enter_common (false); +} +/** * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle * @@ -984,14 +1011,8 @@ void rcu_nmi_enter(void)*/ void rcu_irq_enter (void ) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -lockdep_assert_irqs_disabled (); -if (rdtp->dynticks_nmi_nesting ==0 ) -rcu_dynticks_task_exit (); -rcu_nmi_enter (); -if (rdtp->dynticks_nmi_nesting ==1 ) -rcu_cleanup_after_idle (); +rcu_nmi_enter_common (true); }/*
Re: [PATCH tip/core/rcu 01/19] rcu: Refactor rcu_{nmi,irq}_{enter,exit}()
Quoted 12 lines · Steven Rostedt
Quoted 9 lines · On Wed, 29 Aug 2018 15:20:29 -0700 "Paul E. McKenney"…
This commit also changes order of execution from this:
rcu_dynticks_task_exit(); rcu_dynticks_eqs_exit(); trace_rcu_dyntick(); rcu_cleanup_after_idle();To this:
Quoted 8 lines
Quoted 6 lines
rcu_dynticks_task_exit(); rcu_dynticks_eqs_exit(); rcu_cleanup_after_idle(); trace_rcu_dyntick();In other words, the calls to
trace_rcu_dyntick()andtrace_rcu_dyntick()How is
trace_rcu_dyntick()andtrace_rcu_dyntickreversed ? ;-)
Very carefully?
I changed the first trace_rcu_dyntick() to rcu_cleanup_after_idle(), good catch!
Quoted 23 lines
Quoted 23 lines
are reversed. This has no functional effect because the real concern is whether a given call is before or after the call to
rcu_dynticks_eqs_exit(), and this patch does not change that. Before the call torcu_dynticks_eqs_exit(), RCU is not yet watching the current CPU and after that call RCU is watching.A similar switch in calling order happens on the idle-entry path, with similar lack of effect for the same reasons.
Suggested-by Paul E. McKenney <[email protected]>Signed-off-by Byungchul Park <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 61 +++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 20 deletions(-)kernel/rcu/tree.c +0
-0@@ -771,17 +771,18 @@ void rcu_user_enter(void)#endif /* CONFIG_NO_HZ_FULL */
Quoted 16 lines
kernel/rcu/tree.c void rcu_user_enter(void) · line 783
Quoted 13 lines
kernel/rcu/tree.c +3
-2/** -* rcu_nmi_exit - inform RCU of exit from NMI context +* rcu_nmi_exit_common - inform RCU of exit from NMI context +* @irq: Is this call from rcu_irq_exit? * * If we are returning from the outermost NMI handler that interrupted an * RCU-idle period, update rdtp->dynticks and rdtp->dynticks_nmi_nesting * to let the RCU grace-period handling know that the CPU is back to * being RCU-idle. * -* If you add or remove a call to rcu_nmi_exit(), be sure to test +* If you add or remove a call to rcu_nmi_exit_common(), be sure to test * with CONFIG_RCU_EQS_DEBUG=y. As this is a static function, this description doesn't make sense. You need to move the description down to the new
rcu_nmi_exit()below.
Heh! This will give git a chance to show off its conflict-resolution capabilities!!! Let's see how it does...
Not bad! It resolved the conflicts automatically despite the code movement. Nice!!! ;-)
Quoted 3 lines
Other than that...
Reviewed-by Steven Rostedt (VMware) <[email protected]>
Of course my penalty for my lack of faith in git is a second rebase to pull this in. ;-)
Thank you for your review and comments!
Thanx, Paul
Quoted 119 lines
-- Steve
Quoted 115 lines
kernel/rcu/tree.c +38
-18*/ -void rcu_nmi_exit (void ) +static __always_inline void rcu_nmi_exit_common(bool irq) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks);@@ -807,7 +808,22 @@ void rcu_nmi_exit(void)/* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */ trace_rcu_dyntick (TPS ("Startirq" ), rdtp->dynticks_nmi_nesting,0 , rdtp->dynticks);WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );/* Avoid store tearing. */ + +if (irq) +rcu_prepare_for_idle (); +rcu_dynticks_eqs_enter (); + +if (irq) +rcu_dynticks_task_enter (); +} + +/** +* rcu_nmi_exit - inform RCU of exit from NMI context +*/ +void rcu_nmi_exit (void ) +{ +rcu_nmi_exit_common (false); }/** @@ -831,14 +847,8 @@ void rcu_nmi_exit(void)*/ void rcu_irq_exit (void ) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -lockdep_assert_irqs_disabled (); -if (rdtp->dynticks_nmi_nesting ==1 ) -rcu_prepare_for_idle (); -rcu_nmi_exit (); -if (rdtp->dynticks_nmi_nesting ==0 ) -rcu_dynticks_task_enter (); +rcu_nmi_exit_common (true); }/* @@ -921,7 +931,8 @@ void rcu_user_exit(void)#endif /* CONFIG_NO_HZ_FULL */ /** -* rcu_nmi_enter - inform RCU of entry to NMI context +* rcu_nmi_enter_common - inform RCU of entry to NMI context +* @irq: Is this call from rcu_irq_enter? * * If the CPU was idle from RCU's viewpoint, update rdtp->dynticks and * rdtp->dynticks_nmi_nesting to let the RCU grace-period handling know @@ -929,10 +940,10 @@ void rcu_user_exit(void)* long as the nesting level does not overflow an int. (You will probably * run out of stack space first.) * -* If you add or remove a call to rcu_nmi_enter(), be sure to test +* If you add or remove a call to rcu_nmi_enter_common(), be sure to test * with CONFIG_RCU_EQS_DEBUG=y. */ -void rcu_nmi_enter (void ) +static __always_inline void rcu_nmi_enter_common(bool irq) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks);long incby =2 ;@@ -949,7 +960,15 @@ void rcu_nmi_enter(void)* period (observation due to Andy Lutomirski). */ if (rcu_dynticks_curr_cpu_in_eqs ()) { + +if (irq) +rcu_dynticks_task_exit (); +rcu_dynticks_eqs_exit (); + +if (irq) +rcu_cleanup_after_idle (); + incby =1 ; } trace_rcu_dyntick(incby == 1 ? TPS("Endirq") : TPS("++="),@@ -960,6 +979,14 @@ void rcu_nmi_enter(void)barrier (); } +/** +* rcu_nmi_enter - inform RCU of entry to NMI context +*/ +void rcu_nmi_enter (void ) +{ +rcu_nmi_enter_common (false); +} +/** * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle * @@ -984,14 +1011,8 @@ void rcu_nmi_enter(void)*/ void rcu_irq_enter (void ) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -lockdep_assert_irqs_disabled (); -if (rdtp->dynticks_nmi_nesting ==0 ) -rcu_dynticks_task_exit (); -rcu_nmi_enter (); -if (rdtp->dynticks_nmi_nesting ==1 ) -rcu_cleanup_after_idle (); +rcu_nmi_enter_common (true); }/*
Re: [PATCH tip/core/rcu 01/19] rcu: Refactor rcu_{nmi,irq}_{enter,exit}()
In reply to Steven RostedtQuoted 44 lines · Steven Rostedt
Quoted 7 lines · On Wed, 29 Aug 2018 15:20:29 -0700 "Paul E. McKenney"…
This commit also changes order of execution from this:
rcu_dynticks_task_exit(); rcu_dynticks_eqs_exit(); trace_rcu_dyntick(); rcu_cleanup_after_idle();Quoted 8 lines
To this:
rcu_dynticks_task_exit(); rcu_dynticks_eqs_exit(); rcu_cleanup_after_idle(); trace_rcu_dyntick();In other words, the calls to
trace_rcu_dyntick()andtrace_rcu_dyntick()How is
trace_rcu_dyntick()andtrace_rcu_dyntickreversed ? ;-)Quoted 23 lines
are reversed. This has no functional effect because the real concern is whether a given call is before or after the call to
rcu_dynticks_eqs_exit(), and this patch does not change that. Before the call torcu_dynticks_eqs_exit(), RCU is not yet watching the current CPU and after that call RCU is watching.A similar switch in calling order happens on the idle-entry path, with similar lack of effect for the same reasons.
Suggested-by Paul E. McKenney <[email protected]>Signed-off-by Byungchul Park <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>--- kernel/rcu/tree.c | 61 +++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 20 deletions(-)kernel/rcu/tree.c +0
-0@@ -771,17 +771,18 @@ void rcu_user_enter(void)#endif /* CONFIG_NO_HZ_FULL */
Quoted 16 lines
kernel/rcu/tree.c void rcu_user_enter(void) · line 783
Quoted 13 lines
kernel/rcu/tree.c +3
-2/** -* rcu_nmi_exit - inform RCU of exit from NMI context +* rcu_nmi_exit_common - inform RCU of exit from NMI context +* @irq: Is this call from rcu_irq_exit? * * If we are returning from the outermost NMI handler that interrupted an * RCU-idle period, update rdtp->dynticks and rdtp->dynticks_nmi_nesting * to let the RCU grace-period handling know that the CPU is back to * being RCU-idle. * -* If you add or remove a call to rcu_nmi_exit(), be sure to test +* If you add or remove a call to rcu_nmi_exit_common(), be sure to test * with CONFIG_RCU_EQS_DEBUG=y. As this is a static function, this description doesn't make sense. You need to move the description down to the new
rcu_nmi_exit()below.
Right.. I should've done that.
Thanks you Steve.
Byungchul
Quoted 122 lines
Other than that...
Reviewed-by Steven Rostedt (VMware) <[email protected]>-- Steve
Quoted 115 lines
kernel/rcu/tree.c +38
-18*/ -void rcu_nmi_exit (void ) +static __always_inline void rcu_nmi_exit_common(bool irq) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks);@@ -807,7 +808,22 @@ void rcu_nmi_exit(void)/* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */ trace_rcu_dyntick (TPS ("Startirq" ), rdtp->dynticks_nmi_nesting,0 , rdtp->dynticks);WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );/* Avoid store tearing. */ + +if (irq) +rcu_prepare_for_idle (); +rcu_dynticks_eqs_enter (); + +if (irq) +rcu_dynticks_task_enter (); +} + +/** +* rcu_nmi_exit - inform RCU of exit from NMI context +*/ +void rcu_nmi_exit (void ) +{ +rcu_nmi_exit_common (false); }/** @@ -831,14 +847,8 @@ void rcu_nmi_exit(void)*/ void rcu_irq_exit (void ) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -lockdep_assert_irqs_disabled (); -if (rdtp->dynticks_nmi_nesting ==1 ) -rcu_prepare_for_idle (); -rcu_nmi_exit (); -if (rdtp->dynticks_nmi_nesting ==0 ) -rcu_dynticks_task_enter (); +rcu_nmi_exit_common (true); }/* @@ -921,7 +931,8 @@ void rcu_user_exit(void)#endif /* CONFIG_NO_HZ_FULL */ /** -* rcu_nmi_enter - inform RCU of entry to NMI context +* rcu_nmi_enter_common - inform RCU of entry to NMI context +* @irq: Is this call from rcu_irq_enter? * * If the CPU was idle from RCU's viewpoint, update rdtp->dynticks and * rdtp->dynticks_nmi_nesting to let the RCU grace-period handling know @@ -929,10 +940,10 @@ void rcu_user_exit(void)* long as the nesting level does not overflow an int. (You will probably * run out of stack space first.) * -* If you add or remove a call to rcu_nmi_enter(), be sure to test +* If you add or remove a call to rcu_nmi_enter_common(), be sure to test * with CONFIG_RCU_EQS_DEBUG=y. */ -void rcu_nmi_enter (void ) +static __always_inline void rcu_nmi_enter_common(bool irq) {struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks);long incby =2 ;@@ -949,7 +960,15 @@ void rcu_nmi_enter(void)* period (observation due to Andy Lutomirski). */ if (rcu_dynticks_curr_cpu_in_eqs ()) { + +if (irq) +rcu_dynticks_task_exit (); +rcu_dynticks_eqs_exit (); + +if (irq) +rcu_cleanup_after_idle (); + incby =1 ; } trace_rcu_dyntick(incby == 1 ? TPS("Endirq") : TPS("++="),@@ -960,6 +979,14 @@ void rcu_nmi_enter(void)barrier (); } +/** +* rcu_nmi_enter - inform RCU of entry to NMI context +*/ +void rcu_nmi_enter (void ) +{ +rcu_nmi_enter_common (false); +} +/** * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle * @@ -984,14 +1011,8 @@ void rcu_nmi_enter(void)*/ void rcu_irq_enter (void ) { -struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -lockdep_assert_irqs_disabled (); -if (rdtp->dynticks_nmi_nesting ==0 ) -rcu_dynticks_task_exit (); -rcu_nmi_enter (); -if (rdtp->dynticks_nmi_nesting ==1 ) -rcu_cleanup_after_idle (); +rcu_nmi_enter_common (true); }/*
PATCH 2/19 rcu: Defer reporting RCU-preempt quiescent states when disabled
In reply to Paul E. McKenneyThis commit defers reporting of RCU-preempt quiescent states at rcu_read_unlock_special() time when any of interrupts, softirq, or preemption are disabled. These deferred quiescent states are reported at a later RCU_SOFTIRQ, context switch, idle entry, or CPU-hotplug offline operation. Of course, if another RCU read-side critical section has started in the meantime, the reporting of the quiescent state will be further deferred.
This also means that disabling preemption, interrupts, and/or softirqs will act as an RCU-preempt read-side critical section. This is enforced by checking preempt_count() as needed.
Some special cases must be handled on an ad-hoc basis, for example, context switch is a quiescent state even though both the scheduler and do_exit() disable preemption. In these cases, additional calls to rcu_preempt_deferred_qs() override the preemption disabling. Similar logic overrides disabled interrupts in rcu_preempt_check_callbacks() because in this case the quiescent state happened just before the corresponding scheduling-clock interrupt.
In theory, this change lifts a long-standing restriction that required that if interrupts were disabled across a call to rcu_read_unlock() that the matching rcu_read_lock() also be contained within that interrupts-disabled region of code. Because the reporting of the corresponding RCU-preempt quiescent state is now deferred until after interrupts have been enabled, it is no longer possible for this situation to result in deadlocks involving the scheduler's runqueue and priority-inheritance locks. This may allow some code simplification that might reduce interrupt latency a bit. Unfortunately, in practice this would also defer deboosting a low-priority task that had been subjected to RCU priority boosting, so real-time-response considerations might well force this restriction to remain in place.
Because RCU-preempt grace periods are now blocked not only by RCU read-side critical sections, but also by disabling of interrupts, preemption, and softirqs, it will be possible to eliminate RCU-bh and RCU-sched in favor of RCU-preempt in CONFIG_PREEMPT=y kernels. This may require some additional plumbing to provide the network denial-of-service guarantees that have been traditionally provided by RCU-bh. Once these are in place, CONFIG_PREEMPT=n kernels will be able to fold RCU-bh into RCU-sched. This would mean that all kernels would have but one flavor of RCU, which would open the door to significant code cleanup.
Moving to a single flavor of RCU would also have the beneficial effect of reducing the NOCB kthreads by at least a factor of two.
[ paulmck: Apply rcu_read_unlock_special() preempt_count() feedback
from Joel Fernandes. ]
[ paulmck: Adjust rcu_eqs_enter() call to rcu_preempt_deferred_qs() in
response to bug reports from kbuild test robot. ]
[ paulmck: Fix bug located by kbuild test robot involving recursion
via rcu_preempt_deferred_qs(). ]
--- .../RCU/Design/Requirements/Requirements.html | 50 +++--- include/linux/rcutiny.h | 5 + kernel/rcu/tree.c | 9 ++ kernel/rcu/tree.h | 3 + kernel/rcu/tree_exp.h | 71 +++++++-- kernel/rcu/tree_plugin.h | 144 +++++++++++++----- 6 files changed, 205 insertions(+), 77 deletions(-)
Documentation/RCU/Design/Requirements/Requirements.html +24 -26
@@ -2394,30 +2394,9 @@ when invoked from a CPU-hotplug notifier. <p> RCU depends on the scheduler, and the scheduler uses RCU to protect some of its data structures. -This means the scheduler is forbidden from acquiring -the runqueue locks and the priority-inheritance locks -in the middle of an outermost RCU read-side critical section unless either -(1) it releases them before exiting that same -RCU read-side critical section, or -(2) interrupts are disabled across -that entire RCU read-side critical section. -This same prohibition also applies (recursively!) to any lock that is acquired -while holding any lock to which this prohibition applies. -Adhering to this rule prevents preemptible RCU from invoking -<tt>rcu_read_unlock_special()</tt> while either runqueue or -priority-inheritance locks are held, thus avoiding deadlock. - -<p> -Prior to v4.4, it was only necessary to disable preemption across -RCU read-side critical sections that acquired scheduler locks. -In v4.4, expedited grace periods started using IPIs, and these -IPIs could force a <tt>rcu_read_unlock()</tt> to take the slowpath. -Therefore, this expedited-grace-period change required disabling of -interrupts, not just preemption. - -<p> -For RCU's part, the preemptible-RCU <tt>rcu_read_unlock()</tt> -implementation must be written carefully to avoid similar deadlocks. +The preemptible-RCU <tt>rcu_read_unlock()</tt> +implementation must therefore be written carefully to avoid deadlocks +involving the scheduler's runqueue and priority-inheritance locks. In particular, <tt>rcu_read_unlock()</tt> must tolerate an interrupt where the interrupt handler invokes both <tt>rcu_read_lock()</tt> and <tt>rcu_read_unlock()</tt>.
@@ -2426,7 +2405,7 @@ negative nesting levels to avoid destructive recursion via interrupt handler's use of RCU. <p> -This pair of mutual scheduler-RCU requirements came as a +This scheduler-RCU requirement came as a <a href="https://lwn.net/Articles/453002/">complete surprise</a>. <p>
@@ -2437,9 +2416,28 @@ when running context-switch-heavy workloads when built with <tt>CONFIG_NO_HZ_FULL=y</tt> <a href="http://www.rdrop.com/users/paulmck/scalability/paper/BareMetal.2015.01.15b.pdf">did come as a surprise [PDF]</a>. RCU has made good progress towards meeting this requirement, even -for context-switch-have <tt>CONFIG_NO_HZ_FULL=y</tt> workloads, +for context-switch-heavy <tt>CONFIG_NO_HZ_FULL=y</tt> workloads, but there is room for further improvement. +<p> +In the past, it was forbidden to disable interrupts across an +<tt>rcu_read_unlock()</tt> unless that interrupt-disabled region +of code also included the matching <tt>rcu_read_lock()</tt>. +Violating this restriction could result in deadlocks involving the +scheduler's runqueue and priority-inheritance spinlocks. +This restriction was lifted when interrupt-disabled calls to +<tt>rcu_read_unlock()</tt> started deferring the reporting of +the resulting RCU-preempt quiescent state until the end of that +interrupts-disabled region. +This deferred reporting means that the scheduler's runqueue and +priority-inheritance locks cannot be held while reporting an RCU-preempt +quiescent state, which lifts the earlier restriction, at least from +a deadlock perspective. +Unfortunately, real-time systems using RCU priority boosting may +need this restriction to remain in effect because deferred +quiescent-state reporting also defers deboosting, which in turn +degrades real-time latencies. + <h3><a name="Tracing and RCU">Tracing and RCU</a></h3> <p>
include/linux/rcutiny.h +5 -0
@@ -115,6 +115,11 @@ static inline void rcu_irq_exit_irqson(void) { }static inline void rcu_irq_enter_irqson (void ) { }static inline void rcu_irq_exit (void ) { }static inline void exit_rcu (void ) { } +static inline bool rcu_preempt_need_deferred_qs (struct task_struct *t) +{ +return false; +} +static inline void rcu_preempt_deferred_qs (struct task_struct *t) { }#ifdef CONFIG_SRCU void rcu_scheduler_starting (void );#else /* #ifndef CONFIG_SRCU */
kernel/rcu/tree.c +9 -0
@@ -422,6 +422,7 @@ static void rcu_momentary_dyntick_idle(void) special =atomic_add_return (2 *RCU_DYNTICK_CTRL_CTR , &rdtp->dynticks);/* It is illegal to call this from idle state. */ WARN_ON_ONCE (!(special &RCU_DYNTICK_CTRL_CTR )); +rcu_preempt_deferred_qs (current); }/*
@@ -729,6 +730,7 @@ static void rcu_eqs_enter(bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); +rcu_preempt_deferred_qs (current);WRITE_ONCE (rdtp->dynticks_nesting,0 );/* Avoid irq-access tearing. */ rcu_dynticks_eqs_enter ();rcu_dynticks_task_enter ();
@@ -2849,6 +2851,12 @@ __rcu_process_callbacks(struct rcu_state *rsp)WARN_ON_ONCE (!rdp->beenonline); +/* Report any deferred quiescent states if preemption enabled. */ +if (!(preempt_count () &PREEMPT_MASK )) +rcu_preempt_deferred_qs (current); +else if (rcu_preempt_need_deferred_qs (current)) +resched_cpu (rdp->cpu);/* Provoke future context switch. */ +/* Update RCU state based on any recent quiescent states. */ rcu_check_quiescent_state (rsp, rdp);
@@ -3822,6 +3830,7 @@ void rcu_report_dead(unsigned int cpu)rcu_report_exp_rdp (&rcu_sched_state,this_cpu_ptr (rcu_sched_state.rda), true);preempt_enable (); +rcu_preempt_deferred_qs (current);for_each_rcu_flavor (rsp)rcu_cleanup_dying_idle_cpu (cpu, rsp);
kernel/rcu/tree.h +3 -0
@@ -195,6 +195,7 @@ struct rcu_data {bool core_needs_qs;/* Core waits for quiesc state. */ bool beenonline;/* CPU online at least once. */ bool gpwrap;/* Possible ->gp_seq wrap. */ +bool deferred_qs;/* This CPU awaiting a deferred QS? */ struct rcu_node *mynode;/* This CPU's leaf of hierarchy */ unsigned long grpmask;/* Mask to apply to leaf qsmask. */ unsigned long ticks_this_gp;/* The number of scheduling-clock */
@@ -461,6 +462,8 @@ static void rcu_cleanup_after_idle(void);static void rcu_prepare_for_idle (void );static void rcu_idle_count_callbacks_posted (void );static bool rcu_preempt_has_tasks (struct rcu_node *rnp); +static bool rcu_preempt_need_deferred_qs (struct task_struct *t); +static void rcu_preempt_deferred_qs (struct task_struct *t);static void print_cpu_stall_info_begin (void );static void print_cpu_stall_info (struct rcu_state *rsp,int cpu);static void print_cpu_stall_info_end (void );
kernel/rcu/tree_exp.h +55 -16
93 lines not on this page · raw message
kernel/rcu/tree_plugin.h +109 -35
216 lines not on this page · raw message
Signature · 1 line
2.17.1
RE: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Hi Paul and all,
Quoted 65 lines
-----Original Message-----
From: [email protected] [mailto:linux-kernel- [email protected]] On Behalf Of Paul E. McKenney
Sent: Thursday, August 30, 2018 01:21
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Paul E. McKenney <[email protected]>
Subject: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabledThis commit defers reporting of RCU-preempt quiescent states at
rcu_read_unlock_special()time when any of interrupts, softirq, or preemption are disabled. These deferred quiescent states are reported at a laterRCU_SOFTIRQ, context switch, idle entry, or CPU-hotplug offline operation. Of course, if another RCU read-side critical section has started in the meantime, the reporting of the quiescent state will be further deferred.This also means that disabling preemption, interrupts, and/or softirqs will act as an RCU-preempt read-side critical section.
This is enforced by checkingpreempt_count()as needed.Some special cases must be handled on an ad-hoc basis, for example, context switch is a quiescent state even though both the scheduler and
do_exit()disable preemption. In these cases, additional calls torcu_preempt_deferred_qs()override the preemption disabling. Similar logic overrides disabled interrupts inrcu_preempt_check_callbacks()because in this case the quiescent state happened just before the corresponding scheduling-clock interrupt.In theory, this change lifts a long-standing restriction that required that if interrupts were disabled across a call to
rcu_read_unlock()that the matchingrcu_read_lock()also be contained within that interrupts-disabled region of code. Because the reporting of the corresponding RCU-preempt quiescent state is now deferred until after interrupts have been enabled, it is no longer possible for this situation to result in deadlocks involving the scheduler's runqueue and priority-inheritance locks. This may allow some code simplification that might reduce interrupt latency a bit. Unfortunately, in practice this would also defer deboosting a low-priority task that had been subjected to RCU priority boosting, so real-time-response considerations might well force this restriction to remain in place.Because RCU-preempt grace periods are now blocked not only by RCU read- side critical sections, but also by disabling of interrupts, preemption, and softirqs, it will be possible to eliminate RCU-bh and RCU-sched in favor of RCU-preempt in
CONFIG_PREEMPT=ykernels. This may require some additional plumbing to provide the network denial-of-service guarantees that have been traditionally provided by RCU-bh. Once these are in place,CONFIG_PREEMPT=nkernels will be able to fold RCU-bh into RCU-sched. This would mean that all kernels would have but one flavor of RCU, which would open the door to significant code cleanup.Moving to a single flavor of RCU would also have the beneficial effect of reducing the NOCB kthreads by at least a factor of two.
Signed-off-by Paul E. McKenney <[email protected]> [ paulmck:Apply
rcu_read_unlock_special()preempt_count()feedback
from Joel Fernandes. ]
[ paulmck: Adjustrcu_eqs_enter()call torcu_preempt_deferred_qs()in
response to bug reports from kbuild test robot. ] [ paulmck: Fix bug located
by kbuild test robot involving recursion
viarcu_preempt_deferred_qs(). ]
Quoted 9 lines
--- .../RCU/Design/Requirements/Requirements.html | 50 +++--- include/linux/rcutiny.h | 5 + kernel/rcu/tree.c | 9 ++ kernel/rcu/tree.h | 3 + kernel/rcu/tree_exp.h | 71 +++++++-- kernel/rcu/tree_plugin.h | 144 +++++++++++++----- 6 files changed, 205 insertions(+), 77 deletions(-)
We started seeing the trace below in our regression system, after I bisected I found this is the offending commit. This appears immediately on boot.
Please let me know if you need any additional details.
Thanks,
Ran
[2018-10-27 05:53:07] ================================================================================ [2018-10-27 05:53:07] UBSAN: Undefined behaviour in kernel/rcu/tree_plugin.h:620:28
[2018-10-27 05:53:07] signed integer overflow:
[2018-10-27 05:53:07] 0 - -2147483648 cannot be represented in type 'int'
[2018-10-27 05:53:07] CPU: 1 PID: 0 Comm: swapper/1 Not tainted 4.19.0-for-upstream-dbg-2018-10-25_03-10-39-82 #1 [2018-10-27 05:53:07] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014 [2018-10-27 05:53:07] Call Trace:
[2018-10-27 05:53:07] dump_stack+0x9a/0xeb [2018-10-27 05:53:07] ubsan_epilogue+0x9/0x7c [2018-10-27 05:53:07] handle_overflow+0x235/0x278 [2018-10-27 05:53:07] ? __ubsan_handle_negate_overflow+0x1bd/0x1bd [2018-10-27 05:53:07] ? sched_clock_local+0xd4/0x140 [2018-10-27 05:53:07] ? kvm_clock_read+0x14/0x30 [2018-10-27 05:53:07] rcu_preempt_deferred_qs+0x12a/0x150 [2018-10-27 05:53:07] rcu_note_context_switch+0x1b9/0x1ac0 [2018-10-27 05:53:07] __schedule+0x22d/0x1fd0 [2018-10-27 05:53:07] ? pci_mmcfg_check_reserved+0x130/0x130 [2018-10-27 05:53:07] ? sched_set_stop_task+0x330/0x330 [2018-10-27 05:53:07] ? lockdep_hardirqs_on+0x360/0x620 [2018-10-27 05:53:07] schedule_idle+0x45/0x80 [2018-10-27 05:53:07] do_idle+0x23e/0x3e0 [2018-10-27 05:53:07] ? check_flags.part.26+0x440/0x440 [2018-10-27 05:53:07] ? arch_cpu_idle_exit+0x40/0x40 [2018-10-27 05:53:07] ? __wake_up_common+0x156/0x5c0 [2018-10-27 05:53:07] ? _raw_spin_unlock_irqrestore+0x59/0x70 [2018-10-27 05:53:07] cpu_startup_entry+0x19/0x20 [2018-10-27 05:53:07] start_secondary+0x420/0x570 [2018-10-27 05:53:07] ? set_cpu_sibling_map+0x2f90/0x2f90 [2018-10-27 05:53:07] secondary_startup_64+0xa4/0xb0
[2018-10-27 05:53:07] ================================================================================ [2018-10-27 05:53:07] ================================================================================ [2018-10-27 05:53:07] UBSAN: Undefined behaviour in kernel/rcu/tree_plugin.h:624:28
[2018-10-27 05:53:07] signed integer overflow:
[2018-10-27 05:53:07] -2147483648 + -2147483648 cannot be represented in type 'int'
[2018-10-27 05:53:07] CPU: 1 PID: 0 Comm: swapper/1 Not tainted 4.19.0-for-upstream-dbg-2018-10-25_03-10-39-82 #1 [2018-10-27 05:53:07] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014 [2018-10-27 05:53:07] Call Trace:
[2018-10-27 05:53:07] dump_stack+0x9a/0xeb [2018-10-27 05:53:07] ubsan_epilogue+0x9/0x7c [2018-10-27 05:53:07] handle_overflow+0x235/0x278 [2018-10-27 05:53:07] ? __ubsan_handle_negate_overflow+0x1bd/0x1bd [2018-10-27 05:53:07] ? check_flags.part.26+0x440/0x440 [2018-10-27 05:53:07] ? _raw_spin_unlock_irqrestore+0x3c/0x70 [2018-10-27 05:53:07] ? _raw_spin_unlock_irqrestore+0x3c/0x70 [2018-10-27 05:53:07] ? lockdep_hardirqs_off+0x1fd/0x2c0 [2018-10-27 05:53:07] ? kvm_clock_read+0x14/0x30 [2018-10-27 05:53:07] rcu_preempt_deferred_qs+0x145/0x150 [2018-10-27 05:53:07] rcu_note_context_switch+0x1b9/0x1ac0 [2018-10-27 05:53:07] __schedule+0x22d/0x1fd0 [2018-10-27 05:53:07] ? pci_mmcfg_check_reserved+0x130/0x130 [2018-10-27 05:53:07] ? sched_set_stop_task+0x330/0x330 [2018-10-27 05:53:07] ? lockdep_hardirqs_on+0x360/0x620 [2018-10-27 05:53:07] schedule_idle+0x45/0x80 [2018-10-27 05:53:07] do_idle+0x23e/0x3e0 [2018-10-27 05:53:07] ? check_flags.part.26+0x440/0x440 [2018-10-27 05:53:07] ? arch_cpu_idle_exit+0x40/0x40 [2018-10-27 05:53:07] ? __wake_up_common+0x156/0x5c0 [2018-10-27 05:53:07] ? _raw_spin_unlock_irqrestore+0x59/0x70 [2018-10-27 05:53:07] cpu_startup_entry+0x19/0x20 [2018-10-27 05:53:07] start_secondary+0x420/0x570 [2018-10-27 05:53:07] ? set_cpu_sibling_map+0x2f90/0x2f90 [2018-10-27 05:53:07] secondary_startup_64+0xa4/0xb0
[2018-10-27 05:53:07] ================================================================================
Re: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Quoted 77 lines · Ran Rozenstein
Hi Paul and all,
Quoted 65 lines
-----Original Message-----
From: [email protected] [mailto:linux-kernel- [email protected]] On Behalf Of Paul E. McKenney
Sent: Thursday, August 30, 2018 01:21
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Paul E. McKenney <[email protected]>
Subject: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabledThis commit defers reporting of RCU-preempt quiescent states at
rcu_read_unlock_special()time when any of interrupts, softirq, or preemption are disabled. These deferred quiescent states are reported at a laterRCU_SOFTIRQ, context switch, idle entry, or CPU-hotplug offline operation. Of course, if another RCU read-side critical section has started in the meantime, the reporting of the quiescent state will be further deferred.This also means that disabling preemption, interrupts, and/or softirqs will act as an RCU-preempt read-side critical section.
This is enforced by checkingpreempt_count()as needed.Some special cases must be handled on an ad-hoc basis, for example, context switch is a quiescent state even though both the scheduler and
do_exit()disable preemption. In these cases, additional calls torcu_preempt_deferred_qs()override the preemption disabling. Similar logic overrides disabled interrupts inrcu_preempt_check_callbacks()because in this case the quiescent state happened just before the corresponding scheduling-clock interrupt.In theory, this change lifts a long-standing restriction that required that if interrupts were disabled across a call to
rcu_read_unlock()that the matchingrcu_read_lock()also be contained within that interrupts-disabled region of code. Because the reporting of the corresponding RCU-preempt quiescent state is now deferred until after interrupts have been enabled, it is no longer possible for this situation to result in deadlocks involving the scheduler's runqueue and priority-inheritance locks. This may allow some code simplification that might reduce interrupt latency a bit. Unfortunately, in practice this would also defer deboosting a low-priority task that had been subjected to RCU priority boosting, so real-time-response considerations might well force this restriction to remain in place.Because RCU-preempt grace periods are now blocked not only by RCU read- side critical sections, but also by disabling of interrupts, preemption, and softirqs, it will be possible to eliminate RCU-bh and RCU-sched in favor of RCU-preempt in
CONFIG_PREEMPT=ykernels. This may require some additional plumbing to provide the network denial-of-service guarantees that have been traditionally provided by RCU-bh. Once these are in place,CONFIG_PREEMPT=nkernels will be able to fold RCU-bh into RCU-sched. This would mean that all kernels would have but one flavor of RCU, which would open the door to significant code cleanup.Moving to a single flavor of RCU would also have the beneficial effect of reducing the NOCB kthreads by at least a factor of two.
Signed-off-by Paul E. McKenney <[email protected]> [ paulmck:Apply
rcu_read_unlock_special()preempt_count()feedback
from Joel Fernandes. ]
[ paulmck: Adjustrcu_eqs_enter()call torcu_preempt_deferred_qs()in
response to bug reports from kbuild test robot. ] [ paulmck: Fix bug located
by kbuild test robot involving recursion
viarcu_preempt_deferred_qs(). ]Quoted 9 lines
--- .../RCU/Design/Requirements/Requirements.html | 50 +++--- include/linux/rcutiny.h | 5 + kernel/rcu/tree.c | 9 ++ kernel/rcu/tree.h | 3 + kernel/rcu/tree_exp.h | 71 +++++++-- kernel/rcu/tree_plugin.h | 144 +++++++++++++----- 6 files changed, 205 insertions(+), 77 deletions(-)
Quoted 3 lines
We started seeing the trace below in our regression system, after I bisected I found this is the offending commit. This appears immediately on boot.
Please let me know if you need any additional details.
Interesting. Here is the offending function:
static void rcu_preempt_deferred_qs(struct task_struct *t)
{
unsigned long flags;
bool couldrecurse = t->rcu_read_lock_nesting >= 0;
if (!rcu_preempt_need_deferred_qs(t))
return;
if (couldrecurse)
t->rcu_read_lock_nesting -= INT_MIN;
local_irq_save(flags);
rcu_preempt_deferred_qs_irqrestore(t, flags);
if (couldrecurse)
t->rcu_read_lock_nesting += INT_MIN;
}Using twos-complement arithmetic (which the kernel build gcc arguments enforce, last I checked) this does work. But as UBSAN says, subtracting INT_MIN is unconditionally undefined behavior according to the C standard.
Good catch!!!
So how do I make the above code not simply function, but rather meet the C standard?
One approach to add INT_MIN going in, then add INT_MAX and then add 1 coming out.
Another approach is to sacrifice the INT_MAX value (should be plenty safe), thus subtract INT_MAX going in and add INT_MAX coming out. For consistency, I suppose that I should change the INT_MIN in __rcu_read_unlock() to -INT_MAX.
I could also leave __rcu_read_unlock() alone and XOR the top bit of t->rcu_read_lock_nesting on entry and exit to/from rcu_preempt_deferred_qs().
Sacrificing the INT_MIN value seems most maintainable, as in the following patch. Thoughts?
Thanx, Paul
------------------------------------------------------------------------
kernel/rcu/tree_plugin.h +3 -3
@@ -424,7 +424,7 @@ void __rcu_read_unlock(void) --t->rcu_read_lock_nesting; }else {barrier ();/* critical section before exit code. */ - t->rcu_read_lock_nesting =INT_MIN ; + t->rcu_read_lock_nesting = -INT_MAX ;barrier ();/* assign before ->rcu_read_unlock_special load */ if (unlikely (READ_ONCE (t->rcu_read_unlock_special.s)))rcu_read_unlock_special (t);
@@ -617,11 +617,11 @@ static void rcu_preempt_deferred_qs(struct task_struct *t)if (!rcu_preempt_need_deferred_qs (t))return ;if (couldrecurse) - t->rcu_read_lock_nesting -=INT_MIN ; + t->rcu_read_lock_nesting -=INT_MAX ;local_irq_save (flags);rcu_preempt_deferred_qs_irqrestore (t, flags);if (couldrecurse) - t->rcu_read_lock_nesting +=INT_MIN ; + t->rcu_read_lock_nesting +=INT_MAX ; }/*
Re: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Quoted 116 lines · Paul E. McKenney
Quoted 77 lines · Ran Rozenstein
Hi Paul and all,
Quoted 65 lines
-----Original Message-----
From: [email protected] [mailto:linux-kernel- [email protected]] On Behalf Of Paul E. McKenney
Sent: Thursday, August 30, 2018 01:21
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Paul E. McKenney <[email protected]>
Subject: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabledThis commit defers reporting of RCU-preempt quiescent states at
rcu_read_unlock_special()time when any of interrupts, softirq, or preemption are disabled. These deferred quiescent states are reported at a laterRCU_SOFTIRQ, context switch, idle entry, or CPU-hotplug offline operation. Of course, if another RCU read-side critical section has started in the meantime, the reporting of the quiescent state will be further deferred.This also means that disabling preemption, interrupts, and/or softirqs will act as an RCU-preempt read-side critical section.
This is enforced by checkingpreempt_count()as needed.Some special cases must be handled on an ad-hoc basis, for example, context switch is a quiescent state even though both the scheduler and
do_exit()disable preemption. In these cases, additional calls torcu_preempt_deferred_qs()override the preemption disabling. Similar logic overrides disabled interrupts inrcu_preempt_check_callbacks()because in this case the quiescent state happened just before the corresponding scheduling-clock interrupt.In theory, this change lifts a long-standing restriction that required that if interrupts were disabled across a call to
rcu_read_unlock()that the matchingrcu_read_lock()also be contained within that interrupts-disabled region of code. Because the reporting of the corresponding RCU-preempt quiescent state is now deferred until after interrupts have been enabled, it is no longer possible for this situation to result in deadlocks involving the scheduler's runqueue and priority-inheritance locks. This may allow some code simplification that might reduce interrupt latency a bit. Unfortunately, in practice this would also defer deboosting a low-priority task that had been subjected to RCU priority boosting, so real-time-response considerations might well force this restriction to remain in place.Because RCU-preempt grace periods are now blocked not only by RCU read- side critical sections, but also by disabling of interrupts, preemption, and softirqs, it will be possible to eliminate RCU-bh and RCU-sched in favor of RCU-preempt in
CONFIG_PREEMPT=ykernels. This may require some additional plumbing to provide the network denial-of-service guarantees that have been traditionally provided by RCU-bh. Once these are in place,CONFIG_PREEMPT=nkernels will be able to fold RCU-bh into RCU-sched. This would mean that all kernels would have but one flavor of RCU, which would open the door to significant code cleanup.Moving to a single flavor of RCU would also have the beneficial effect of reducing the NOCB kthreads by at least a factor of two.
Signed-off-by Paul E. McKenney <[email protected]> [ paulmck:Apply
rcu_read_unlock_special()preempt_count()feedback
from Joel Fernandes. ]
[ paulmck: Adjustrcu_eqs_enter()call torcu_preempt_deferred_qs()in
response to bug reports from kbuild test robot. ] [ paulmck: Fix bug located
by kbuild test robot involving recursion
viarcu_preempt_deferred_qs(). ]Quoted 9 lines
--- .../RCU/Design/Requirements/Requirements.html | 50 +++--- include/linux/rcutiny.h | 5 + kernel/rcu/tree.c | 9 ++ kernel/rcu/tree.h | 3 + kernel/rcu/tree_exp.h | 71 +++++++-- kernel/rcu/tree_plugin.h | 144 +++++++++++++----- 6 files changed, 205 insertions(+), 77 deletions(-)Quoted 3 lines
We started seeing the trace below in our regression system, after I bisected I found this is the offending commit. This appears immediately on boot.
Please let me know if you need any additional details.Interesting. Here is the offending function:
static void rcu_preempt_deferred_qs(struct task_struct *t) { unsigned long flags; bool couldrecurse = t->rcu_read_lock_nesting >= 0; if (!rcu_preempt_need_deferred_qs(t)) return; if (couldrecurse) t->rcu_read_lock_nesting -= INT_MIN; local_irq_save(flags); rcu_preempt_deferred_qs_irqrestore(t, flags); if (couldrecurse) t->rcu_read_lock_nesting += INT_MIN; }Using twos-complement arithmetic (which the kernel build gcc arguments enforce, last I checked) this does work. But as UBSAN says, subtracting
INT_MINis unconditionally undefined behavior according to the C standard.Good catch!!!
So how do I make the above code not simply function, but rather meet the C standard?
One approach to add
INT_MINgoing in, then addINT_MAXand then add 1 coming out.Another approach is to sacrifice the
INT_MAXvalue (should be plenty safe), thus subtractINT_MAXgoing in and addINT_MAXcoming out. For consistency, I suppose that I should change theINT_MINin__rcu_read_unlock()to -INT_MAX.
Quoted 6 lines
I could also leave
__rcu_read_unlock()alone and XOR the top bit oft->rcu_read_lock_nestingon entry and exit to/fromrcu_preempt_deferred_qs().Sacrificing the
INT_MINvalue seems most maintainable, as in the following patch. Thoughts?
The INT_MAX naming could be very confusing for nesting levels, could we not instead just define something like:
#define RCU_NESTING_MIN (INT_MIN - 1)
#define RCU_NESTING_MAX (INT_MAX)
and just use that? also one more comment below:
Quoted 17 lines
------------------------------------------------------------------------
kernel/rcu/tree_plugin.h +1
-1@@ -424,7 +424,7 @@ void __rcu_read_unlock(void) --t->rcu_read_lock_nesting; }else {barrier ();/* critical section before exit code. */ - t->rcu_read_lock_nesting =INT_MIN ; + t->rcu_read_lock_nesting = -INT_MAX ;barrier ();/* assign before ->rcu_read_unlock_special load */ if (unlikely (READ_ONCE (t->rcu_read_unlock_special.s)))rcu_read_unlock_special (t);
kernel/rcu/tree_plugin.h static void rcu_preempt_deferred_qs(struct task_struct *t) · line 620
Quoted 5 lines
kernel/rcu/tree_plugin.h +1
-1@@ -617,11 +617,11 @@ static void rcu_preempt_deferred_qs(struct task_struct *t)if (!rcu_preempt_need_deferred_qs (t))return ;if (couldrecurse) - t->rcu_read_lock_nesting -=INT_MIN ; + t->rcu_read_lock_nesting -=INT_MAX ;
Shouldn't this be: t->rcu_read_lock_nesting -= -INT_MAX; ?
kernel/rcu/tree_plugin.h static void rcu_preempt_deferred_qs(struct task_struct *t) · line 624
Quoted 5 lines
kernel/rcu/tree_plugin.h +1
-1local_irq_save (flags);rcu_preempt_deferred_qs_irqrestore (t, flags);if (couldrecurse) - t->rcu_read_lock_nesting +=INT_MIN ; + t->rcu_read_lock_nesting +=INT_MAX ;
And t->rcu_read_lock_nesting += -INT_MAX; ?
But apologies if I missed something, thanks,
- Joel
Re: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Quoted 124 lines · Joel Fernandes
Quoted 116 lines · Paul E. McKenney
Quoted 77 lines · Ran Rozenstein
Hi Paul and all,
Quoted 65 lines
-----Original Message-----
From: [email protected] [mailto:linux-kernel- [email protected]] On Behalf Of Paul E. McKenney
Sent: Thursday, August 30, 2018 01:21
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Paul E. McKenney <[email protected]>
Subject: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabledThis commit defers reporting of RCU-preempt quiescent states at
rcu_read_unlock_special()time when any of interrupts, softirq, or preemption are disabled. These deferred quiescent states are reported at a laterRCU_SOFTIRQ, context switch, idle entry, or CPU-hotplug offline operation. Of course, if another RCU read-side critical section has started in the meantime, the reporting of the quiescent state will be further deferred.This also means that disabling preemption, interrupts, and/or softirqs will act as an RCU-preempt read-side critical section.
This is enforced by checkingpreempt_count()as needed.Some special cases must be handled on an ad-hoc basis, for example, context switch is a quiescent state even though both the scheduler and
do_exit()disable preemption. In these cases, additional calls torcu_preempt_deferred_qs()override the preemption disabling. Similar logic overrides disabled interrupts inrcu_preempt_check_callbacks()because in this case the quiescent state happened just before the corresponding scheduling-clock interrupt.In theory, this change lifts a long-standing restriction that required that if interrupts were disabled across a call to
rcu_read_unlock()that the matchingrcu_read_lock()also be contained within that interrupts-disabled region of code. Because the reporting of the corresponding RCU-preempt quiescent state is now deferred until after interrupts have been enabled, it is no longer possible for this situation to result in deadlocks involving the scheduler's runqueue and priority-inheritance locks. This may allow some code simplification that might reduce interrupt latency a bit. Unfortunately, in practice this would also defer deboosting a low-priority task that had been subjected to RCU priority boosting, so real-time-response considerations might well force this restriction to remain in place.Because RCU-preempt grace periods are now blocked not only by RCU read- side critical sections, but also by disabling of interrupts, preemption, and softirqs, it will be possible to eliminate RCU-bh and RCU-sched in favor of RCU-preempt in
CONFIG_PREEMPT=ykernels. This may require some additional plumbing to provide the network denial-of-service guarantees that have been traditionally provided by RCU-bh. Once these are in place,CONFIG_PREEMPT=nkernels will be able to fold RCU-bh into RCU-sched. This would mean that all kernels would have but one flavor of RCU, which would open the door to significant code cleanup.Moving to a single flavor of RCU would also have the beneficial effect of reducing the NOCB kthreads by at least a factor of two.
Signed-off-by Paul E. McKenney <[email protected]> [ paulmck:Apply
rcu_read_unlock_special()preempt_count()feedback
from Joel Fernandes. ]
[ paulmck: Adjustrcu_eqs_enter()call torcu_preempt_deferred_qs()in
response to bug reports from kbuild test robot. ] [ paulmck: Fix bug located
by kbuild test robot involving recursion
viarcu_preempt_deferred_qs(). ]Quoted 9 lines
--- .../RCU/Design/Requirements/Requirements.html | 50 +++--- include/linux/rcutiny.h | 5 + kernel/rcu/tree.c | 9 ++ kernel/rcu/tree.h | 3 + kernel/rcu/tree_exp.h | 71 +++++++-- kernel/rcu/tree_plugin.h | 144 +++++++++++++----- 6 files changed, 205 insertions(+), 77 deletions(-)Quoted 3 lines
We started seeing the trace below in our regression system, after I bisected I found this is the offending commit. This appears immediately on boot.
Please let me know if you need any additional details.Interesting. Here is the offending function:
static void rcu_preempt_deferred_qs(struct task_struct *t) { unsigned long flags; bool couldrecurse = t->rcu_read_lock_nesting >= 0; if (!rcu_preempt_need_deferred_qs(t)) return; if (couldrecurse) t->rcu_read_lock_nesting -= INT_MIN; local_irq_save(flags); rcu_preempt_deferred_qs_irqrestore(t, flags); if (couldrecurse) t->rcu_read_lock_nesting += INT_MIN; }Using twos-complement arithmetic (which the kernel build gcc arguments enforce, last I checked) this does work. But as UBSAN says, subtracting
INT_MINis unconditionally undefined behavior according to the C standard.Good catch!!!
So how do I make the above code not simply function, but rather meet the C standard?
One approach to add
INT_MINgoing in, then addINT_MAXand then add 1 coming out.Another approach is to sacrifice the
INT_MAXvalue (should be plenty safe), thus subtractINT_MAXgoing in and addINT_MAXcoming out. For consistency, I suppose that I should change theINT_MINin__rcu_read_unlock()to -INT_MAX.Quoted 6 lines
I could also leave
__rcu_read_unlock()alone and XOR the top bit oft->rcu_read_lock_nestingon entry and exit to/fromrcu_preempt_deferred_qs().Sacrificing the
INT_MINvalue seems most maintainable, as in the following patch. Thoughts?
Quoted 6 lines
The
INT_MAXnaming could be very confusing for nesting levels, could we not instead just define something like:
#defineRCU_NESTING_MIN(INT_MIN- 1)
#defineRCU_NESTING_MAX(INT_MAX)and just use that? also one more comment below:
Hmmm... There is currently no use for RCU_NESTING_MAX, but if the check at the end of __rcu_read_unlock() were to be extended to check for too-deep positive nesting, it would need to check for something like INT_MAX/2. You could of course argue that the current check against INT_MIN/2 should instead be against -INT_MAX/2, but there really isn't much difference between the two.
Another approach would be to convert to unsigned in order to avoid the overflow problem completely.
For the moment, anyway, I am inclined to leave it as is.
Quoted 15 lines
Quoted 15 lines
------------------------------------------------------------------------
kernel/rcu/tree_plugin.h +1
-1@@ -424,7 +424,7 @@ void __rcu_read_unlock(void) --t->rcu_read_lock_nesting; }else {barrier ();/* critical section before exit code. */ - t->rcu_read_lock_nesting =INT_MIN ; + t->rcu_read_lock_nesting = -INT_MAX ;barrier ();/* assign before ->rcu_read_unlock_special load */ if (unlikely (READ_ONCE (t->rcu_read_unlock_special.s)))rcu_read_unlock_special (t);
Quoted 8 lines
kernel/rcu/tree_plugin.h static void rcu_preempt_deferred_qs(struct task_struct *t) · line 620
Quoted 6 lines
kernel/rcu/tree_plugin.h +1
-1@@ -617,11 +617,11 @@ static void rcu_preempt_deferred_qs(struct task_struct *t)if (!rcu_preempt_need_deferred_qs (t))return ;if (couldrecurse) - t->rcu_read_lock_nesting -=INT_MIN ; + t->rcu_read_lock_nesting -=INT_MAX ;Shouldn't this be:
t->rcu_read_lock_nesting-= -INT_MAX; ?
Suppose t->rcu_read_lock_nesting is zero. Then you change would leave the value a large positive number (INT_MAX, to be precise), which would then result in signed integer overflow if there was a nested rcu_read_lock(). Worse yet, if t->rcu_read_lock_nesting is any positive number, subtracting -INT_MAX would immediately result in signed integer overflow. Please note that the whole point of this patch in the first place is to avoid signed integer overflow.
Note that the check at the beginnning of this function never sets couldrecurse if ->rcu_read_lock_nesting is negative, which avoids the possibility of overflow given the current code. Unless you have two billion nested rcu_read_lock() calls, in which case you broke it, so you get to buy it. ;-)
Quoted 7 lines
kernel/rcu/tree_plugin.h static void rcu_preempt_deferred_qs(struct task_struct *t) · line 624
Quoted 5 lines
kernel/rcu/tree_plugin.h +1
-1local_irq_save (flags);rcu_preempt_deferred_qs_irqrestore (t, flags);if (couldrecurse) - t->rcu_read_lock_nesting +=INT_MIN ; + t->rcu_read_lock_nesting +=INT_MAX ;And
t->rcu_read_lock_nesting+= -INT_MAX; ?
And this would have similar problems for ->rcu_read_lock_nesting not equal to zero on entry to this function.
Quoted 1 line
But apologies if I missed something, thanks,
No need to apologies, especially if it turns out that I am the one who is confused. Either way, thank you for looking this over!
Thanx, Paul
Re: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Quoted 139 lines · Paul E. McKenney
Quoted 124 lines · Joel Fernandes
Quoted 116 lines · Paul E. McKenney
Quoted 77 lines · Ran Rozenstein
Hi Paul and all,
Quoted 65 lines
-----Original Message-----
From: [email protected] [mailto:linux-kernel- [email protected]] On Behalf Of Paul E. McKenney
Sent: Thursday, August 30, 2018 01:21
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Paul E. McKenney <[email protected]>
Subject: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabledThis commit defers reporting of RCU-preempt quiescent states at
rcu_read_unlock_special()time when any of interrupts, softirq, or preemption are disabled. These deferred quiescent states are reported at a laterRCU_SOFTIRQ, context switch, idle entry, or CPU-hotplug offline operation. Of course, if another RCU read-side critical section has started in the meantime, the reporting of the quiescent state will be further deferred.This also means that disabling preemption, interrupts, and/or softirqs will act as an RCU-preempt read-side critical section.
This is enforced by checkingpreempt_count()as needed.Some special cases must be handled on an ad-hoc basis, for example, context switch is a quiescent state even though both the scheduler and
do_exit()disable preemption. In these cases, additional calls torcu_preempt_deferred_qs()override the preemption disabling. Similar logic overrides disabled interrupts inrcu_preempt_check_callbacks()because in this case the quiescent state happened just before the corresponding scheduling-clock interrupt.In theory, this change lifts a long-standing restriction that required that if interrupts were disabled across a call to
rcu_read_unlock()that the matchingrcu_read_lock()also be contained within that interrupts-disabled region of code. Because the reporting of the corresponding RCU-preempt quiescent state is now deferred until after interrupts have been enabled, it is no longer possible for this situation to result in deadlocks involving the scheduler's runqueue and priority-inheritance locks. This may allow some code simplification that might reduce interrupt latency a bit. Unfortunately, in practice this would also defer deboosting a low-priority task that had been subjected to RCU priority boosting, so real-time-response considerations might well force this restriction to remain in place.Because RCU-preempt grace periods are now blocked not only by RCU read- side critical sections, but also by disabling of interrupts, preemption, and softirqs, it will be possible to eliminate RCU-bh and RCU-sched in favor of RCU-preempt in
CONFIG_PREEMPT=ykernels. This may require some additional plumbing to provide the network denial-of-service guarantees that have been traditionally provided by RCU-bh. Once these are in place,CONFIG_PREEMPT=nkernels will be able to fold RCU-bh into RCU-sched. This would mean that all kernels would have but one flavor of RCU, which would open the door to significant code cleanup.Moving to a single flavor of RCU would also have the beneficial effect of reducing the NOCB kthreads by at least a factor of two.
Signed-off-by Paul E. McKenney <[email protected]> [ paulmck:Apply
rcu_read_unlock_special()preempt_count()feedback
from Joel Fernandes. ]
[ paulmck: Adjustrcu_eqs_enter()call torcu_preempt_deferred_qs()in
response to bug reports from kbuild test robot. ] [ paulmck: Fix bug located
by kbuild test robot involving recursion
viarcu_preempt_deferred_qs(). ]Quoted 9 lines
--- .../RCU/Design/Requirements/Requirements.html | 50 +++--- include/linux/rcutiny.h | 5 + kernel/rcu/tree.c | 9 ++ kernel/rcu/tree.h | 3 + kernel/rcu/tree_exp.h | 71 +++++++-- kernel/rcu/tree_plugin.h | 144 +++++++++++++----- 6 files changed, 205 insertions(+), 77 deletions(-)Quoted 3 lines
We started seeing the trace below in our regression system, after I bisected I found this is the offending commit. This appears immediately on boot.
Please let me know if you need any additional details.Interesting. Here is the offending function:
static void rcu_preempt_deferred_qs(struct task_struct *t) { unsigned long flags; bool couldrecurse = t->rcu_read_lock_nesting >= 0; if (!rcu_preempt_need_deferred_qs(t)) return; if (couldrecurse) t->rcu_read_lock_nesting -= INT_MIN; local_irq_save(flags); rcu_preempt_deferred_qs_irqrestore(t, flags); if (couldrecurse) t->rcu_read_lock_nesting += INT_MIN; }Using twos-complement arithmetic (which the kernel build gcc arguments enforce, last I checked) this does work. But as UBSAN says, subtracting
INT_MINis unconditionally undefined behavior according to the C standard.Good catch!!!
So how do I make the above code not simply function, but rather meet the C standard?
One approach to add
INT_MINgoing in, then addINT_MAXand then add 1 coming out.Another approach is to sacrifice the
INT_MAXvalue (should be plenty safe), thus subtractINT_MAXgoing in and addINT_MAXcoming out. For consistency, I suppose that I should change theINT_MINin__rcu_read_unlock()to -INT_MAX.Quoted 6 lines
I could also leave
__rcu_read_unlock()alone and XOR the top bit oft->rcu_read_lock_nestingon entry and exit to/fromrcu_preempt_deferred_qs().Sacrificing the
INT_MINvalue seems most maintainable, as in the following patch. Thoughts?Quoted 6 lines
The
INT_MAXnaming could be very confusing for nesting levels, could we not instead just define something like:
#defineRCU_NESTING_MIN(INT_MIN- 1)
#defineRCU_NESTING_MAX(INT_MAX)and just use that? also one more comment below:
Hmmm... There is currently no use for
RCU_NESTING_MAX, but if the check at the end of__rcu_read_unlock()were to be extended to check for too-deep positive nesting, it would need to check for something like INT_MAX/2. You could of course argue that the current check against INT_MIN/2 should instead be against -INT_MAX/2, but there really isn't much difference between the two.
Quoted 4 lines
Another approach would be to convert to unsigned in order to avoid the overflow problem completely.
For the moment, anyway, I am inclined to leave it as is.
Both the unsigned and INT_MIN/2 options sound good to me, but if you want leave it as is, that would be fine as well. thanks,
- Joel
Re: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Quoted 140 lines · Joel Fernandes
Quoted 139 lines · Paul E. McKenney
Quoted 124 lines · Joel Fernandes
Quoted 116 lines · Paul E. McKenney
Quoted 77 lines · Ran Rozenstein
Hi Paul and all,
Quoted 65 lines
-----Original Message-----
From: [email protected] [mailto:linux-kernel- [email protected]] On Behalf Of Paul E. McKenney
Sent: Thursday, August 30, 2018 01:21
To: [email protected]
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Paul E. McKenney <[email protected]>
Subject: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabledThis commit defers reporting of RCU-preempt quiescent states at
rcu_read_unlock_special()time when any of interrupts, softirq, or preemption are disabled. These deferred quiescent states are reported at a laterRCU_SOFTIRQ, context switch, idle entry, or CPU-hotplug offline operation. Of course, if another RCU read-side critical section has started in the meantime, the reporting of the quiescent state will be further deferred.This also means that disabling preemption, interrupts, and/or softirqs will act as an RCU-preempt read-side critical section.
This is enforced by checkingpreempt_count()as needed.Some special cases must be handled on an ad-hoc basis, for example, context switch is a quiescent state even though both the scheduler and
do_exit()disable preemption. In these cases, additional calls torcu_preempt_deferred_qs()override the preemption disabling. Similar logic overrides disabled interrupts inrcu_preempt_check_callbacks()because in this case the quiescent state happened just before the corresponding scheduling-clock interrupt.In theory, this change lifts a long-standing restriction that required that if interrupts were disabled across a call to
rcu_read_unlock()that the matchingrcu_read_lock()also be contained within that interrupts-disabled region of code. Because the reporting of the corresponding RCU-preempt quiescent state is now deferred until after interrupts have been enabled, it is no longer possible for this situation to result in deadlocks involving the scheduler's runqueue and priority-inheritance locks. This may allow some code simplification that might reduce interrupt latency a bit. Unfortunately, in practice this would also defer deboosting a low-priority task that had been subjected to RCU priority boosting, so real-time-response considerations might well force this restriction to remain in place.Because RCU-preempt grace periods are now blocked not only by RCU read- side critical sections, but also by disabling of interrupts, preemption, and softirqs, it will be possible to eliminate RCU-bh and RCU-sched in favor of RCU-preempt in
CONFIG_PREEMPT=ykernels. This may require some additional plumbing to provide the network denial-of-service guarantees that have been traditionally provided by RCU-bh. Once these are in place,CONFIG_PREEMPT=nkernels will be able to fold RCU-bh into RCU-sched. This would mean that all kernels would have but one flavor of RCU, which would open the door to significant code cleanup.Moving to a single flavor of RCU would also have the beneficial effect of reducing the NOCB kthreads by at least a factor of two.
Signed-off-by Paul E. McKenney <[email protected]> [ paulmck:Apply
rcu_read_unlock_special()preempt_count()feedback
from Joel Fernandes. ]
[ paulmck: Adjustrcu_eqs_enter()call torcu_preempt_deferred_qs()in
response to bug reports from kbuild test robot. ] [ paulmck: Fix bug located
by kbuild test robot involving recursion
viarcu_preempt_deferred_qs(). ]Quoted 9 lines
--- .../RCU/Design/Requirements/Requirements.html | 50 +++--- include/linux/rcutiny.h | 5 + kernel/rcu/tree.c | 9 ++ kernel/rcu/tree.h | 3 + kernel/rcu/tree_exp.h | 71 +++++++-- kernel/rcu/tree_plugin.h | 144 +++++++++++++----- 6 files changed, 205 insertions(+), 77 deletions(-)Quoted 3 lines
We started seeing the trace below in our regression system, after I bisected I found this is the offending commit. This appears immediately on boot.
Please let me know if you need any additional details.Interesting. Here is the offending function:
static void rcu_preempt_deferred_qs(struct task_struct *t) { unsigned long flags; bool couldrecurse = t->rcu_read_lock_nesting >= 0; if (!rcu_preempt_need_deferred_qs(t)) return; if (couldrecurse) t->rcu_read_lock_nesting -= INT_MIN; local_irq_save(flags); rcu_preempt_deferred_qs_irqrestore(t, flags); if (couldrecurse) t->rcu_read_lock_nesting += INT_MIN; }Using twos-complement arithmetic (which the kernel build gcc arguments enforce, last I checked) this does work. But as UBSAN says, subtracting
INT_MINis unconditionally undefined behavior according to the C standard.Good catch!!!
So how do I make the above code not simply function, but rather meet the C standard?
One approach to add
INT_MINgoing in, then addINT_MAXand then add 1 coming out.Another approach is to sacrifice the
INT_MAXvalue (should be plenty safe), thus subtractINT_MAXgoing in and addINT_MAXcoming out. For consistency, I suppose that I should change theINT_MINin__rcu_read_unlock()to -INT_MAX.Quoted 6 lines
I could also leave
__rcu_read_unlock()alone and XOR the top bit oft->rcu_read_lock_nestingon entry and exit to/fromrcu_preempt_deferred_qs().Sacrificing the
INT_MINvalue seems most maintainable, as in the following patch. Thoughts?Quoted 6 lines
The
INT_MAXnaming could be very confusing for nesting levels, could we not instead just define something like:
#defineRCU_NESTING_MIN(INT_MIN- 1)
#defineRCU_NESTING_MAX(INT_MAX)and just use that? also one more comment below:
Hmmm... There is currently no use for
RCU_NESTING_MAX, but if the check at the end of__rcu_read_unlock()were to be extended to check for too-deep positive nesting, it would need to check for something like INT_MAX/2. You could of course argue that the current check against INT_MIN/2 should instead be against -INT_MAX/2, but there really isn't much difference between the two.
Quoted 7 lines
Quoted 4 lines
Another approach would be to convert to unsigned in order to avoid the overflow problem completely.
For the moment, anyway, I am inclined to leave it as is.
Both the unsigned and INT_MIN/2 options sound good to me, but if you want leave it as is, that would be fine as well. thanks,
One approach would be something like this:
#define RCU_READ_LOCK_BIAS (INT_MAX)
#define RCU_READ_LOCK_NMAX (-INT_MAX)
#define RCU_READ_LOCK_PMAX INT_MAX
Then _rcu_read_unlock() would set ->rcu_read_lock_nesting to -RCU_READ_LOCK_BIAS, and compare against RCU_READ_LOCK_NMAX. The comparison against RCU_READ_LOCK_PMAX would preferably take place just after the increment in __rcu_read_lock(), again only under CONFIG_PROVE_RCU.
rcu_preempt_deferred_qs() would then subtract then add RCU_READ_LOCK_BIAS.
Thoughts?
Thanx, Paul
Re: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Quoted 4 lines · Paul E. McKenney
Quoted 3 lines · Joel Fernandes
Quoted 2 lines · Paul E. McKenney
Quoted 1 line · Joel Fernandes
On Mon, Oct 29, 2018 at 07:27:35AM -0700, Paul E. McKenney wrote:
[ . . . ]
Quoted 34 lines
Quoted 14 lines
Quoted 14 lines
Quoted 6 lines
The
INT_MAXnaming could be very confusing for nesting levels, could we not instead just define something like:
#defineRCU_NESTING_MIN(INT_MIN- 1)
#defineRCU_NESTING_MAX(INT_MAX)and just use that? also one more comment below:
Hmmm... There is currently no use for
RCU_NESTING_MAX, but if the check at the end of__rcu_read_unlock()were to be extended to check for too-deep positive nesting, it would need to check for something like INT_MAX/2. You could of course argue that the current check against INT_MIN/2 should instead be against -INT_MAX/2, but there really isn't much difference between the two.Quoted 7 lines
Quoted 4 lines
Another approach would be to convert to unsigned in order to avoid the overflow problem completely.
For the moment, anyway, I am inclined to leave it as is.
Both the unsigned and INT_MIN/2 options sound good to me, but if you want leave it as is, that would be fine as well. thanks,
One approach would be something like this:
#define
RCU_READ_LOCK_BIAS(INT_MAX)
#defineRCU_READ_LOCK_NMAX(-INT_MAX)
#defineRCU_READ_LOCK_PMAXINT_MAXThen
_rcu_read_unlock()would set ->rcu_read_lock_nesting to -RCU_READ_LOCK_BIAS, and compare againstRCU_READ_LOCK_NMAX. The comparison againstRCU_READ_LOCK_PMAXwould preferably take place just after the increment in__rcu_read_lock(), again only underCONFIG_PROVE_RCU.
Quoted 3 lines
rcu_preempt_deferred_qs()would then subtract then addRCU_READ_LOCK_BIAS.Thoughts?
Hearing no objections, here is the updated patch.
Thanx, Paul
------------------------------------------------------------------------
commit 970cab5d3d206029ed27274a98ea1c3d7e780e53
Author: Paul E. McKenney <[email protected]> Date: Mon Oct 29 07:36:50 2018 -0700
rcu: Avoid signed integer overflow in rcu_preempt_deferred_qs()
Subtracting INT_MIN can be interpreted as unconditional signed integer overflow, which according to the C standard is undefined behavior. Therefore, kernel build arguments notwithstanding, it would be good to future-proof the code. This commit therefore substitutes INT_MAX for INT_MIN in order to avoid undefined behavior.
While in the neighborhood, this commit also creates some meaningful names for INT_MAX and friends in order to improve readability, as suggested by Joel Fernandes.
squash! rcu: Avoid signed integer overflow in rcu_preempt_deferred_qs()
While in the neighborhood, use macros to give meaningful names.
kernel/rcu/tree_plugin.h +13 -8
@@ -397,6 +397,11 @@ static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp)return rnp->gp_tasks !=NULL ; } +/* Bias and limit values for ->rcu_read_lock_nesting. */ +#define RCU_NEST_BIAS INT_MAX +#define RCU_NEST_NMAX (-INT_MAX / 2) +#define RCU_NEST_PMAX (INT_MAX / 2) +/* * Preemptible RCU implementation for rcu_read_lock(). * Just increment ->rcu_read_lock_nesting, shared state will be updated
@@ -405,6 +410,8 @@ static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp)void __rcu_read_lock (void ) { current->rcu_read_lock_nesting++; +if (IS_ENABLED (CONFIG_PROVE_LOCKING )) +WARN_ON_ONCE (current->rcu_read_lock_nesting >RCU_NEST_PMAX );barrier ();/* critical section after entry code. */ }EXPORT_SYMBOL_GPL (__rcu_read_lock);
@@ -424,20 +431,18 @@ void __rcu_read_unlock(void) --t->rcu_read_lock_nesting; }else {barrier ();/* critical section before exit code. */ - t->rcu_read_lock_nesting =INT_MIN ; + t->rcu_read_lock_nesting = -RCU_NEST_BIAS ;barrier ();/* assign before ->rcu_read_unlock_special load */ if (unlikely (READ_ONCE (t->rcu_read_unlock_special.s)))rcu_read_unlock_special (t);barrier ();/* ->rcu_read_unlock_special load before assign */ t->rcu_read_lock_nesting =0 ; } -#ifdef CONFIG_PROVE_LOCKING - { -int rrln =READ_ONCE (t->rcu_read_lock_nesting); +if (IS_ENABLED (CONFIG_PROVE_LOCKING )) { +int rrln = t->rcu_read_lock_nesting; -WARN_ON_ONCE (rrln <0 && rrln >INT_MIN /2 ); +WARN_ON_ONCE (rrln <0 && rrln >RCU_NEST_NMAX ); } -#endif /* #ifdef CONFIG_PROVE_LOCKING */ }EXPORT_SYMBOL_GPL (__rcu_read_unlock);
@@ -617,11 +622,11 @@ static void rcu_preempt_deferred_qs(struct task_struct *t)if (!rcu_preempt_need_deferred_qs (t))return ;if (couldrecurse) - t->rcu_read_lock_nesting -=INT_MIN ; + t->rcu_read_lock_nesting -=RCU_NEST_BIAS ;local_irq_save (flags);rcu_preempt_deferred_qs_irqrestore (t, flags);if (couldrecurse) - t->rcu_read_lock_nesting +=INT_MIN ; + t->rcu_read_lock_nesting +=RCU_NEST_BIAS ; }/*
RE: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Quoted 24 lines
Hearing no objections, here is the updated patch.
Thanx, Paul
------------------------------------------------------------------------
commit
970cab5d3d206029ed27274a98ea1c3d7e780e53Author: Paul E. McKenney <[email protected]> Date: Mon Oct 29 07:36:50 2018 -0700rcu: Avoid signed integer overflow in
rcu_preempt_deferred_qs()Subtracting
INT_MINcan be interpreted as unconditional signed integer overflow, which according to the C standard is undefined behavior. Therefore, kernel build arguments notwithstanding, it would be good to future-proof the code. This commit therefore substitutesINT_MAXforINT_MINin order to avoid undefined behavior.While in the neighborhood, this commit also creates some meaningful
names
forINT_MAXand friends in order to improve readability, as suggested by Joel Fernandes.
Quoted 9 lines
Reported-by Ran Rozenstein <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>squash! rcu: Avoid signed integer overflow in
rcu_preempt_deferred_qs()While in the neighborhood, use macros to give meaningful names.
Signed-off-by Paul E. McKenney <[email protected]>
Hi,
What is the acceptance status of this patch?
Thanks,
Ran
Re: [PATCH tip/core/rcu 02/19] rcu: Defer reporting RCU-preempt quiescent states when disabled
Quoted 29 lines · Ran Rozenstein
Quoted 29 lines
Hearing no objections, here is the updated patch.
Thanx, Paul
------------------------------------------------------------------------
commit
970cab5d3d206029ed27274a98ea1c3d7e780e53Author: Paul E. McKenney <[email protected]> Date: Mon Oct 29 07:36:50 2018 -0700rcu: Avoid signed integer overflow in
rcu_preempt_deferred_qs()Subtracting
INT_MINcan be interpreted as unconditional signed integer overflow, which according to the C standard is undefined behavior. Therefore, kernel build arguments notwithstanding, it would be good to future-proof the code. This commit therefore substitutesINT_MAXforINT_MINin order to avoid undefined behavior.While in the neighborhood, this commit also creates some meaningful
names
forINT_MAXand friends in order to improve readability, as suggested by Joel Fernandes.Reported-by Ran Rozenstein <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>squash! rcu: Avoid signed integer overflow in
rcu_preempt_deferred_qs()
Quoted 7 lines
Quoted 3 lines
While in the neighborhood, use macros to give meaningful names.
Signed-off-by Paul E. McKenney <[email protected]>Hi,
What is the acceptance status of this patch?
It is queued in -rcu. If no problems arise beforehand, I intend to submit it as part of a pull request into -tip, which (again if no problems arise) be pulled into mainline during the next merge window.
Oddly enough, a couple of weeks ago the C++ Standards Committee voted in a proposal for C++20 removing undefined behavior for signed integer overflow. This is C++ rather than C, and C must support additional hardware that wouldn't much like forcing twos complement for signed integer overflow. But still... ;-)
Thanx, Paul
PATCH 3/19 rcutorture: Test extended "rcu" read-side critical sections
In reply to Paul E. McKenneyThis commit makes the "rcu" torture type test extended read-side critical sections in order to test the deferral of RCU-preempt quiescent-state testing.
In CONFIG_PREEMPT=n kernels, this simply duplicates the setup already in place for the "sched" torture type.
--- kernel/rcu/rcutorture.c | 1 + 1 file changed, 1 insertion(+)
kernel/rcu/rcutorture.c +1 -0
@@ -431,6 +431,7 @@ static struct rcu_torture_ops rcu_ops = { .stats = NULL, .irq_capable = 1, .can_boost = rcu_can_boost(), + .extendables = RCUTORTURE_MAX_EXTEND, .name = "rcu" };
Signature · 1 line
2.17.1
PATCH 4/19 rcu: Allow processing deferred QSes for exiting RCU-preempt readers
In reply to Paul E. McKenneyIf an RCU-preempt read-side critical section is exiting, that is, ->rcu_read_lock_nesting is negative, then it is a good time to look at the possibility of reporting deferred quiescent states. This commit therefore updates the checks in rcu_preempt_need_deferred_qs() to allow exiting critical sections to report deferred quiescent states.
--- kernel/rcu/tree_plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/rcu/tree_plugin.h +1 -1
@@ -602,7 +602,7 @@ static bool rcu_preempt_need_deferred_qs(struct task_struct *t) {return (this_cpu_ptr (&rcu_preempt_data)->deferred_qs ||READ_ONCE (t->rcu_read_unlock_special.s)) && - !t->rcu_read_lock_nesting; + t->rcu_read_lock_nesting <=0 ; }/*
Signature · 1 line
2.17.1
PATCH 5/19 rcu: Remove now-unused ->b.exp_need_qs field from the rcu_special union
In reply to Paul E. McKenneyThe ->b.exp_need_qs field is now set only to false, so this commit removes it. The job this field used to do is now done by the rcu_data structure's ->deferred_qs field, which is a consequence of a better split between task-based (the rcu_node structure's ->exp_tasks field) and CPU-based (the aforementioned rcu_data structure's ->deferred_qs field) tracking of quiescent states for RCU-preempt expedited grace periods.
--- include/linux/sched.h | 6 +----- kernel/rcu/tree_plugin.h | 13 ++++--------- 2 files changed, 5 insertions(+), 14 deletions(-)
include/linux/sched.h +1 -5
@@ -571,12 +571,8 @@ union rcu_special {struct {u8 blocked;u8 need_qs; -u8 exp_need_qs; - -/* Otherwise the compiler can store garbage here: */ -u8 pad; } b;/* Bits. */ -u32 s;/* Set of bits. */ +u16 s;/* Set of bits. */ };enum perf_event_task_context {
kernel/rcu/tree_plugin.h +4 -9
@@ -284,13 +284,10 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp)* no need to check for a subsequent expedited GP. (Though we are * still in a quiescent state in any case.) */ -if (blkd_state &RCU_EXP_BLKD && - t->rcu_read_unlock_special.b.exp_need_qs) { - t->rcu_read_unlock_special.b.exp_need_qs = false; +if (blkd_state &RCU_EXP_BLKD && rdp->deferred_qs)rcu_report_exp_rdp (rdp->rsp, rdp, true); - }else { -WARN_ON_ONCE (t->rcu_read_unlock_special.b.exp_need_qs); - } +else +WARN_ON_ONCE (rdp->deferred_qs); }/*
@@ -509,9 +506,7 @@ rcu_preempt_deferred_qs_irqrestore(struct task_struct *t, unsigned long flags)* tasks are handled when removing the task from the * blocked-tasks list below. */ -if (special.b.exp_need_qs || rdp->deferred_qs) { - t->rcu_read_unlock_special.b.exp_need_qs = false; - rdp->deferred_qs = false; +if (rdp->deferred_qs) {rcu_report_exp_rdp (rcu_state_p, rdp, true);if (!t->rcu_read_unlock_special.s) {local_irq_restore (flags);
Signature · 1 line
2.17.1
PATCH 6/19 rcu: Add warning to detect half-interrupts
In reply to Paul E. McKenneyRCU's dyntick-idle code is written to tolerate half-interrupts, that it, either an interrupt that invokes rcu_irq_enter() but never invokes the corresponding rcu_irq_exit() on the one hand, or an interrupt that never invokes rcu_irq_enter() but does invoke the "corresponding" rcu_irq_exit() on the other. These things really did happen at one time, as evidenced by this ca-2011 LKML post:
http://lkml.kernel.org/r/[email protected]
The reason why RCU tolerates half-interrupts is that usermode helpers used exceptions to invoke a system call from within the kernel such that the system call did a normal return (not a return from exception) to the calling context. This caused rcu_irq_enter() to be invoked without a matching rcu_irq_exit(). However, usermode helpers have since been rewritten to make much more housebroken use of workqueues, kernel threads, and do_execve(), and therefore should no longer produce half-interrupts. No one knows of any other source of half-interrupts, but then again, no one seems insane enough to go audit the entire kernel to verify that half-interrupts really are a relic of the past.
This commit therefore adds a pair of WARN_ON_ONCE() calls that will trigger in the presence of half interrupts, which the code will continue to handle correctly. If neither of these WARN_ON_ONCE() trigger by mid-2021, then perhaps RCU can stop handling half-interrupts, which would be a considerable simplification.
--- kernel/rcu/tree.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/rcu/tree.c +2 -0
@@ -714,6 +714,7 @@ static void rcu_eqs_enter(bool user)struct rcu_dynticks *rdtp; rdtp =this_cpu_ptr (&rcu_dynticks); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting !=DYNTICK_IRQ_NONIDLE );WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && rdtp->dynticks_nesting ==0 );
@@ -895,6 +896,7 @@ static void rcu_eqs_exit(bool user)trace_rcu_dyntick (TPS ("End" ), rdtp->dynticks_nesting,1 , rdtp->dynticks);WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current));WRITE_ONCE (rdtp->dynticks_nesting,1 ); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting);WRITE_ONCE (rdtp->dynticks_nmi_nesting,DYNTICK_IRQ_NONIDLE ); }
Signature · 1 line
2.17.1
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 20 lines · Paul E. McKenney
RCU's dyntick-idle code is written to tolerate half-interrupts, that it, either an interrupt that invokes
rcu_irq_enter()but never invokes the correspondingrcu_irq_exit()on the one hand, or an interrupt that never invokesrcu_irq_enter()but does invoke the "corresponding"rcu_irq_exit()on the other. These things really did happen at one time, as evidenced by this ca-2011 LKML post:http://lkml.kernel.org/r/[email protected]
The reason why RCU tolerates half-interrupts is that usermode helpers used exceptions to invoke a system call from within the kernel such that the system call did a normal return (not a return from exception) to the calling context. This caused
rcu_irq_enter()to be invoked without a matchingrcu_irq_exit(). However, usermode helpers have since been rewritten to make much more housebroken use of workqueues, kernel threads, anddo_execve(), and therefore should no longer produce half-interrupts. No one knows of any other source of half-interrupts, but then again, no one seems insane enough to go audit the entire kernel to verify that half-interrupts really are a relic of the past.
Quoted 5 lines
This commit therefore adds a pair of
WARN_ON_ONCE()calls that will trigger in the presence of half interrupts, which the code will continue to handle correctly. If neither of theseWARN_ON_ONCE()trigger by mid-2021, then perhaps RCU can stop handling half-interrupts, which would be a considerable simplification.
Hi Paul and everyone,
I was thinking some more about this patch and whether we can simplify this code much in 2021. Since 2021 is a bit far away, I thought working on it in again to keep it fresh in memory is a good idea ;-)
To me it seems we cannot easily combine the counters (dynticks_nesting and dynticks_nmi_nesting) even if we confirmed that there is no possibility of a half-interrupt scenario (assuming simplication means counter combining like Byungchul tried to do in https://goo.gl/X1U77X). The reason is because these 2 counters need to be tracked separately as they are used differently in the following function:
static int rcu_is_cpu_rrupt_from_idle(void)
{
return __this_cpu_read(rcu_data.dynticks_nesting) <= 0 &&
__this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1;
}dynticks_nesting actually tracks if we entered/exited idle or user mode.
dynticks_nmi_nesting tracks if we entered/exited interrupts.
We have to do the "dynticks_nmi_nesting <= 1" check because rcu_is_cpu_rrupt_from_idle() can possibly be called from an interrupt itself (like timer) so we discount 1 interrupt, and, the "dynticks_nesting <= 0" check is because the CPU MUST be in user or idle for the check to return true. We can't really combine these two into one counter then I think because they both convey different messages.
The only simplication we can do, is probably the "crowbar" updates to dynticks_nmi_nesting can be removed from rcu_eqs_enter/exit once we confirm no more half-interrupts are possible. Which might still be a worthwhile thing to do (while still keeping both counters separate).
However, I think we could combine the counters and lead to simplying the code in case we implement rcu_is_cpu_rrupt_from_idle differently such that it does not need the counters but NOHZ_FULL may take issue with that since it needs rcu_user_enter->rcu_eqs_enter to convey that the CPU is "RCU"-idle.
Actually, I had another question... rcu_user_enter() is a NOOP in !NOHZ_FULL config. In this case I was wondering if the the warning Paul added (in the patch I'm replying to) will really get fired for half-interrupts. The vast majority of the systems I believe are NOHZ_IDLE not NOHZ_FULL.
This is what a half-interrupt really looks like right? Please correct me if I'm wrong:rcu_irq_enter() [half interrupt causes an exception and thus rcu_irq_enter]rcu_user_enter() [due to usermode upcall]rcu_user_exit()
(no more rcu_irq_exit() - hence half an interrupt)
But the rcu_user_enter()/exit is a NOOP in some configs, so will the warning in rcu_eqs_e{xit,nter} really do anything?
Or was the idea with adding the new warnings, that they would fire the next time rcu_idle_enter/exit is called? Like for example:
rcu_irq_enter() [This is due to half-interrupt]rcu_idle_enter() [Eventually we enter the idle loop at some point
after the half-interrupt and the rcu_eqs_enter() would "crowbar" the dynticks_nmi_nesting counter to 0].
thanks!
- Joel
Quoted 33 lines
Reported-by Steven Rostedt <[email protected]>Reported-by Joel Fernandes <[email protected]>Reported-by Andy Lutomirski <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Reviewed-by Joel Fernandes (Google) <[email protected]>--- kernel/rcu/tree.c | 2 ++ 1 file changed, 2 insertions(+)kernel/rcu/tree.c +2
-0@@ -714,6 +714,7 @@ static void rcu_eqs_enter(bool user)struct rcu_dynticks *rdtp; rdtp =this_cpu_ptr (&rcu_dynticks); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting !=DYNTICK_IRQ_NONIDLE );WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && rdtp->dynticks_nesting ==0 );@@ -895,6 +896,7 @@ static void rcu_eqs_exit(bool user)trace_rcu_dyntick (TPS ("End" ), rdtp->dynticks_nesting,1 , rdtp->dynticks);WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current));WRITE_ONCE (rdtp->dynticks_nesting,1 ); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting);WRITE_ONCE (rdtp->dynticks_nmi_nesting,DYNTICK_IRQ_NONIDLE ); }Signature · 1 line
2.17.1
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 27 lines · Joel Fernandes
Quoted 20 lines · Paul E. McKenney
RCU's dyntick-idle code is written to tolerate half-interrupts, that it, either an interrupt that invokes
rcu_irq_enter()but never invokes the correspondingrcu_irq_exit()on the one hand, or an interrupt that never invokesrcu_irq_enter()but does invoke the "corresponding"rcu_irq_exit()on the other. These things really did happen at one time, as evidenced by this ca-2011 LKML post:http://lkml.kernel.org/r/[email protected]
The reason why RCU tolerates half-interrupts is that usermode helpers used exceptions to invoke a system call from within the kernel such that the system call did a normal return (not a return from exception) to the calling context. This caused
rcu_irq_enter()to be invoked without a matchingrcu_irq_exit(). However, usermode helpers have since been rewritten to make much more housebroken use of workqueues, kernel threads, anddo_execve(), and therefore should no longer produce half-interrupts. No one knows of any other source of half-interrupts, but then again, no one seems insane enough to go audit the entire kernel to verify that half-interrupts really are a relic of the past.Quoted 5 lines
This commit therefore adds a pair of
WARN_ON_ONCE()calls that will trigger in the presence of half interrupts, which the code will continue to handle correctly. If neither of theseWARN_ON_ONCE()trigger by mid-2021, then perhaps RCU can stop handling half-interrupts, which would be a considerable simplification.
Quoted 4 lines
Hi Paul and everyone,
I was thinking some more about this patch and whether we can simplify this code much in 2021. Since 2021 is a bit far away, I thought working on it in again to keep it fresh in memory is a good idea ;-)
Indeed, easy to forget. ;-)
Quoted 7 lines
To me it seems we cannot easily combine the counters (
dynticks_nestinganddynticks_nmi_nesting) even if we confirmed that there is no possibility of a half-interrupt scenario (assuming simplication means counter combining like Byungchul tried to do in https://goo.gl/X1U77X). The reason is because these 2 counters need to be tracked separately as they are used differently in the following function:
Quoted 7 lines
static int
rcu_is_cpu_rrupt_from_idle(void){ return __this_cpu_read(rcu_data.dynticks_nesting) <= 0 && __this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1; }
dynticks_nestingactually tracks if we entered/exited idle or user mode.
True, though it tracks user mode only in CONFIG_NO_HZ_FULL kernels.
Quoted 1 line
dynticks_nmi_nestingtracks if we entered/exited interrupts.
Including NMIs, yes.
Quoted 12 lines
We have to do the "
dynticks_nmi_nesting<= 1" check becausercu_is_cpu_rrupt_from_idle()can possibly be called from an interrupt itself (like timer) so we discount 1 interrupt, and, the "dynticks_nesting<= 0" check is because the CPU MUST be in user or idle for the check to return true. We can't really combine these two into one counter then I think because they both convey different messages.The only simplication we can do, is probably the "crowbar" updates to
dynticks_nmi_nestingcan be removed from rcu_eqs_enter/exit once we confirm no more half-interrupts are possible. Which might still be a worthwhile thing to do (while still keeping both counters separate).
Quoted 4 lines
However, I think we could combine the counters and lead to simplying the code in case we implement
rcu_is_cpu_rrupt_from_idledifferently such that it does not need the counters butNOHZ_FULLmay take issue with that since it needsrcu_user_enter->rcu_eqs_enterto convey that the CPU is "RCU"-idle.
I haven't gone through it in detail, but it seems like we should be able to treat in-kernel process-level execution like an interrupt from idle or userspace, as the case might be. If we did that, shouldn't we be able to just do this?
static int rcu_is_cpu_rrupt_from_idle(void)
{
return __this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1;
}Quoted 10 lines
Actually, I had another question...
rcu_user_enter()is a NOOP in !NOHZ_FULL config. In this case I was wondering if the the warning Paul added (in the patch I'm replying to) will really get fired for half-interrupts. The vast majority of the systems I believe areNOHZ_IDLEnotNOHZ_FULL.
This is what a half-interrupt really looks like right? Please correct me if I'm wrong:rcu_irq_enter()[half interrupt causes an exception and thusrcu_irq_enter]rcu_user_enter()[due to usermode upcall]rcu_user_exit()
(no morercu_irq_exit()- hence half an interrupt)
Quoted 2 lines
But the rcu_user_enter()/exit is a NOOP in some configs, so will the warning in rcu_eqs_e{xit,nter} really do anything?
Yes, because these are also called from rcu_idle_enter() and rcu_idle_exit(), which is invoked even in !NO_HZ_FULL kernels.
Quoted 7 lines
Or was the idea with adding the new warnings, that they would fire the next time rcu_idle_enter/exit is called? Like for example:
rcu_irq_enter()[This is due to half-interrupt]rcu_idle_enter()[Eventually we enter the idle loop at some point
after the half-interrupt and thercu_eqs_enter()would "crowbar" thedynticks_nmi_nestingcounter to 0].
You got it! ;-)
So yes, these warnings just detect the presence of misnesting. Presumably event tracing would then be used to track down the culprits. Assuming that the misnesting is reproducible and all that.
Thanx, Paul
Quoted 38 lines
thanks!
- Joel
Quoted 33 lines
Reported-by Steven Rostedt <[email protected]>Reported-by Joel Fernandes <[email protected]>Reported-by Andy Lutomirski <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Reviewed-by Joel Fernandes (Google) <[email protected]>--- kernel/rcu/tree.c | 2 ++ 1 file changed, 2 insertions(+)kernel/rcu/tree.c +2
-0@@ -714,6 +714,7 @@ static void rcu_eqs_enter(bool user)struct rcu_dynticks *rdtp; rdtp =this_cpu_ptr (&rcu_dynticks); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting !=DYNTICK_IRQ_NONIDLE );WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && rdtp->dynticks_nesting ==0 );@@ -895,6 +896,7 @@ static void rcu_eqs_exit(bool user)trace_rcu_dyntick (TPS ("End" ), rdtp->dynticks_nesting,1 , rdtp->dynticks);WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current));WRITE_ONCE (rdtp->dynticks_nesting,1 ); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting);WRITE_ONCE (rdtp->dynticks_nmi_nesting,DYNTICK_IRQ_NONIDLE ); }Signature · 1 line
2.17.1
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 78 lines · Paul E. McKenney
Quoted 27 lines · Joel Fernandes
Quoted 20 lines · Paul E. McKenney
RCU's dyntick-idle code is written to tolerate half-interrupts, that it, either an interrupt that invokes
rcu_irq_enter()but never invokes the correspondingrcu_irq_exit()on the one hand, or an interrupt that never invokesrcu_irq_enter()but does invoke the "corresponding"rcu_irq_exit()on the other. These things really did happen at one time, as evidenced by this ca-2011 LKML post:http://lkml.kernel.org/r/[email protected]
The reason why RCU tolerates half-interrupts is that usermode helpers used exceptions to invoke a system call from within the kernel such that the system call did a normal return (not a return from exception) to the calling context. This caused
rcu_irq_enter()to be invoked without a matchingrcu_irq_exit(). However, usermode helpers have since been rewritten to make much more housebroken use of workqueues, kernel threads, anddo_execve(), and therefore should no longer produce half-interrupts. No one knows of any other source of half-interrupts, but then again, no one seems insane enough to go audit the entire kernel to verify that half-interrupts really are a relic of the past.Quoted 5 lines
This commit therefore adds a pair of
WARN_ON_ONCE()calls that will trigger in the presence of half interrupts, which the code will continue to handle correctly. If neither of theseWARN_ON_ONCE()trigger by mid-2021, then perhaps RCU can stop handling half-interrupts, which would be a considerable simplification.Quoted 4 lines
Hi Paul and everyone,
I was thinking some more about this patch and whether we can simplify this code much in 2021. Since 2021 is a bit far away, I thought working on it in again to keep it fresh in memory is a good idea ;-)Indeed, easy to forget. ;-)
Quoted 7 lines
To me it seems we cannot easily combine the counters (
dynticks_nestinganddynticks_nmi_nesting) even if we confirmed that there is no possibility of a half-interrupt scenario (assuming simplication means counter combining like Byungchul tried to do in https://goo.gl/X1U77X). The reason is because these 2 counters need to be tracked separately as they are used differently in the following function:Quoted 7 lines
static int
rcu_is_cpu_rrupt_from_idle(void){ return __this_cpu_read(rcu_data.dynticks_nesting) <= 0 && __this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1; }
dynticks_nestingactually tracks if we entered/exited idle or user mode.True, though it tracks user mode only in
CONFIG_NO_HZ_FULLkernels.Quoted 1 line
dynticks_nmi_nestingtracks if we entered/exited interrupts.Including NMIs, yes.
Quoted 12 lines
We have to do the "
dynticks_nmi_nesting<= 1" check becausercu_is_cpu_rrupt_from_idle()can possibly be called from an interrupt itself (like timer) so we discount 1 interrupt, and, the "dynticks_nesting<= 0" check is because the CPU MUST be in user or idle for the check to return true. We can't really combine these two into one counter then I think because they both convey different messages.The only simplication we can do, is probably the "crowbar" updates to
dynticks_nmi_nestingcan be removed from rcu_eqs_enter/exit once we confirm no more half-interrupts are possible. Which might still be a worthwhile thing to do (while still keeping both counters separate).Quoted 4 lines
However, I think we could combine the counters and lead to simplying the code in case we implement
rcu_is_cpu_rrupt_from_idledifferently such that it does not need the counters butNOHZ_FULLmay take issue with that since it needsrcu_user_enter->rcu_eqs_enterto convey that the CPU is "RCU"-idle.I haven't gone through it in detail, but it seems like we should be able to treat in-kernel process-level execution like an interrupt from idle or userspace, as the case might be. If we did that, shouldn't we be able to just do this?
Quoted 4 lines
static int
rcu_is_cpu_rrupt_from_idle(void){ return __this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1; }
I think that would work only if this function is always called from an interrupt:
The comments on this function says:
* If the current CPU is idle or running at a first-level (not nested)
* interrupt from idle, return true. The caller must have at least
* disabled preemption.
*/
According to this comment rcu_is_cpu_rrupt_from_idle can be called from somewhere other than an interrupt although from code-reading that does not seem to be.
If it is ever possible to call rcu_is_cpu_rrupt_from_idle from anywhere but an interrupt, then we would have a scenario like:rcu_eqs_exit() (Called say from rcu_idle_exit)rcu_is_cpu_rrupt_from_idle (now nesting counter is 1, so the <=1 check
returns true).
This would result in the function falsely claiming the CPU was idle from an RCU standpoint I think.
However both from testing and from code reading, I don't see such a scenario happening. Could we be more explicit in the code that this function can only be called from an interrupt, and also we change the code comment to be more clear about it (like the following diff)?
I made the following change and it didn't seem to blow up and also makes the code more clear. The only change I would make if we were considering merging this is to change the BUG_ON to WARN_ON_ONCE but I want to check first if you think this change is useful to do. I think being explicit about the intention is a good thing. I also specifically check for first-level of interrupt nesting (==1).
kernel/rcu/tree.c +8 -3
@@ -377,14 +377,19 @@ static void __maybe_unused rcu_momentary_dyntick_idle(void)/** * rcu_is_cpu_rrupt_from_idle - see if idle or immediately interrupted from idle * -* If the current CPU is idle or running at a first-level (not nested) +* If the current CPU is idle and running at a first-level (not nested) * interrupt from idle, return true. The caller must have at least * disabled preemption. */ static int rcu_is_cpu_rrupt_from_idle (void ) { -return __this_cpu_read (rcu_data.dynticks_nesting) <=0 && -__this_cpu_read (rcu_data.dynticks_nmi_nesting) <=1 ; +int dynticks_nesting =__this_cpu_read (rcu_data.dynticks_nesting); +int dynticks_nmi_nesting =__this_cpu_read (rcu_data.dynticks_nmi_nesting); + +/* This function should only be called from an interrupt */ +BUG_ON (dynticks_nmi_nesting <1 ); + +return dynticks_nmi_nesting ==1 && dynticks_nesting <=0 ; }#define DEFAULT_RCU_BLIMIT 10/* Maximum callbacks per rcu_do_batch. */
Quoted 10 lines
Quoted 10 lines
Actually, I had another question...
rcu_user_enter()is a NOOP in !NOHZ_FULL config. In this case I was wondering if the the warning Paul added (in the patch I'm replying to) will really get fired for half-interrupts. The vast majority of the systems I believe areNOHZ_IDLEnotNOHZ_FULL.
This is what a half-interrupt really looks like right? Please correct me if I'm wrong:rcu_irq_enter()[half interrupt causes an exception and thusrcu_irq_enter]rcu_user_enter()[due to usermode upcall]rcu_user_exit()
(no morercu_irq_exit()- hence half an interrupt)
Quoted 5 lines
Quoted 2 lines
But the rcu_user_enter()/exit is a NOOP in some configs, so will the warning in rcu_eqs_e{xit,nter} really do anything?
Yes, because these are also called from
rcu_idle_enter()andrcu_idle_exit(), which is invoked even in !NO_HZ_FULL kernels.
Got it.
Quoted 9 lines
Quoted 7 lines
Or was the idea with adding the new warnings, that they would fire the next time rcu_idle_enter/exit is called? Like for example:
rcu_irq_enter()[This is due to half-interrupt]rcu_idle_enter()[Eventually we enter the idle loop at some point
after the half-interrupt and thercu_eqs_enter()would "crowbar" thedynticks_nmi_nestingcounter to 0].You got it! ;-)
Thanks a lot for confirming,
- Joel
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 103 lines · Joel Fernandes
Quoted 78 lines · Paul E. McKenney
Quoted 27 lines · Joel Fernandes
Quoted 20 lines · Paul E. McKenney
RCU's dyntick-idle code is written to tolerate half-interrupts, that it, either an interrupt that invokes
rcu_irq_enter()but never invokes the correspondingrcu_irq_exit()on the one hand, or an interrupt that never invokesrcu_irq_enter()but does invoke the "corresponding"rcu_irq_exit()on the other. These things really did happen at one time, as evidenced by this ca-2011 LKML post:http://lkml.kernel.org/r/[email protected]
The reason why RCU tolerates half-interrupts is that usermode helpers used exceptions to invoke a system call from within the kernel such that the system call did a normal return (not a return from exception) to the calling context. This caused
rcu_irq_enter()to be invoked without a matchingrcu_irq_exit(). However, usermode helpers have since been rewritten to make much more housebroken use of workqueues, kernel threads, anddo_execve(), and therefore should no longer produce half-interrupts. No one knows of any other source of half-interrupts, but then again, no one seems insane enough to go audit the entire kernel to verify that half-interrupts really are a relic of the past.Quoted 5 lines
This commit therefore adds a pair of
WARN_ON_ONCE()calls that will trigger in the presence of half interrupts, which the code will continue to handle correctly. If neither of theseWARN_ON_ONCE()trigger by mid-2021, then perhaps RCU can stop handling half-interrupts, which would be a considerable simplification.Quoted 4 lines
Hi Paul and everyone,
I was thinking some more about this patch and whether we can simplify this code much in 2021. Since 2021 is a bit far away, I thought working on it in again to keep it fresh in memory is a good idea ;-)Indeed, easy to forget. ;-)
Quoted 7 lines
To me it seems we cannot easily combine the counters (
dynticks_nestinganddynticks_nmi_nesting) even if we confirmed that there is no possibility of a half-interrupt scenario (assuming simplication means counter combining like Byungchul tried to do in https://goo.gl/X1U77X). The reason is because these 2 counters need to be tracked separately as they are used differently in the following function:Quoted 7 lines
static int
rcu_is_cpu_rrupt_from_idle(void){ return __this_cpu_read(rcu_data.dynticks_nesting) <= 0 && __this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1; }
dynticks_nestingactually tracks if we entered/exited idle or user mode.True, though it tracks user mode only in
CONFIG_NO_HZ_FULLkernels.Quoted 1 line
dynticks_nmi_nestingtracks if we entered/exited interrupts.Including NMIs, yes.
Quoted 12 lines
We have to do the "
dynticks_nmi_nesting<= 1" check becausercu_is_cpu_rrupt_from_idle()can possibly be called from an interrupt itself (like timer) so we discount 1 interrupt, and, the "dynticks_nesting<= 0" check is because the CPU MUST be in user or idle for the check to return true. We can't really combine these two into one counter then I think because they both convey different messages.The only simplication we can do, is probably the "crowbar" updates to
dynticks_nmi_nestingcan be removed from rcu_eqs_enter/exit once we confirm no more half-interrupts are possible. Which might still be a worthwhile thing to do (while still keeping both counters separate).Quoted 4 lines
However, I think we could combine the counters and lead to simplying the code in case we implement
rcu_is_cpu_rrupt_from_idledifferently such that it does not need the counters butNOHZ_FULLmay take issue with that since it needsrcu_user_enter->rcu_eqs_enterto convey that the CPU is "RCU"-idle.I haven't gone through it in detail, but it seems like we should be able to treat in-kernel process-level execution like an interrupt from idle or userspace, as the case might be. If we did that, shouldn't we be able to just do this?
Quoted 4 lines
static int
rcu_is_cpu_rrupt_from_idle(void){ return __this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1; }I think that would work only if this function is always called from an interrupt:
The comments on this function says:
* If the current CPU is idle or running at a first-level (not nested)
* interrupt from idle, return true. The caller must have at least
* disabled preemption.
*/According to this comment
rcu_is_cpu_rrupt_from_idlecan be called from somewhere other than an interrupt although from code-reading that does not seem to be.If it is ever possible to call
rcu_is_cpu_rrupt_from_idlefrom anywhere but an interrupt, then we would have a scenario like:rcu_eqs_exit()(Called say fromrcu_idle_exit)rcu_is_cpu_rrupt_from_idle(now nesting counter is 1, so the <=1 check
returns true).
Quoted 5 lines
This would result in the function falsely claiming the CPU was idle from an RCU standpoint I think.
However both from testing and from code reading, I don't see such a scenario happening.
Agreed!
Quoted 3 lines
Could we be more explicit in the code that this function can only
be called from an interrupt, and also we change the code comment to be more clear about it (like the following diff)?
That would be good!
Nice trick on using dyntick state to check for interrupt nesting, but wouldn't consolidating the counters break that? But is there a lockdep check for being in a hardware interrupt handler? If not, could one be added? This would have the benefit of not adding overhead to the scheduling-clock interrupt in production builds of the Linux kernel, while still finding this bug in testing.
(Another approach would be to use IS_ENABLED(CONFIG_PROVE_RCU), but a lockdep check would be cleaner.)
Quoted 57 lines
I made the following change and it didn't seem to blow up and also makes the code more clear. The only change I would make if we were considering merging this is to change the
BUG_ONtoWARN_ON_ONCEbut I want to check first if you think this change is useful to do. I think being explicit about the intention is a good thing. I also specifically check for first-level of interrupt nesting (==1).kernel/rcu/tree.c +8
-3@@ -377,14 +377,19 @@ static void __maybe_unused rcu_momentary_dyntick_idle(void)/** * rcu_is_cpu_rrupt_from_idle - see if idle or immediately interrupted from idle * -* If the current CPU is idle or running at a first-level (not nested) +* If the current CPU is idle and running at a first-level (not nested) * interrupt from idle, return true. The caller must have at least * disabled preemption. */ static int rcu_is_cpu_rrupt_from_idle (void ) { -return __this_cpu_read (rcu_data.dynticks_nesting) <=0 && -__this_cpu_read (rcu_data.dynticks_nmi_nesting) <=1 ; +int dynticks_nesting =__this_cpu_read (rcu_data.dynticks_nesting); +int dynticks_nmi_nesting =__this_cpu_read (rcu_data.dynticks_nmi_nesting); + +/* This function should only be called from an interrupt */ +BUG_ON (dynticks_nmi_nesting <1 ); + +return dynticks_nmi_nesting ==1 && dynticks_nesting <=0 ; }#define DEFAULT_RCU_BLIMIT 10/* Maximum callbacks per rcu_do_batch. */ Quoted 12 lines
Quoted 12 lines
Actually, I had another question...
rcu_user_enter()is a NOOP in !NOHZ_FULL config. In this case I was wondering if the the warning Paul added (in the patch I'm replying to) will really get fired for half-interrupts. The vast majority of the systems I believe areNOHZ_IDLEnotNOHZ_FULL.
This is what a half-interrupt really looks like right? Please correct me if I'm wrong:rcu_irq_enter()[half interrupt causes an exception and thusrcu_irq_enter]rcu_user_enter()[due to usermode upcall]rcu_user_exit()
(no morercu_irq_exit()- hence half an interrupt)Quoted 5 lines
Quoted 2 lines
But the rcu_user_enter()/exit is a NOOP in some configs, so will the warning in rcu_eqs_e{xit,nter} really do anything?
Yes, because these are also called from
rcu_idle_enter()andrcu_idle_exit(), which is invoked even in !NO_HZ_FULL kernels.Got it.
Quoted 3 lines
Quoted 3 lines
Or was the idea with adding the new warnings, that they would fire the next time rcu_idle_enter/exit is called? Like for example:
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
rcu_irq_enter()[This is due to half-interrupt]rcu_idle_enter()[Eventually we enter the idle loop at some point
after the half-interrupt and thercu_eqs_enter()would "crowbar" thedynticks_nmi_nestingcounter to 0].You got it! ;-)
Thanks a lot for confirming,
Thank you for looking into this!
Thanx, Paul
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
On Tue, Mar 12, 2019 at 08:20:34AM -0700, Paul E. McKenney wrote:
[snip]
Quoted 14 lines
Quoted 4 lines
Could we be more explicit in the code that this function can only
be called from an interrupt, and also we change the code comment to be more clear about it (like the following diff)?That would be good!
Nice trick on using dyntick state to check for interrupt nesting, but wouldn't consolidating the counters break that? But is there a lockdep check for being in a hardware interrupt handler? If not, could one be added? This would have the benefit of not adding overhead to the scheduling-clock interrupt in production builds of the Linux kernel, while still finding this bug in testing.
Quoted 2 lines
(Another approach would be to use
IS_ENABLED(CONFIG_PROVE_RCU), but a lockdep check would be cleaner.)
AFAICS, lockdep does not specifically track when we enter an interrupt, but rather only tracks when interrupts are enabled/disabled.
But we could use in_irq() to find if we are in an interrupt (which uses the preempt_count to track in HARDIRQ_MASK section of the counter).
I will add an in_irq() check that does the check only when PROVE_RCU is enabled, and send a patch.
Thanks and it is my pleasure to look into this, quite interesting!
- Joel
Steven Rostedt
reviewerRe: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 2 lines · On Wed, 13 Mar 2019 11:09:48 -0400 Joel Fernandes <[email protected]>…
AFAICS, lockdep does not specifically track when we enter an interrupt, but rather only tracks when interrupts are enabled/disabled.
It does:
#define __irq_enter() \
do { \
account_irq_enter_time(current); \
preempt_count_add(HARDIRQ_OFFSET); \
trace_hardirq_enter(); \
} while (0)# define trace_hardirq_enter() \
do { \
current->hardirq_context++; \
} while (0)And if the hardirq_context ever does not match "in_irq()" lockdep will complain loudly.
-- Steve
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 15 lines · Steven Rostedt
Quoted 2 lines · On Wed, 13 Mar 2019 11:09:48 -0400 Joel Fernandes <[email protected]>…
AFAICS, lockdep does not specifically track when we enter an interrupt, but rather only tracks when interrupts are enabled/disabled.
It does:
#define __irq_enter() \ do { \ account_irq_enter_time(current); \ preempt_count_add(HARDIRQ_OFFSET); \ trace_hardirq_enter(); \ } while (0)
Quoted 8 lines
# define trace_hardirq_enter() \ do { \ current->hardirq_context++; \ } while (0)And if the
hardirq_contextever does not match "in_irq()" lockdep will complain loudly.
Good to know, thank you!
Does this mean that there is a better approach that Joel's suggestion? I believe he would end up with something like this:
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && !in_irq());
It would be nice if there is something like this:
lockdep_assert_in_irq_handler();
But I haven't seen this. (Not that I have looked particularly hard for such a thing, mind you!)
Thanx, Paul
Steven Rostedt
reviewerRe: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 11 lines · On Wed, 13 Mar 2019 08:51:55 -0700 "Paul E. McKenney" <[email protected]>…
Does this mean that there is a better approach that Joel's suggestion? I believe he would end up with something like this:
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && !in_irq());It would be nice if there is something like this:
lockdep_assert_in_irq_handler();But I haven't seen this. (Not that I have looked particularly hard for such a thing, mind you!)
That would be trivial to implement:
#define lockdep_assert_in_irq() do {
WARN_ON(debug_locks && !current->hardirq_context);
} while (0)-- Steve
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 12 lines · Steven Rostedt
Quoted 9 lines · On Wed, 13 Mar 2019 08:51:55 -0700 "Paul E. McKenney" <[email protected]>…
Does this mean that there is a better approach that Joel's suggestion? I believe he would end up with something like this:
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && !in_irq());It would be nice if there is something like this:
lockdep_assert_in_irq_handler();
Quoted 8 lines
Quoted 2 lines
But I haven't seen this. (Not that I have looked particularly hard for such a thing, mind you!)
That would be trivial to implement:
#define lockdep_assert_in_irq() do { WARN_ON(debug_locks && !current->hardirq_context); } while (0)
Looks good to me!
Joel, does this work for you? I could be wrong, but I suspect that Steve is suggesting that you incorporate the above into your eventual patch. ;-)
Thanx, Paul
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 18 lines · Paul E. McKenney
Quoted 17 lines · Steven Rostedt
Quoted 11 lines · On Wed, 13 Mar 2019 08:51:55 -0700 "Paul E. McKenney" <[email protected]>…
Does this mean that there is a better approach that Joel's suggestion? I believe he would end up with something like this:
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && !in_irq());It would be nice if there is something like this:
lockdep_assert_in_irq_handler();But I haven't seen this. (Not that I have looked particularly hard for such a thing, mind you!)
That would be trivial to implement:
Quoted 8 lines
Quoted 3 lines
#define lockdep_assert_in_irq() do { WARN_ON(debug_locks && !current->hardirq_context); } while (0)Looks good to me!
Joel, does this work for you? I could be wrong, but I suspect that Steve is suggesting that you incorporate the above into your eventual patch. ;-)
Oh thanks for pointing that out. Yes it does work for me. I agree with the lockdep API addition and others could benefit from it too. I will incorporate the lockdep API addition into the RCU patch, but let me know if I should rather split it.
thanks!
- Joel
Steven Rostedt
reviewerRe: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 4 lines · On Thu, 14 Mar 2019 08:31:59 -0400 Joel Fernandes <[email protected]>…
Oh thanks for pointing that out. Yes it does work for me. I agree with the lockdep API addition and others could benefit from it too. I will incorporate the lockdep API addition into the RCU patch, but let me know if I should rather split it.
I'd recommend splitting it (adding the lockdep_assert in a patch by itself), but make sure that patch Cc's the lockdep maintainers and explains the reason for adding it. Might as well Cc the lockdep maintainers on the entire series too.
-- Steve
Steven Rostedt
reviewerRe: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 8 lines · On Thu, 14 Mar 2019 09:36:57 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 4 lines · On Thu, 14 Mar 2019 08:31:59 -0400 Joel Fernandes <[email protected]>…
Oh thanks for pointing that out. Yes it does work for me. I agree with the lockdep API addition and others could benefit from it too. I will incorporate the lockdep API addition into the RCU patch, but let me know if I should rather split it.
Quoted 5 lines
I'd recommend splitting it (adding the
lockdep_assertin a patch by itself), but make sure that patch Cc's the lockdep maintainers and explains the reason for adding it. Might as well Cc the lockdep maintainers on the entire series too.
Feel free to add "Suggested-by: Steven Rostedt (VMware) <[email protected]>" on that patch too.
-- Steve
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 11 lines · Steven Rostedt
Quoted 8 lines · On Thu, 14 Mar 2019 09:36:57 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 4 lines · On Thu, 14 Mar 2019 08:31:59 -0400 Joel Fernandes <[email protected]>…
Oh thanks for pointing that out. Yes it does work for me. I agree with the lockdep API addition and others could benefit from it too. I will incorporate the lockdep API addition into the RCU patch, but let me know if I should rather split it.
Quoted 8 lines
Quoted 5 lines
I'd recommend splitting it (adding the
lockdep_assertin a patch by itself), but make sure that patch Cc's the lockdep maintainers and explains the reason for adding it. Might as well Cc the lockdep maintainers on the entire series too.Feel free to add "Suggested-by: Steven Rostedt (VMware) <[email protected]>" on that patch too.
Will definitely split it and add your tag. Thanks!
- Joel
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
In reply to Joel FernandesQuoted 32 lines · Joel Fernandes
Quoted 20 lines · Paul E. McKenney
RCU's dyntick-idle code is written to tolerate half-interrupts, that it, either an interrupt that invokes
rcu_irq_enter()but never invokes the correspondingrcu_irq_exit()on the one hand, or an interrupt that never invokesrcu_irq_enter()but does invoke the "corresponding"rcu_irq_exit()on the other. These things really did happen at one time, as evidenced by this ca-2011 LKML post:http://lkml.kernel.org/r/[email protected]
The reason why RCU tolerates half-interrupts is that usermode helpers used exceptions to invoke a system call from within the kernel such that the system call did a normal return (not a return from exception) to the calling context. This caused
rcu_irq_enter()to be invoked without a matchingrcu_irq_exit(). However, usermode helpers have since been rewritten to make much more housebroken use of workqueues, kernel threads, anddo_execve(), and therefore should no longer produce half-interrupts. No one knows of any other source of half-interrupts, but then again, no one seems insane enough to go audit the entire kernel to verify that half-interrupts really are a relic of the past.Quoted 5 lines
This commit therefore adds a pair of
WARN_ON_ONCE()calls that will trigger in the presence of half interrupts, which the code will continue to handle correctly. If neither of theseWARN_ON_ONCE()trigger by mid-2021, then perhaps RCU can stop handling half-interrupts, which would be a considerable simplification.Hi Paul and everyone,
I was thinking some more about this patch and whether we can simplify this code much in 2021. Since 2021 is a bit far away, I thought working on it in again to keep it fresh in memory is a good idea ;-)
Quoted 6 lines
To me it seems we cannot easily combine the counters (
dynticks_nestinganddynticks_nmi_nesting) even if we confirmed that there is no possibility of a half-interrupt scenario (assuming simplication means counter combining like Byungchul tried to do in https://goo.gl/X1U77X). The reason is because these 2 counters need to be tracked separately as they are used differently in the following function:
Hi Joel and Paul,
I always love the way to logically approach problems so I'm a fan of all your works :) But I'm JUST curious about something here. Why can't we combine them the way I tried even if we confirm no possibility of half-interrupt? IMHO, the only thing we want to know through calling rcu_is_cpu_rrupt_from_idle() is whether the interrupt comes from RCU-idle or not - of course assuming the caller context always be an well-defined interrupt context like e.g. the tick handler.
So the function can return true if the caller is within a RCU-idle region except a well-known single interrupt nested.
Of course, now that we cannot confirm it yet, the crowbar is necessary. But does it still have a problem even after confirming it? Why? What am I missing? Could you explain why for me? :(
Thanks,
Byungchul
Quoted 85 lines
static int
rcu_is_cpu_rrupt_from_idle(void){ return __this_cpu_read(rcu_data.dynticks_nesting) <= 0 && __this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1; }
dynticks_nestingactually tracks if we entered/exited idle or user mode.
dynticks_nmi_nestingtracks if we entered/exited interrupts.We have to do the "
dynticks_nmi_nesting<= 1" check becausercu_is_cpu_rrupt_from_idle()can possibly be called from an interrupt itself (like timer) so we discount 1 interrupt, and, the "dynticks_nesting<= 0" check is because the CPU MUST be in user or idle for the check to return true. We can't really combine these two into one counter then I think because they both convey different messages.The only simplication we can do, is probably the "crowbar" updates to
dynticks_nmi_nestingcan be removed from rcu_eqs_enter/exit once we confirm no more half-interrupts are possible. Which might still be a worthwhile thing to do (while still keeping both counters separate).However, I think we could combine the counters and lead to simplying the code in case we implement
rcu_is_cpu_rrupt_from_idledifferently such that it does not need the counters butNOHZ_FULLmay take issue with that since it needsrcu_user_enter->rcu_eqs_enterto convey that the CPU is "RCU"-idle.Actually, I had another question...
rcu_user_enter()is a NOOP in !NOHZ_FULL config. In this case I was wondering if the the warning Paul added (in the patch I'm replying to) will really get fired for half-interrupts. The vast majority of the systems I believe areNOHZ_IDLEnotNOHZ_FULL.
This is what a half-interrupt really looks like right? Please correct me if I'm wrong:rcu_irq_enter()[half interrupt causes an exception and thusrcu_irq_enter]rcu_user_enter()[due to usermode upcall]rcu_user_exit()
(no morercu_irq_exit()- hence half an interrupt)But the rcu_user_enter()/exit is a NOOP in some configs, so will the warning in rcu_eqs_e{xit,nter} really do anything?
Or was the idea with adding the new warnings, that they would fire the next time rcu_idle_enter/exit is called? Like for example:
rcu_irq_enter()[This is due to half-interrupt]rcu_idle_enter()[Eventually we enter the idle loop at some point
after the half-interrupt and thercu_eqs_enter()would "crowbar" thedynticks_nmi_nestingcounter to 0].thanks!
- Joel
Quoted 33 lines
Reported-by Steven Rostedt <[email protected]>Reported-by Joel Fernandes <[email protected]>Reported-by Andy Lutomirski <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Reviewed-by Joel Fernandes (Google) <[email protected]>--- kernel/rcu/tree.c | 2 ++ 1 file changed, 2 insertions(+)kernel/rcu/tree.c +2
-0@@ -714,6 +714,7 @@ static void rcu_eqs_enter(bool user)struct rcu_dynticks *rdtp; rdtp =this_cpu_ptr (&rcu_dynticks); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting !=DYNTICK_IRQ_NONIDLE );WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && rdtp->dynticks_nesting ==0 );@@ -895,6 +896,7 @@ static void rcu_eqs_exit(bool user)trace_rcu_dyntick (TPS ("End" ), rdtp->dynticks_nesting,1 , rdtp->dynticks);WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current));WRITE_ONCE (rdtp->dynticks_nesting,1 ); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting);WRITE_ONCE (rdtp->dynticks_nmi_nesting,DYNTICK_IRQ_NONIDLE ); }Signature · 1 line
2.17.1
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 48 lines · Byungchul Park
Quoted 31 lines · Joel Fernandes
Quoted 20 lines · Paul E. McKenney
RCU's dyntick-idle code is written to tolerate half-interrupts, that it, either an interrupt that invokes
rcu_irq_enter()but never invokes the correspondingrcu_irq_exit()on the one hand, or an interrupt that never invokesrcu_irq_enter()but does invoke the "corresponding"rcu_irq_exit()on the other. These things really did happen at one time, as evidenced by this ca-2011 LKML post:http://lkml.kernel.org/r/[email protected]
The reason why RCU tolerates half-interrupts is that usermode helpers used exceptions to invoke a system call from within the kernel such that the system call did a normal return (not a return from exception) to the calling context. This caused
rcu_irq_enter()to be invoked without a matchingrcu_irq_exit(). However, usermode helpers have since been rewritten to make much more housebroken use of workqueues, kernel threads, anddo_execve(), and therefore should no longer produce half-interrupts. No one knows of any other source of half-interrupts, but then again, no one seems insane enough to go audit the entire kernel to verify that half-interrupts really are a relic of the past.Quoted 5 lines
This commit therefore adds a pair of
WARN_ON_ONCE()calls that will trigger in the presence of half interrupts, which the code will continue to handle correctly. If neither of theseWARN_ON_ONCE()trigger by mid-2021, then perhaps RCU can stop handling half-interrupts, which would be a considerable simplification.Hi Paul and everyone,
I was thinking some more about this patch and whether we can simplify this code much in 2021. Since 2021 is a bit far away, I thought working on it in again to keep it fresh in memory is a good idea ;-)Quoted 6 lines
To me it seems we cannot easily combine the counters (
dynticks_nestinganddynticks_nmi_nesting) even if we confirmed that there is no possibility of a half-interrupt scenario (assuming simplication means counter combining like Byungchul tried to do in https://goo.gl/X1U77X). The reason is because these 2 counters need to be tracked separately as they are used differently in the following function:Hi Joel and Paul,
I always love the way to logically approach problems so I'm a fan of all your works :) But I'm JUST curious about something here. Why can't we combine them the way I tried even if we confirm no possibility of half-interrupt? IMHO, the only thing we want to know through calling
rcu_is_cpu_rrupt_from_idle()is whether the interrupt comes from RCU-idle or not - of course assuming the caller context always be an well-defined interrupt context like e.g. the tick handler.
Quoted 6 lines
So the function can return true if the caller is within a RCU-idle region except a well-known single interrupt nested.
Of course, now that we cannot confirm it yet, the crowbar is necessary. But does it still have a problem even after confirming it? Why? What am I missing? Could you explain why for me? :(
Did you also want to consider the case the function is called from others than well-known interrupt contexts? If yes, then I agree with you, there doesn't seem to be the kind of code and it's not a good idea to let the function be called generally though.
Quoted 87 lines
Thanks,
ByungchulQuoted 84 lines
static int
rcu_is_cpu_rrupt_from_idle(void){ return __this_cpu_read(rcu_data.dynticks_nesting) <= 0 && __this_cpu_read(rcu_data.dynticks_nmi_nesting) <= 1; }
dynticks_nestingactually tracks if we entered/exited idle or user mode.
dynticks_nmi_nestingtracks if we entered/exited interrupts.We have to do the "
dynticks_nmi_nesting<= 1" check becausercu_is_cpu_rrupt_from_idle()can possibly be called from an interrupt itself (like timer) so we discount 1 interrupt, and, the "dynticks_nesting<= 0" check is because the CPU MUST be in user or idle for the check to return true. We can't really combine these two into one counter then I think because they both convey different messages.The only simplication we can do, is probably the "crowbar" updates to
dynticks_nmi_nestingcan be removed from rcu_eqs_enter/exit once we confirm no more half-interrupts are possible. Which might still be a worthwhile thing to do (while still keeping both counters separate).However, I think we could combine the counters and lead to simplying the code in case we implement
rcu_is_cpu_rrupt_from_idledifferently such that it does not need the counters butNOHZ_FULLmay take issue with that since it needsrcu_user_enter->rcu_eqs_enterto convey that the CPU is "RCU"-idle.Actually, I had another question...
rcu_user_enter()is a NOOP in !NOHZ_FULL config. In this case I was wondering if the the warning Paul added (in the patch I'm replying to) will really get fired for half-interrupts. The vast majority of the systems I believe areNOHZ_IDLEnotNOHZ_FULL.
This is what a half-interrupt really looks like right? Please correct me if I'm wrong:rcu_irq_enter()[half interrupt causes an exception and thusrcu_irq_enter]rcu_user_enter()[due to usermode upcall]rcu_user_exit()
(no morercu_irq_exit()- hence half an interrupt)But the rcu_user_enter()/exit is a NOOP in some configs, so will the warning in rcu_eqs_e{xit,nter} really do anything?
Or was the idea with adding the new warnings, that they would fire the next time rcu_idle_enter/exit is called? Like for example:
rcu_irq_enter()[This is due to half-interrupt]rcu_idle_enter()[Eventually we enter the idle loop at some point
after the half-interrupt and thercu_eqs_enter()would "crowbar" thedynticks_nmi_nestingcounter to 0].thanks!
- Joel
Quoted 32 lines
Reported-by Steven Rostedt <[email protected]>Reported-by Joel Fernandes <[email protected]>Reported-by Andy Lutomirski <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Reviewed-by Joel Fernandes (Google) <[email protected]>--- kernel/rcu/tree.c | 2 ++ 1 file changed, 2 insertions(+)kernel/rcu/tree.c +2
-0@@ -714,6 +714,7 @@ static void rcu_eqs_enter(bool user)struct rcu_dynticks *rdtp; rdtp =this_cpu_ptr (&rcu_dynticks); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting !=DYNTICK_IRQ_NONIDLE );WRITE_ONCE (rdtp->dynticks_nmi_nesting,0 );WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && rdtp->dynticks_nesting ==0 );@@ -895,6 +896,7 @@ static void rcu_eqs_exit(bool user)trace_rcu_dyntick (TPS ("End" ), rdtp->dynticks_nesting,1 , rdtp->dynticks);WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current));WRITE_ONCE (rdtp->dynticks_nesting,1 ); +WARN_ON_ONCE (rdtp->dynticks_nmi_nesting);WRITE_ONCE (rdtp->dynticks_nmi_nesting,DYNTICK_IRQ_NONIDLE ); }Signature · 1 line
2.17.1
Re: [PATCH tip/core/rcu 06/19] rcu: Add warning to detect half-interrupts
Quoted 57 lines · Byungchul Park
Quoted 48 lines · Byungchul Park
Quoted 31 lines · Joel Fernandes
Quoted 20 lines · Paul E. McKenney
RCU's dyntick-idle code is written to tolerate half-interrupts, that it, either an interrupt that invokes
rcu_irq_enter()but never invokes the correspondingrcu_irq_exit()on the one hand, or an interrupt that never invokesrcu_irq_enter()but does invoke the "corresponding"rcu_irq_exit()on the other. These things really did happen at one time, as evidenced by this ca-2011 LKML post:http://lkml.kernel.org/r/[email protected]
The reason why RCU tolerates half-interrupts is that usermode helpers used exceptions to invoke a system call from within the kernel such that the system call did a normal return (not a return from exception) to the calling context. This caused
rcu_irq_enter()to be invoked without a matchingrcu_irq_exit(). However, usermode helpers have since been rewritten to make much more housebroken use of workqueues, kernel threads, anddo_execve(), and therefore should no longer produce half-interrupts. No one knows of any other source of half-interrupts, but then again, no one seems insane enough to go audit the entire kernel to verify that half-interrupts really are a relic of the past.Quoted 5 lines
This commit therefore adds a pair of
WARN_ON_ONCE()calls that will trigger in the presence of half interrupts, which the code will continue to handle correctly. If neither of theseWARN_ON_ONCE()trigger by mid-2021, then perhaps RCU can stop handling half-interrupts, which would be a considerable simplification.Hi Paul and everyone,
I was thinking some more about this patch and whether we can simplify this code much in 2021. Since 2021 is a bit far away, I thought working on it in again to keep it fresh in memory is a good idea ;-)Quoted 6 lines
To me it seems we cannot easily combine the counters (
dynticks_nestinganddynticks_nmi_nesting) even if we confirmed that there is no possibility of a half-interrupt scenario (assuming simplication means counter combining like Byungchul tried to do in https://goo.gl/X1U77X). The reason is because these 2 counters need to be tracked separately as they are used differently in the following function:Hi Joel and Paul,
I always love the way to logically approach problems so I'm a fan of all your works :) But I'm JUST curious about something here. Why can't we combine them the way I tried even if we confirm no possibility of half-interrupt? IMHO, the only thing we want to know through calling
rcu_is_cpu_rrupt_from_idle()is whether the interrupt comes from RCU-idle or not - of course assuming the caller context always be an well-defined interrupt context like e.g. the tick handler.Quoted 6 lines
So the function can return true if the caller is within a RCU-idle region except a well-known single interrupt nested.
Of course, now that we cannot confirm it yet, the crowbar is necessary. But does it still have a problem even after confirming it? Why? What am I missing? Could you explain why for me? :(
Quoted 5 lines
Did you also want to consider the case the function is called from others than
well-known interrupt contexts? If yes, then I agree with you, there doesn't seem to be the kind of code and it's not a good idea to let the function be called generally though.
We were discussing exactly this on the thread. I am going to be adding a lockdep check to make sure the function isn't called from anywhere but an interrupt context. Then once we can confirm that there are no more half-interrupts in the future, we can apply your counter combining approach.
Based on the comments on the rrupt_from_idle() function, it wasn't clear to me if the function was intended to be called from any context. That's why I thought the counter combing approach would not work, but you are right - it should work.
I will CC you on the lockdep check patch. Also hope you are subscribed to the new shiny [email protected] list as well :-)
thanks,
- Joel
PATCH 7/19 rcu: Apply RCU-bh QSes to RCU-sched and RCU-preempt when safe
In reply to Paul E. McKenneyOne necessary step towards consolidating the three flavors of RCU is to make sure that the resulting consolidated "one flavor to rule them all" correctly handles networking denial-of-service attacks. One thing that allows RCU-bh to do so is that __do_softirq() invokes rcu_bh_qs() every so often, and so something similar has to happen for consolidated RCU.
This must be done carefully. For example, if a preemption-disabled region of code takes an interrupt which does softirq processing before returning, consolidated RCU must ignore the resulting rcu_bh_qs() invocations -- preemption is still disabled, and that means an RCU reader for the consolidated flavor.
This commit therefore creates a new rcu_softirq_qs() that is called only from the ksoftirqd task, thus avoiding the interrupted-a-preempted-region problem. This new rcu_softirq_qs() function invokes rcu_sched_qs(), rcu_preempt_qs(), and rcu_preempt_deferred_qs(). The latter call handles any deferred quiescent states.
Note that __do_softirq() still invokes rcu_bh_qs(). It will continue to do so until a later stage of cleanup when the RCU-bh flavor is removed.
--- include/linux/rcutiny.h | 5 +++++ include/linux/rcutree.h | 1 + kernel/rcu/tree.c | 7 +++++++ kernel/rcu/tree.h | 1 + kernel/rcu/tree_plugin.h | 5 +++++ kernel/softirq.c | 2 ++ 6 files changed, 21 insertions(+)
include/linux/rcutiny.h +5 -0
@@ -90,6 +90,11 @@ static inline void kfree_call_rcu(struct rcu_head *head,call_rcu (head, func); } +static inline void rcu_softirq_qs (void ) +{ +rcu_sched_qs (); +} +#define rcu_note_context_switch (preempt) \ do { \ rcu_sched_qs(); \
include/linux/rcutree.h +1 -0
@@ -30,6 +30,7 @@#ifndef __LINUX_RCUTREE_H#define __LINUX_RCUTREE_H +void rcu_softirq_qs (void );void rcu_note_context_switch (bool preempt);int rcu_needs_cpu (u64 basem,u64 *nextevt);void rcu_cpu_stall_reset (void );
kernel/rcu/tree.c +7 -0
@@ -255,6 +255,13 @@ void rcu_bh_qs(void) } } +void rcu_softirq_qs (void ) +{ +rcu_sched_qs (); +rcu_preempt_qs (); +rcu_preempt_deferred_qs (current); +} +/* * Steal a bit from the bottom of ->dynticks for idle entry/exit * control. Initially this is for TLB flushing.
kernel/rcu/tree.h +1 -0
@@ -433,6 +433,7 @@ DECLARE_PER_CPU(char, rcu_cpu_has_work);/* Forward declarations for rcutree_plugin.h */ static void rcu_bootup_announce (void ); +static void rcu_preempt_qs (void );static void rcu_preempt_note_context_switch (bool preempt);static int rcu_preempt_blocked_readers_cgp (struct rcu_node *rnp);#ifdef CONFIG_HOTPLUG_CPU
kernel/rcu/tree_plugin.h +5 -0
@@ -974,6 +974,11 @@ static void __init rcu_bootup_announce(void)rcu_bootup_announce_oddness (); } +/* Because preemptible RCU does not exist, we can ignore its QSes. */ +static void rcu_preempt_qs (void ) +{ +} +/* * Because preemptible RCU does not exist, we never have to check for * CPUs being in quiescent states.
kernel/softirq.c +2 -0
@@ -302,6 +302,8 @@ asmlinkage __visible void __softirq_entry __do_softirq(void) }rcu_bh_qs (); +if (__this_cpu_read (ksoftirqd) == current) +rcu_softirq_qs ();local_irq_disable (); pending =local_softirq_pending ();
Signature · 1 line
2.17.1
PATCH 8/19 rcu: Report expedited grace periods at context-switch time
In reply to Paul E. McKenneyThis commit reduces the latency of expedited RCU grace periods by reporting a quiescent state for the CPU at context-switch time. In CONFIG_PREEMPT=y kernels, if the outgoing task is still within an RCU read-side critical section (and thus still blocking some grace period, perhaps including this expedited grace period), then that task will already have been placed on one of the leaf rcu_node structures' ->blkd_tasks list.
--- kernel/rcu/tree_plugin.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
kernel/rcu/tree_plugin.h +3 -2
@@ -332,7 +332,7 @@ static void rcu_preempt_qs(void)static void rcu_preempt_note_context_switch (bool preempt) {struct task_struct *t = current; -struct rcu_data *rdp; +struct rcu_data *rdp =this_cpu_ptr (rcu_state_p->rda);struct rcu_node *rnp;lockdep_assert_irqs_disabled ();
@@ -341,7 +341,6 @@ static void rcu_preempt_note_context_switch(bool preempt) !t->rcu_read_unlock_special.b.blocked) {/* Possibly blocking in an RCU read-side critical section. */ - rdp =this_cpu_ptr (rcu_state_p->rda); rnp = rdp->mynode;raw_spin_lock_rcu_node (rnp); t->rcu_read_unlock_special.b.blocked = true;
@@ -383,6 +382,8 @@ static void rcu_preempt_note_context_switch(bool preempt)* means that we continue to block the current grace period. */ rcu_preempt_qs (); +if (rdp->deferred_qs) +rcu_report_exp_rdp (rcu_state_p, rdp, true); }/*
Signature · 1 line
2.17.1
PATCH 9/19 rcu: Define RCU-bh update API in terms of RCU
In reply to Paul E. McKenneyNow that the main RCU API knows about softirq disabling and softirq's quiescent states, the RCU-bh update code can be dispensed with. This commit therefore removes the RCU-bh update-side implementation and defines RCU-bh's update-side API in terms of that of either RCU-preempt or RCU-sched, depending on the setting of the CONFIG_PREEMPT Kconfig option.
In kernels built with CONFIG_RCU_NOCB_CPU=y this has the knock-on effect of reducing by one the number of rcuo kthreads per CPU.
--- include/linux/rcupdate.h | 10 ++-- include/linux/rcutiny.h | 10 +++- include/linux/rcutree.h | 8 ++- kernel/rcu/tiny.c | 115 +++++++-------------------------------- kernel/rcu/tree.c | 97 +++------------------------------ kernel/rcu/tree_plugin.h | 1 - kernel/softirq.c | 1 - 7 files changed, 48 insertions(+), 194 deletions(-)
include/linux/rcupdate.h +6 -4
@@ -55,11 +55,15 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func);#define call_rcu call_rcu_sched#endif /* #else #ifdef CONFIG_PREEMPT_RCU */ -void call_rcu_bh (struct rcu_head *head,rcu_callback_t func);void call_rcu_sched (struct rcu_head *head,rcu_callback_t func);void synchronize_sched (void );void rcu_barrier_tasks (void ); +static inline void call_rcu_bh (struct rcu_head *head,rcu_callback_t func) +{ +call_rcu (head, func); +} +#ifdef CONFIG_PREEMPT_RCU void __rcu_read_lock (void );
@@ -104,7 +108,6 @@ static inline int rcu_preempt_depth(void)void rcu_init (void );extern int rcu_scheduler_active__read_mostly ;void rcu_sched_qs (void ); -void rcu_bh_qs (void );void rcu_check_callbacks (int user);void rcu_report_dead (unsigned int cpu);void rcutree_migrate_callbacks (int cpu);
@@ -326,8 +329,7 @@ static inline void rcu_preempt_sleep_check(void) { }* and rcu_assign_pointer(). Some of these could be folded into their * callers, but they are left separate in order to ease introduction of * multiple flavors of pointers to match the multiple flavors of RCU -* (e.g., __rcu_bh, * __rcu_sched, and __srcu), should this make sense in -* the future. +* (e.g., __rcu_sched, and __srcu), should this make sense in the future. */ #ifdef __CHECKER__
include/linux/rcutiny.h +7 -3
@@ -56,19 +56,23 @@ static inline void cond_synchronize_sched(unsigned long oldstate)might_sleep (); } -extern void rcu_barrier_bh (void ); -extern void rcu_barrier_sched (void ); -static inline void synchronize_rcu_expedited (void ) {synchronize_sched ();/* Only one CPU, so pretty fast anyway!!! */ } +extern void rcu_barrier_sched (void ); +static inline void rcu_barrier (void ) {rcu_barrier_sched ();/* Only one CPU, so only one list of callbacks! */ } +static inline void rcu_barrier_bh (void ) +{ +rcu_barrier (); +} +static inline void synchronize_rcu_bh (void ) {synchronize_sched ();
include/linux/rcutree.h +6 -2
@@ -45,7 +45,11 @@ static inline void rcu_virt_note_context_switch(int cpu)rcu_note_context_switch (false); } -void synchronize_rcu_bh (void ); +static inline void synchronize_rcu_bh (void ) +{ +synchronize_rcu (); +} +void synchronize_sched_expedited (void );void synchronize_rcu_expedited (void );
@@ -69,7 +73,7 @@ void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func);*/ static inline void synchronize_rcu_bh_expedited (void ) { -synchronize_sched_expedited (); +synchronize_rcu_expedited (); }void rcu_barrier (void );
kernel/rcu/tiny.c +21 -94
183 lines not on this page · raw message
kernel/rcu/tree.c +8 -89
163 lines not on this page · raw message
kernel/rcu/tree_plugin.h +0 -1
@@ -1320,7 +1320,6 @@ static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp,static void rcu_kthread_do_work (void ) {rcu_do_batch (&rcu_sched_state,this_cpu_ptr (&rcu_sched_data)); -rcu_do_batch (&rcu_bh_state,this_cpu_ptr (&rcu_bh_data));rcu_do_batch (&rcu_preempt_state,this_cpu_ptr (&rcu_preempt_data)); }
kernel/softirq.c +0 -1
@@ -301,7 +301,6 @@ asmlinkage __visible void __softirq_entry __do_softirq(void) pending >>= softirq_bit; } -rcu_bh_qs ();if (__this_cpu_read (ksoftirqd) == current)rcu_softirq_qs ();local_irq_disable ();
Signature · 1 line
2.17.1
PATCH 10/19 rcu: Update comments and help text for no more RCU-bh updaters
In reply to Paul E. McKenneyThis commit updates comments and help text to account for the fact that RCU-bh update-side functions are now simple wrappers for their RCU or RCU-sched counterparts.
--- include/linux/rcupdate.h | 12 ++++-------- include/linux/rcupdate_wait.h | 6 +++--- include/linux/rcutree.h | 14 ++------------ kernel/rcu/Kconfig | 10 +++++----- kernel/rcu/tree.c | 17 +++++++++-------- kernel/rcu/update.c | 2 +- 6 files changed, 24 insertions(+), 37 deletions(-)
include/linux/rcupdate.h +4 -8
@@ -688,14 +688,10 @@ static inline void rcu_read_unlock(void)/** * rcu_read_lock_bh() - mark the beginning of an RCU-bh critical section * -* This is equivalent of rcu_read_lock(), but to be used when updates -* are being done using call_rcu_bh() or synchronize_rcu_bh(). Since -* both call_rcu_bh() and synchronize_rcu_bh() consider completion of a -* softirq handler to be a quiescent state, a process in RCU read-side -* critical section must be protected by disabling softirqs. Read-side -* critical sections in interrupt context can use just rcu_read_lock(), -* though this should at least be commented to avoid confusing people -* reading the code. +* This is equivalent of rcu_read_lock(), but also disables softirqs. +* Note that synchronize_rcu() and friends may be used for the update +* side, although synchronize_rcu_bh() is available as a wrapper in the +* short term. Longer term, the _bh update-side API will be eliminated. * * Note that rcu_read_lock_bh() and the matching rcu_read_unlock_bh() * must occur in the same context, for example, it is illegal to invoke
include/linux/rcupdate_wait.h +3 -3
@@ -36,13 +36,13 @@ do { \* @...: List of call_rcu() functions for the flavors to wait on. * * This macro waits concurrently for multiple flavors of RCU grace periods. -* For example, synchronize_rcu_mult(call_rcu, call_rcu_bh) would wait -* on concurrent RCU and RCU-bh grace periods. Waiting on a give SRCU +* For example, synchronize_rcu_mult(call_rcu, call_rcu_sched) would wait +* on concurrent RCU and RCU-sched grace periods. Waiting on a give SRCU * domain requires you to write a wrapper function for that SRCU domain's * call_srcu() function, supplying the corresponding srcu_struct. * * If Tiny RCU, tell _wait_rcu_gp() not to bother waiting for RCU -* or RCU-bh, given that anywhere synchronize_rcu_mult() can be called +* or RCU-sched, given that anywhere synchronize_rcu_mult() can be called * is automatically a grace period. */ #define synchronize_rcu_mult (...) \
include/linux/rcutree.h +2 -12
@@ -58,18 +58,8 @@ void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func);/** * synchronize_rcu_bh_expedited - Brute-force RCU-bh grace period * -* Wait for an RCU-bh grace period to elapse, but use a "big hammer" -* approach to force the grace period to end quickly. This consumes -* significant time on all CPUs and is unfriendly to real-time workloads, -* so is thus not recommended for any sort of common-case code. In fact, -* if you are using synchronize_rcu_bh_expedited() in a loop, please -* restructure your code to batch your updates, and then use a single -* synchronize_rcu_bh() instead. -* -* Note that it is illegal to call this function while holding any lock -* that is acquired by a CPU-hotplug notifier. And yes, it is also illegal -* to call this function from a CPU-hotplug notifier. Failing to observe -* these restriction will result in deadlock. +* This is a transitional API and will soon be removed, with all +* callers converted to synchronize_rcu_expedited(). */ static inline void synchronize_rcu_bh_expedited (void ) {
kernel/rcu/Kconfig +5 -5
@@ -229,11 +229,11 @@ config RCU_NOCB_CPU CPUs specified at boot time by the rcu_nocbs parameter. For each such CPU, a kthread ("rcuox/N") will be created to invoke callbacks, where the "N" is the CPU being offloaded, - and where the "x" is "b" for RCU-bh, "p" for RCU-preempt, and - "s" for RCU-sched. Nothing prevents this kthread from running - on the specified CPUs, but (1) the kthreads may be preempted - between each callback, and (2) affinity or cgroups can be used - to force the kthreads to run on whatever set of CPUs is desired. + and where the "p" for RCU-preempt and "s" for RCU-sched. + Nothing prevents this kthread from running on the specified + CPUs, but (1) the kthreads may be preempted between each + callback, and (2) affinity or cgroups can be used to force + the kthreads to run on whatever set of CPUs is desired. Say Y here if you want to help to debug reduced OS jitter. Say N here if you are unsure.
kernel/rcu/tree.c +9 -8
@@ -565,7 +565,8 @@ unsigned long rcu_sched_get_gp_seq(void)EXPORT_SYMBOL_GPL (rcu_sched_get_gp_seq);/* -* Return the number of RCU-bh GPs completed thus far for debug & stats. +* Return the number of RCU GPs completed thus far for debug & stats. +* This is a transitional API and will soon be removed. */ unsigned long rcu_bh_get_gp_seq (void ) {
@@ -3068,13 +3069,13 @@ void kfree_call_rcu(struct rcu_head *head,EXPORT_SYMBOL_GPL (kfree_call_rcu);/* -* Because a context switch is a grace period for RCU-sched and RCU-bh, -* any blocking grace-period wait automatically implies a grace period -* if there is only one CPU online at any point time during execution -* of either synchronize_sched() or synchronize_rcu_bh(). It is OK to -* occasionally incorrectly indicate that there are multiple CPUs online -* when there was in fact only one the whole time, as this just adds -* some overhead: RCU still operates correctly. +* Because a context switch is a grace period for RCU-sched, any blocking +* grace-period wait automatically implies a grace period if there +* is only one CPU online at any point time during execution of either +* synchronize_sched() or synchronize_rcu_bh(). It is OK to occasionally +* incorrectly indicate that there are multiple CPUs online when there +* was in fact only one the whole time, as this just adds some overhead: +* RCU still operates correctly. */ static int rcu_blocking_is_gp (void ) {
kernel/rcu/update.c +1 -1
@@ -298,7 +298,7 @@ EXPORT_SYMBOL_GPL(rcu_read_lock_held);* * Check debug_lockdep_rcu_enabled() to prevent false positives during boot. * -* Note that rcu_read_lock() is disallowed if the CPU is either idle or +* Note that rcu_read_lock_bh() is disallowed if the CPU is either idle or * offline from an RCU perspective, so check for those as well. */ int rcu_read_lock_bh_held (void )
Signature · 1 line
2.17.1
PATCH 11/19 rcu: Drop "wake" parameter from rcu_report_exp_rdp()
In reply to Paul E. McKenneyThe rcu_report_exp_rdp() function is always invoked with its "wake" argument set to "true", so this commit drops this parameter. The only potential call site that would use "false" is in the code driving the expedited grace period, and that code uses rcu_report_exp_cpu_mult() instead, which therefore retains its "wake" parameter.
--- kernel/rcu/tree.c | 9 +++------ kernel/rcu/tree_exp.h | 9 ++++----- kernel/rcu/tree_plugin.h | 6 +++--- 3 files changed, 10 insertions(+), 14 deletions(-)
kernel/rcu/tree.c +3 -6
@@ -165,8 +165,7 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf);static void rcu_boost_kthread_setaffinity (struct rcu_node *rnp,int outgoingcpu);static void invoke_rcu_core (void );static void invoke_rcu_callbacks (struct rcu_state *rsp,struct rcu_data *rdp); -static void rcu_report_exp_rdp (struct rcu_state *rsp, -struct rcu_data *rdp,bool wake); +static void rcu_report_exp_rdp (struct rcu_state *rsp,struct rcu_data *rdp);static void sync_sched_exp_online_cleanup (int cpu);/* rcuc/rcub kthread realtime priority */
@@ -239,8 +238,7 @@ void rcu_sched_qs(void)if (!__this_cpu_read (rcu_sched_data.cpu_no_qs.b.exp))return ;__this_cpu_write (rcu_sched_data.cpu_no_qs.b.exp, false); -rcu_report_exp_rdp (&rcu_sched_state, -this_cpu_ptr (&rcu_sched_data), true); +rcu_report_exp_rdp (&rcu_sched_state,this_cpu_ptr (&rcu_sched_data)); }void rcu_softirq_qs (void )
@@ -3757,8 +3755,7 @@ void rcu_report_dead(unsigned int cpu)/* QS for any half-done expedited RCU-sched GP. */ preempt_disable (); -rcu_report_exp_rdp (&rcu_sched_state, -this_cpu_ptr (rcu_sched_state.rda), true); +rcu_report_exp_rdp (&rcu_sched_state,this_cpu_ptr (rcu_sched_state.rda));preempt_enable ();rcu_preempt_deferred_qs (current);for_each_rcu_flavor (rsp)
kernel/rcu/tree_exp.h +4 -5
@@ -259,11 +259,10 @@ static void rcu_report_exp_cpu_mult(struct rcu_state *rsp, struct rcu_node *rnp,/* * Report expedited quiescent state for specified rcu_data (CPU). */ -static void rcu_report_exp_rdp (struct rcu_state *rsp,struct rcu_data *rdp, -bool wake) +static void rcu_report_exp_rdp (struct rcu_state *rsp,struct rcu_data *rdp) {WRITE_ONCE (rdp->deferred_qs, false); -rcu_report_exp_cpu_mult (rsp, rdp->mynode, rdp->grpmask, wake); +rcu_report_exp_cpu_mult (rsp, rdp->mynode, rdp->grpmask, true); }/* Common code for synchronize_{rcu,sched}_expedited() work-done checking. */
@@ -352,7 +351,7 @@ static void sync_sched_exp_handler(void *data)return ;if (rcu_is_cpu_rrupt_from_idle ()) {rcu_report_exp_rdp (&rcu_sched_state, -this_cpu_ptr (&rcu_sched_data), true); +this_cpu_ptr (&rcu_sched_data));return ; }__this_cpu_write (rcu_sched_data.cpu_no_qs.b.exp, true);
@@ -750,7 +749,7 @@ static void sync_rcu_exp_handler(void *info)if (!t->rcu_read_lock_nesting) {if (!(preempt_count () & (PREEMPT_MASK |SOFTIRQ_MASK )) ||rcu_dynticks_curr_cpu_in_eqs ()) { -rcu_report_exp_rdp (rsp, rdp, true); +rcu_report_exp_rdp (rsp, rdp); }else { rdp->deferred_qs = true;resched_cpu (rdp->cpu);
kernel/rcu/tree_plugin.h +3 -3
@@ -285,7 +285,7 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp)* still in a quiescent state in any case.) */ if (blkd_state &RCU_EXP_BLKD && rdp->deferred_qs) -rcu_report_exp_rdp (rdp->rsp, rdp, true); +rcu_report_exp_rdp (rdp->rsp, rdp);else WARN_ON_ONCE (rdp->deferred_qs); }
@@ -383,7 +383,7 @@ static void rcu_preempt_note_context_switch(bool preempt)*/ rcu_preempt_qs ();if (rdp->deferred_qs) -rcu_report_exp_rdp (rcu_state_p, rdp, true); +rcu_report_exp_rdp (rcu_state_p, rdp); }/*
@@ -508,7 +508,7 @@ rcu_preempt_deferred_qs_irqrestore(struct task_struct *t, unsigned long flags)* blocked-tasks list below. */ if (rdp->deferred_qs) { -rcu_report_exp_rdp (rcu_state_p, rdp, true); +rcu_report_exp_rdp (rcu_state_p, rdp);if (!t->rcu_read_unlock_special.s) {local_irq_restore (flags);return ;
Signature · 1 line
2.17.1
PATCH 12/19 rcu: Fix typo in rcu_get_gp_kthreads_prio() header comment
In reply to Paul E. McKenney--- kernel/rcu/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/rcu/tree.c +1 -1
@@ -181,7 +181,7 @@ module_param(gp_init_delay, int, 0444);static int gp_cleanup_delay;module_param (gp_cleanup_delay, int,0444 ); -/* Retreive RCU kthreads priority for rcutorture */ +/* Retrieve RCU kthreads priority for rcutorture */ int rcu_get_gp_kthreads_prio (void ) {return kthread_prio;
Signature · 1 line
2.17.1
PATCH 13/19 rcu: Define RCU-sched API in terms of RCU for Tree RCU PREEMPT builds
In reply to Paul E. McKenneyNow that RCU-preempt knows about preemption disabling, its implementation of synchronize_rcu() works for synchronize_sched(), and likewise for the other RCU-sched update-side API members. This commit therefore confines the RCU-sched update-side code to CONFIG_PREEMPT=n builds, and defines RCU-sched's update-side API members in terms of those of RCU-preempt.
This means that any given build of the Linux kernel has only one update-side flavor of RCU, namely RCU-preempt for CONFIG_PREEMPT=y builds and RCU-sched for CONFIG_PREEMPT=n builds. This in turn means that kernels built with CONFIG_RCU_NOCB_CPU=y have only one rcuo kthread per CPU.
--- include/linux/rcupdate.h | 14 +- include/linux/rcutiny.h | 7 + include/linux/rcutree.h | 7 +- kernel/rcu/tree.c | 301 +++++++++++++-------------------------- kernel/rcu/tree.h | 9 +- kernel/rcu/tree_exp.h | 153 ++++++++++---------- kernel/rcu/tree_plugin.h | 297 ++++++++++++++------------------------ 7 files changed, 308 insertions(+), 480 deletions(-)
include/linux/rcupdate.h +4 -10
@@ -49,11 +49,11 @@/* Exported common interfaces */ -#ifdef CONFIG_PREEMPT_RCU -void call_rcu (struct rcu_head *head,rcu_callback_t func); -#else /* #ifdef CONFIG_PREEMPT_RCU */ +#ifdef CONFIG_TINY_RCU #define call_rcu call_rcu_sched -#endif /* #else #ifdef CONFIG_PREEMPT_RCU */ +#else +void call_rcu (struct rcu_head *head,rcu_callback_t func); +#endif void call_rcu_sched (struct rcu_head *head,rcu_callback_t func);void synchronize_sched (void );
@@ -92,11 +92,6 @@ static inline void __rcu_read_unlock(void)preempt_enable (); } -static inline void synchronize_rcu (void ) -{ -synchronize_sched (); -} -static inline int rcu_preempt_depth (void ) {return 0 ;
@@ -107,7 +102,6 @@ static inline int rcu_preempt_depth(void)/* Internal to kernel */ void rcu_init (void );extern int rcu_scheduler_active__read_mostly ; -void rcu_sched_qs (void );void rcu_check_callbacks (int user);void rcu_report_dead (unsigned int cpu);void rcutree_migrate_callbacks (int cpu);
include/linux/rcutiny.h +7 -0
@@ -36,6 +36,11 @@ static inline int rcu_dynticks_snap(struct rcu_dynticks *rdtp)/* Never flag non-existent other CPUs! */ static inline bool rcu_eqs_special_set (int cpu) {return false; } +static inline void synchronize_rcu (void ) +{ +synchronize_sched (); +} +static inline unsigned long get_state_synchronize_rcu (void ) {return 0 ;
@@ -94,6 +99,8 @@ static inline void kfree_call_rcu(struct rcu_head *head,call_rcu (head, func); } +void rcu_sched_qs (void ); +static inline void rcu_softirq_qs (void ) {rcu_sched_qs ();
include/linux/rcutree.h +6 -1
@@ -45,14 +45,19 @@ static inline void rcu_virt_note_context_switch(int cpu)rcu_note_context_switch (false); } +void synchronize_rcu (void );static inline void synchronize_rcu_bh (void ) {synchronize_rcu (); } -void synchronize_sched_expedited (void );void synchronize_rcu_expedited (void ); +static inline void synchronize_sched_expedited (void ) +{ +synchronize_rcu_expedited (); +} +void kfree_call_rcu (struct rcu_head *head,rcu_callback_t func);/**
kernel/rcu/tree.c +102 -199
456 lines not on this page · raw message
kernel/rcu/tree.h +2 -7
@@ -225,9 +225,6 @@ struct rcu_data {/* 5) _rcu_barrier(), OOM callbacks, and expediting. */ struct rcu_head barrier_head; -#ifdef CONFIG_RCU_FAST_NO_HZ -struct rcu_head oom_head; -#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */ int exp_dynticks_snap;/* Double-check need for IPI. */ /* 6) Callback offloading. */
@@ -433,8 +430,7 @@ DECLARE_PER_CPU(char, rcu_cpu_has_work);/* Forward declarations for rcutree_plugin.h */ static void rcu_bootup_announce (void ); -static void rcu_preempt_qs (void ); -static void rcu_preempt_note_context_switch (bool preempt); +static void rcu_qs (void );static int rcu_preempt_blocked_readers_cgp (struct rcu_node *rnp);#ifdef CONFIG_HOTPLUG_CPU static bool rcu_preempt_has_tasks (struct rcu_node *rnp);
@@ -444,9 +440,8 @@ static int rcu_print_task_stall(struct rcu_node *rnp);static int rcu_print_task_exp_stall (struct rcu_node *rnp);static void rcu_preempt_check_blocked_tasks (struct rcu_state *rsp,struct rcu_node *rnp); -static void rcu_preempt_check_callbacks (void ); +static void rcu_flavor_check_callbacks (int user);void call_rcu (struct rcu_head *head,rcu_callback_t func); -static void __init __rcu_init_preempt (void );static void dump_blkd_tasks (struct rcu_state *rsp,struct rcu_node *rnp,int ncheck);static void rcu_initiate_boost (struct rcu_node *rnp,unsigned long flags);
kernel/rcu/tree_exp.h +77 -76
193 lines not on this page · raw message
kernel/rcu/tree_plugin.h +110 -187
425 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH 14/19 rcu: Express Tiny RCU updates in terms of RCU rather than RCU-sched
In reply to Paul E. McKenneyThis commit renames Tiny RCU functions so that the lowest level of functionality is RCU (e.g., synchronize_rcu()) rather than RCU-sched (e.g., synchronize_sched()). This provides greater naming compatibility with Tree RCU, which will in turn permit more LoC removal once the RCU-sched and RCU-bh update-side API is removed.
[ paulmck: Fix Tiny call_rcu()'s EXPORT_SYMBOL() in response to a bug
report from kbuild test robot. ]
--- include/linux/rcupdate.h | 12 +++++----- include/linux/rcutiny.h | 34 +++++++++++++++------------- include/linux/rcutree.h | 1 - kernel/rcu/tiny.c | 48 ++++++++++++++++++++-------------------- 4 files changed, 48 insertions(+), 47 deletions(-)
include/linux/rcupdate.h +5 -7
@@ -49,15 +49,14 @@/* Exported common interfaces */ -#ifdef CONFIG_TINY_RCU -#define call_rcu call_rcu_sched -#else -void call_rcu (struct rcu_head *head,rcu_callback_t func); +#ifndef CONFIG_TINY_RCU +void synchronize_sched (void ); +void call_rcu_sched (struct rcu_head *head,rcu_callback_t func);#endif -void call_rcu_sched (struct rcu_head *head,rcu_callback_t func); -void synchronize_sched (void ); +void call_rcu (struct rcu_head *head,rcu_callback_t func);void rcu_barrier_tasks (void ); +void synchronize_rcu (void );static inline void call_rcu_bh (struct rcu_head *head,rcu_callback_t func) {
@@ -68,7 +67,6 @@ static inline void call_rcu_bh(struct rcu_head *head, rcu_callback_t func)void __rcu_read_lock (void );void __rcu_read_unlock (void ); -void synchronize_rcu (void );/* * Defined as a macro as it is a very low level header included from
include/linux/rcutiny.h +19 -15
@@ -36,9 +36,9 @@ static inline int rcu_dynticks_snap(struct rcu_dynticks *rdtp)/* Never flag non-existent other CPUs! */ static inline bool rcu_eqs_special_set (int cpu) {return false; } -static inline void synchronize_rcu (void ) +static inline void synchronize_sched (void ) { -synchronize_sched (); +synchronize_rcu (); }static inline unsigned long get_state_synchronize_rcu (void )
@@ -61,16 +61,11 @@ static inline void cond_synchronize_sched(unsigned long oldstate)might_sleep (); } -static inline void synchronize_rcu_expedited (void ) -{ -synchronize_sched ();/* Only one CPU, so pretty fast anyway!!! */ -} +extern void rcu_barrier (void ); -extern void rcu_barrier_sched (void ); - -static inline void rcu_barrier (void ) +static inline void rcu_barrier_sched (void ) { -rcu_barrier_sched ();/* Only one CPU, so only one list of callbacks! */ +rcu_barrier ();/* Only one CPU, so only one list of callbacks! */ }static inline void rcu_barrier_bh (void )
@@ -88,27 +83,36 @@ static inline void synchronize_rcu_bh_expedited(void)synchronize_sched (); } +static inline void synchronize_rcu_expedited (void ) +{ +synchronize_sched (); +} +static inline void synchronize_sched_expedited (void ) {synchronize_sched (); } -static inline void kfree_call_rcu (struct rcu_head *head, -rcu_callback_t func) +static inline void call_rcu_sched (struct rcu_head *head,rcu_callback_t func) +{ +call_rcu (head, func); +} + +static inline void kfree_call_rcu (struct rcu_head *head,rcu_callback_t func) {call_rcu (head, func); } -void rcu_sched_qs (void ); +void rcu_qs (void );static inline void rcu_softirq_qs (void ) { -rcu_sched_qs (); +rcu_qs (); }#define rcu_note_context_switch (preempt) \ do { \ - rcu_sched_qs(); \ + rcu_qs(); \ rcu_tasks_qs(current); \ } while (0)
include/linux/rcutree.h +0 -1
@@ -45,7 +45,6 @@ static inline void rcu_virt_note_context_switch(int cpu)rcu_note_context_switch (false); } -void synchronize_rcu (void );static inline void synchronize_rcu_bh (void ) {synchronize_rcu ();
kernel/rcu/tiny.c +24 -24
110 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH 15/19 rcu: Remove RCU_STATE_INITIALIZER()
In reply to Paul E. McKenneyNow that a given build of the Linux kernel has only one set of rcu_state, rcu_node, and rcu_data structures, there is no point in creating a macro to declare and compile-time initialize them. This commit therefore just does normal declaration and compile-time initialization of these structures. While in the area, this commit also removes #ifndefs of the no-longer-ever-defined preprocessor macro RCU_TREE_NONCORE.
--- kernel/rcu/tree.c | 54 ++++++++++++----------------------------------- kernel/rcu/tree.h | 29 +++++++++++++++++++------ 2 files changed, 37 insertions(+), 46 deletions(-)
kernel/rcu/tree.c +14 -40
@@ -72,46 +72,20 @@/* Data structures. */ -/* -* In order to export the rcu_state name to the tracing tools, it -* needs to be added in the __tracepoint_string section. -* This requires defining a separate variable tp_<sname>_varname -* that points to the string being used, and this will allow -* the tracing userspace tools to be able to decipher the string -* address to the matching string. -*/ -#ifdef CONFIG_TRACING -# define DEFINE_RCU_TPS (sname) \ -static char sname##_varname[] = #sname; \ -static const char *tp_##sname##_varname __used __tracepoint_string = sname##_varname; -# define RCU_STATE_NAME (sname) sname##_varname -#else -# define DEFINE_RCU_TPS (sname) -# define RCU_STATE_NAME (sname) __stringify(sname) -#endif - -#define RCU_STATE_INITIALIZER (sname, sabbr, cr) \ -DEFINE_RCU_TPS(sname) \ -static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data); \ -struct rcu_state rcu_state = { \ - .level = { &rcu_state.node[0] }, \ - .rda = &rcu_data, \ - .call = cr, \ - .gp_state = RCU_GP_IDLE, \ - .gp_seq = (0UL - 300UL) << RCU_SEQ_CTR_SHIFT, \ - .barrier_mutex = __MUTEX_INITIALIZER(rcu_state.barrier_mutex), \ - .name = RCU_STATE_NAME(sname), \ - .abbr = sabbr, \ - .exp_mutex = __MUTEX_INITIALIZER(rcu_state.exp_mutex), \ - .exp_wake_mutex = __MUTEX_INITIALIZER(rcu_state.exp_wake_mutex), \ - .ofl_lock = __SPIN_LOCK_UNLOCKED(rcu_state.ofl_lock), \ -} - -#ifdef CONFIG_PREEMPT_RCU -RCU_STATE_INITIALIZER (rcu_preempt,'p' , call_rcu); -#else -RCU_STATE_INITIALIZER (rcu_sched,'s' , call_rcu); -#endif +static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data); +struct rcu_state rcu_state = { + .level = { &rcu_state.node[0 ] }, + .rda = &rcu_data, + .call = call_rcu, + .gp_state =RCU_GP_IDLE , + .gp_seq = (0UL -300UL ) <<RCU_SEQ_CTR_SHIFT , + .barrier_mutex =__MUTEX_INITIALIZER (rcu_state.barrier_mutex), + .name =RCU_NAME , + .abbr =RCU_ABBR , + .exp_mutex =__MUTEX_INITIALIZER (rcu_state.exp_mutex), + .exp_wake_mutex =__MUTEX_INITIALIZER (rcu_state.exp_wake_mutex), + .ofl_lock =__SPIN_LOCK_UNLOCKED (rcu_state.ofl_lock), +};static struct rcu_state *const rcu_state_p = &rcu_state;static struct rcu_data __percpu *const rcu_data_p = &rcu_data;
kernel/rcu/tree.h +23 -6
@@ -386,7 +386,6 @@ struct rcu_state {#define RCU_GP_CLEANUP 7/* Grace-period cleanup started. */ #define RCU_GP_CLEANED 8/* Grace-period cleanup complete. */ -#ifndef RCU_TREE_NONCORE static const char *const gp_state_names[] = {"RCU_GP_IDLE" ,"RCU_GP_WAIT_GPS" ,
@@ -398,7 +397,29 @@ static const char * const gp_state_names[] = {"RCU_GP_CLEANUP" , "RCU_GP_CLEANED", }; -#endif /* #ifndef RCU_TREE_NONCORE */ + +/* +* In order to export the rcu_state name to the tracing tools, it +* needs to be added in the __tracepoint_string section. +* This requires defining a separate variable tp_<sname>_varname +* that points to the string being used, and this will allow +* the tracing userspace tools to be able to decipher the string +* address to the matching string. +*/ +#ifdef CONFIG_PREEMPT_RCU +#define RCU_ABBR 'p' +#define RCU_NAME_RAW "rcu_preempt" +#else /* #ifdef CONFIG_PREEMPT_RCU */ +#define RCU_ABBR 's' +#define RCU_NAME_RAW "rcu_sched" +#endif /* #else #ifdef CONFIG_PREEMPT_RCU */ +#ifndef CONFIG_TRACING +#define RCU_NAME RCU_NAME_RAW +#else /* #ifdef CONFIG_TRACING */ +static char rcu_name[] =RCU_NAME_RAW ; +static const char *tp_rcu_varname __used __tracepoint_string = rcu_name; +#define RCU_NAME rcu_name +#endif /* #else #ifdef CONFIG_TRACING */ extern struct list_head rcu_struct_flavors;
@@ -426,8 +447,6 @@ DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_loops);DECLARE_PER_CPU (char, rcu_cpu_has_work);#endif /* #ifdef CONFIG_RCU_BOOST */ -#ifndef RCU_TREE_NONCORE -/* Forward declarations for rcutree_plugin.h */ static void rcu_bootup_announce (void );static void rcu_qs (void );
@@ -495,5 +514,3 @@ void srcu_offline_cpu(unsigned int cpu);void srcu_online_cpu (unsigned int cpu) { }void srcu_offline_cpu (unsigned int cpu) { }#endif /* #else #ifdef CONFIG_SRCU */ - -#endif /* #ifndef RCU_TREE_NONCORE */
Signature · 1 line
2.17.1
PATCH 16/19 rcu: Eliminate rcu_state structure's ->call field
In reply to Paul E. McKenneyThe rcu_state structure's ->call field references the corresponding RCU flavor's call_rcu() function. However, now that there is only ever one rcu_state structure in a given build of the Linux kernel, and that flavor uses plain old call_rcu(), there is not a lot of point in continuing to have the ->call field. This commit therefore removes it.
--- kernel/rcu/tree.c | 1 - kernel/rcu/tree.h | 1 - kernel/rcu/tree_exp.h | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-)
kernel/rcu/tree.c +0 -1
@@ -76,7 +76,6 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data);struct rcu_state rcu_state = { .level = { &rcu_state.node[0 ] }, .rda = &rcu_data, - .call = call_rcu, .gp_state =RCU_GP_IDLE , .gp_seq = (0UL -300UL ) <<RCU_SEQ_CTR_SHIFT , .barrier_mutex =__MUTEX_INITIALIZER (rcu_state.barrier_mutex),
kernel/rcu/tree.h +0 -1
@@ -313,7 +313,6 @@ struct rcu_state {/* Hierarchy levels (+1 to */ /* shut bogus gcc warning) */ struct rcu_data __percpu *rda;/* pointer of percu rcu_data. */ -call_rcu_func_t call;/* call_rcu() flavor. */ int ncpus;/* # CPUs seen so far. */ /* The following fields are guarded by the root rcu_node's lock. */
kernel/rcu/tree_exp.h +1 -1
@@ -619,7 +619,7 @@ static void _synchronize_rcu_expedited(struct rcu_state *rsp,/* If expedited grace periods are prohibited, fall back to normal. */ if (rcu_gp_is_normal ()) { -wait_rcu_gp (rsp->call); +wait_rcu_gp (call_rcu);return ; }
Signature · 1 line
2.17.1
PATCH 17/19 rcu: Remove rcu_state structure's ->rda field
In reply to Paul E. McKenneyThe rcu_state structure's ->rda field was used to find the per-CPU rcu_data structures corresponding to that rcu_state structure. But now there is only one rcu_state structure (creatively named "rcu_state") and one set of per-CPU rcu_data structures (creatively named "rcu_data"). Therefore, uses of the ->rda field can always be replaced by "rcu_data, and this commit makes that change and removes the ->rda field.
--- kernel/rcu/tree.c | 67 ++++++++++++++++++++-------------------- kernel/rcu/tree.h | 1 - kernel/rcu/tree_exp.h | 19 ++++++------ kernel/rcu/tree_plugin.h | 24 +++++++------- 4 files changed, 54 insertions(+), 57 deletions(-)
kernel/rcu/tree.c +33 -34
@@ -75,7 +75,6 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data);struct rcu_state rcu_state = { .level = { &rcu_state.node[0 ] }, - .rda = &rcu_data, .gp_state =RCU_GP_IDLE , .gp_seq = (0UL -300UL ) <<RCU_SEQ_CTR_SHIFT , .barrier_mutex =__MUTEX_INITIALIZER (rcu_state.barrier_mutex),
@@ -586,7 +585,7 @@ void show_rcu_gp_kthreads(void)if (!rcu_is_leaf_node (rnp))continue ;for_each_leaf_node_possible_cpu (rnp, cpu) { - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu);if (rdp->gpwrap ||ULONG_CMP_GE (rsp->gp_seq, rdp->gp_seq_needed))
@@ -660,7 +659,7 @@ static void rcu_eqs_enter(bool user)trace_rcu_dyntick (TPS ("Start" ), rdtp->dynticks_nesting,0 , rdtp->dynticks);WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current));for_each_rcu_flavor (rsp) { - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data);do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle ();
@@ -1033,7 +1032,7 @@ bool rcu_lockdep_current_cpu_online(void)return true;preempt_disable ();for_each_rcu_flavor (rsp) { - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data); rnp = rdp->mynode;if (rdp->grpmask &rcu_rnp_online_cpus (rnp)) {preempt_enable ();
@@ -1351,7 +1350,7 @@ static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gp_seq)print_cpu_stall_info_end ();for_each_possible_cpu (cpu) - totqlen +=rcu_segcblist_n_cbs (&per_cpu_ptr (rsp->rda, + totqlen +=rcu_segcblist_n_cbs (&per_cpu_ptr (&rcu_data, cpu)->cblist); pr_cont("(detected by %d, t=%ld jiffies, g=%ld, q=%lu)\n", smp_processor_id(), (long)(jiffies - rsp->gp_start),
@@ -1391,7 +1390,7 @@ static void print_cpu_stall(struct rcu_state *rsp) {int cpu;unsigned long flags; -struct rcu_data *rdp =this_cpu_ptr (rsp->rda); +struct rcu_data *rdp =this_cpu_ptr (&rcu_data);struct rcu_node *rnp =rcu_get_root (rsp);long totqlen =0 ;
@@ -1412,7 +1411,7 @@ static void print_cpu_stall(struct rcu_state *rsp)raw_spin_unlock_irqrestore_rcu_node (rdp->mynode, flags);print_cpu_stall_info_end ();for_each_possible_cpu (cpu) - totqlen +=rcu_segcblist_n_cbs (&per_cpu_ptr (rsp->rda, + totqlen +=rcu_segcblist_n_cbs (&per_cpu_ptr (&rcu_data, cpu)->cblist); pr_cont(" (t=%lu jiffies g=%ld q=%lu)\n", jiffies - rsp->gp_start,
@@ -1623,7 +1622,7 @@ static bool rcu_start_this_gp(struct rcu_node *rnp_start, struct rcu_data *rdp,static bool rcu_future_gp_cleanup (struct rcu_state *rsp,struct rcu_node *rnp) {bool needmore; -struct rcu_data *rdp =this_cpu_ptr (rsp->rda); +struct rcu_data *rdp =this_cpu_ptr (&rcu_data); needmore =ULONG_CMP_LT (rnp->gp_seq, rnp->gp_seq_needed);if (!needmore)
@@ -1935,7 +1934,7 @@ static bool rcu_gp_init(struct rcu_state *rsp)rcu_for_each_node_breadth_first (rsp, rnp) {rcu_gp_slow (rsp, gp_init_delay);raw_spin_lock_irqsave_rcu_node (rnp, flags); - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data);rcu_preempt_check_blocked_tasks (rsp, rnp); rnp->qsmask = rnp->qsmaskinit;WRITE_ONCE (rnp->gp_seq, rsp->gp_seq);
@@ -2049,7 +2048,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)dump_blkd_tasks (rsp, rnp,10 );WARN_ON_ONCE (rnp->qsmask);WRITE_ONCE (rnp->gp_seq, new_gp_seq); - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data);if (rnp == rdp->mynode) needgp =__note_gp_changes (rsp, rnp, rdp) || needgp;/* smp_mb() provided by prior unlock-lock pair. */
@@ -2069,7 +2068,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)trace_rcu_grace_period (rsp->name, rsp->gp_seq,TPS ("end" )); rsp->gp_state =RCU_GP_IDLE ;/* Check for GP requests since above loop. */ - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data);if (!needgp &&ULONG_CMP_LT (rnp->gp_seq, rnp->gp_seq_needed)) {trace_rcu_this_gp (rnp, rdp, rnp->gp_seq_needed,TPS ("CleanupMore" ));
@@ -2404,7 +2403,7 @@ rcu_check_quiescent_state(struct rcu_state *rsp, struct rcu_data *rdp)static void rcu_cleanup_dying_cpu (struct rcu_state *rsp) { RCU_TRACE(bool blkd;) - RCU_TRACE(struct rcu_data *rdp = this_cpu_ptr(rsp->rda);) + RCU_TRACE(struct rcu_data *rdp = this_cpu_ptr(&rcu_data);) RCU_TRACE(struct rcu_node *rnp = rdp->mynode;)if (!IS_ENABLED (CONFIG_HOTPLUG_CPU ))
@@ -2468,7 +2467,7 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf)*/ static void rcu_cleanup_dead_cpu (int cpu,struct rcu_state *rsp) { -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);struct rcu_node *rnp = rdp->mynode;/* Outgoing CPU's rdp & rnp. */ if (!IS_ENABLED (CONFIG_HOTPLUG_CPU ))
@@ -2621,7 +2620,7 @@ static void force_qs_rnp(struct rcu_state *rsp, int (*f)(struct rcu_data *rsp))for_each_leaf_node_possible_cpu (rnp, cpu) {unsigned long bit =leaf_node_cpu_bit (rnp, cpu);if ((rnp->qsmask & bit) !=0 ) { -if (f (per_cpu_ptr (rsp->rda, cpu))) +if (f (per_cpu_ptr (&rcu_data, cpu))) mask |= bit; } }
@@ -2647,7 +2646,7 @@ static void force_quiescent_state(struct rcu_state *rsp)struct rcu_node *rnp_old =NULL ;/* Funnel through hierarchy to reduce memory contention. */ - rnp =__this_cpu_read (rsp->rda->mynode); + rnp =__this_cpu_read (rcu_data.mynode);for (; rnp !=NULL ; rnp = rnp->parent) { ret = (READ_ONCE (rsp->gp_flags) &RCU_GP_FLAG_FQS ) || !raw_spin_trylock (&rnp->fqslock);
@@ -2739,7 +2738,7 @@ static void __rcu_process_callbacks(struct rcu_state *rsp) {unsigned long flags; -struct rcu_data *rdp =raw_cpu_ptr (rsp->rda); +struct rcu_data *rdp =raw_cpu_ptr (&rcu_data);struct rcu_node *rnp = rdp->mynode;WARN_ON_ONCE (!rdp->beenonline);
@@ -2893,14 +2892,14 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func, head->func = func; head->next =NULL ;local_irq_save (flags); - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data);/* Add the callback to our list. */ if (unlikely (!rcu_segcblist_is_enabled (&rdp->cblist)) || cpu !=-1 ) {int offline;if (cpu !=-1 ) - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu);if (likely (rdp->mynode)) {/* Post-boot, so this should be for a no-CBs CPU. */ offline = !__call_rcu_nocb (rdp, head, lazy, flags);
@@ -3134,7 +3133,7 @@ static int rcu_pending(void)struct rcu_state *rsp;for_each_rcu_flavor (rsp) -if (__rcu_pending (rsp,this_cpu_ptr (rsp->rda))) +if (__rcu_pending (rsp,this_cpu_ptr (&rcu_data)))return 1 ;return 0 ; }
@@ -3152,7 +3151,7 @@ static bool rcu_cpu_has_callbacks(bool *all_lazy)struct rcu_state *rsp;for_each_rcu_flavor (rsp) { - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data);if (rcu_segcblist_empty (&rdp->cblist))continue ; hc = true;
@@ -3201,7 +3200,7 @@ static void rcu_barrier_callback(struct rcu_head *rhp)static void rcu_barrier_func (void *type) {struct rcu_state *rsp = type; -struct rcu_data *rdp =raw_cpu_ptr (rsp->rda); +struct rcu_data *rdp =raw_cpu_ptr (&rcu_data);_rcu_barrier_trace (rsp,TPS ("IRQ" ),-1 , rsp->barrier_sequence); rdp->barrier_head.func = rcu_barrier_callback;
@@ -3261,7 +3260,7 @@ static void _rcu_barrier(struct rcu_state *rsp)for_each_possible_cpu (cpu) {if (!cpu_online (cpu) && !rcu_is_nocb_cpu (cpu))continue ; - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu);if (rcu_is_nocb_cpu (cpu)) {if (!rcu_nocb_cpu_needs_barrier (rsp, cpu)) { _rcu_barrier_trace(rsp, TPS("OfflineNoCB"), cpu,
@@ -3371,7 +3370,7 @@ static void rcu_init_new_rnp(struct rcu_node *rnp_leaf)static void __init rcu_boot_init_percpu_data (int cpu,struct rcu_state *rsp) { -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);/* Set up local state, ensuring consistent view of global state. */ rdp->grpmask =leaf_node_cpu_bit (rdp->mynode, cpu);
@@ -3397,7 +3396,7 @@ static void rcu_init_percpu_data(int cpu, struct rcu_state *rsp) { unsigned long flags; -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);struct rcu_node *rnp =rcu_get_root (rsp);/* Set up local state, ensuring consistent view of global state. */
@@ -3453,7 +3452,7 @@ int rcutree_prepare_cpu(unsigned int cpu)*/ static void rcutree_affinity_setting (unsigned int cpu,int outgoing) { -struct rcu_data *rdp =per_cpu_ptr (rcu_state_p->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);rcu_boost_kthread_setaffinity (rdp->mynode, outgoing); }
@@ -3470,7 +3469,7 @@ int rcutree_online_cpu(unsigned int cpu)struct rcu_state *rsp;for_each_rcu_flavor (rsp) { - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu); rnp = rdp->mynode;raw_spin_lock_irqsave_rcu_node (rnp, flags); rnp->ffmask |= rdp->grpmask;
@@ -3497,7 +3496,7 @@ int rcutree_offline_cpu(unsigned int cpu)struct rcu_state *rsp;for_each_rcu_flavor (rsp) { - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu); rnp = rdp->mynode;raw_spin_lock_irqsave_rcu_node (rnp, flags); rnp->ffmask &= ~rdp->grpmask;
@@ -3531,7 +3530,7 @@ int rcutree_dead_cpu(unsigned int cpu)for_each_rcu_flavor (rsp) {rcu_cleanup_dead_cpu (cpu, rsp); -do_nocb_deferred_wakeup (per_cpu_ptr (rsp->rda, cpu)); +do_nocb_deferred_wakeup (per_cpu_ptr (&rcu_data, cpu)); }return 0 ; }
@@ -3565,7 +3564,7 @@ void rcu_cpu_starting(unsigned int cpu)per_cpu (rcu_cpu_started, cpu) =1 ;for_each_rcu_flavor (rsp) { - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu); rnp = rdp->mynode; mask = rdp->grpmask;raw_spin_lock_irqsave_rcu_node (rnp, flags);
@@ -3599,7 +3598,7 @@ static void rcu_cleanup_dying_idle_cpu(int cpu, struct rcu_state *rsp) {unsigned long flags;unsigned long mask; -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);struct rcu_node *rnp = rdp->mynode;/* Outgoing CPU's rdp & rnp. */ /* Remove outgoing CPU from mask in the leaf rcu_node structure. */
@@ -3632,7 +3631,7 @@ void rcu_report_dead(unsigned int cpu)/* QS for any half-done expedited RCU-sched GP. */ preempt_disable (); -rcu_report_exp_rdp (&rcu_state,this_cpu_ptr (rcu_state.rda)); +rcu_report_exp_rdp (&rcu_state,this_cpu_ptr (&rcu_data));preempt_enable ();rcu_preempt_deferred_qs (current);for_each_rcu_flavor (rsp)
@@ -3646,7 +3645,7 @@ static void rcu_migrate_callbacks(int cpu, struct rcu_state *rsp) {unsigned long flags;struct rcu_data *my_rdp; -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);struct rcu_node *rnp_root =rcu_get_root (rdp->rsp);bool needwake;
@@ -3654,7 +3653,7 @@ static void rcu_migrate_callbacks(int cpu, struct rcu_state *rsp)return ;/* No callbacks to migrate. */ local_irq_save (flags); - my_rdp =this_cpu_ptr (rsp->rda); + my_rdp =this_cpu_ptr (&rcu_data);if (rcu_nocb_adopt_orphan_cbs (my_rdp, rdp, flags)) {local_irq_restore (flags);return ;
@@ -3856,7 +3855,7 @@ static void __init rcu_init_one(struct rcu_state *rsp)for_each_possible_cpu (i) {while (i > rnp->grphi) rnp++; -per_cpu_ptr (rsp->rda, i)->mynode = rnp; +per_cpu_ptr (&rcu_data, i)->mynode = rnp;rcu_boot_init_percpu_data (i, rsp); }list_add (&rsp->flavors, &rcu_struct_flavors);
kernel/rcu/tree.h +0 -1
@@ -312,7 +312,6 @@ struct rcu_state {struct rcu_node *level[RCU_NUM_LVLS +1 ];/* Hierarchy levels (+1 to */ /* shut bogus gcc warning) */ -struct rcu_data __percpu *rda;/* pointer of percu rcu_data. */ int ncpus;/* # CPUs seen so far. */ /* The following fields are guarded by the root rcu_node's lock. */
kernel/rcu/tree_exp.h +9 -10
@@ -286,7 +286,7 @@ static bool sync_exp_work_done(struct rcu_state *rsp, unsigned long s)*/ static bool exp_funnel_lock (struct rcu_state *rsp,unsigned long s) { -struct rcu_data *rdp =per_cpu_ptr (rsp->rda,raw_smp_processor_id ()); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data,raw_smp_processor_id ());struct rcu_node *rnp = rdp->mynode;struct rcu_node *rnp_root =rcu_get_root (rsp);
@@ -361,7 +361,7 @@ static void sync_rcu_exp_select_node_cpus(struct work_struct *wp) mask_ofl_test =0 ;for_each_leaf_node_cpu_mask (rnp, cpu, rnp->expmask) {unsigned long mask =leaf_node_cpu_bit (rnp, cpu); -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);struct rcu_dynticks *rdtp =per_cpu_ptr (&rcu_dynticks, cpu);int snap;
@@ -390,7 +390,7 @@ static void sync_rcu_exp_select_node_cpus(struct work_struct *wp)/* IPI the remaining CPUs for expedited quiescent state. */ for_each_leaf_node_cpu_mask (rnp, cpu, rnp->expmask) {unsigned long mask =leaf_node_cpu_bit (rnp, cpu); -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);if (!(mask_ofl_ipi & mask))continue ;
@@ -509,7 +509,7 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)if (!(rnp->expmask & mask))continue ; ndetected++; - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu); pr_cont(" %d-%c%c%c", cpu, "O."[!!cpu_online(cpu)], "o."[!!(rdp->grpmask & rnp->expmaskinit)],
@@ -642,7 +642,7 @@ static void _synchronize_rcu_expedited(struct rcu_state *rsp, }/* Wait for expedited grace period to complete. */ - rdp =per_cpu_ptr (rsp->rda,raw_smp_processor_id ()); + rdp =per_cpu_ptr (&rcu_data,raw_smp_processor_id ()); rnp =rcu_get_root (rsp);wait_event (rnp->exp_wq[rcu_seq_ctr (s) &0x3 ],sync_exp_work_done (rsp, s));
@@ -665,7 +665,7 @@ static void sync_rcu_exp_handler(void *info) {unsigned long flags;struct rcu_state *rsp = info; -struct rcu_data *rdp =this_cpu_ptr (rsp->rda); +struct rcu_data *rdp =this_cpu_ptr (&rcu_data);struct rcu_node *rnp = rdp->mynode;struct task_struct *t = current;
@@ -772,13 +772,12 @@ EXPORT_SYMBOL_GPL(synchronize_rcu_expedited);#else /* #ifdef CONFIG_PREEMPT_RCU */ /* Invoked on each online non-idle CPU for expedited quiescent state. */ -static void sync_sched_exp_handler (void *data) +static void sync_sched_exp_handler (void *unused) {struct rcu_data *rdp;struct rcu_node *rnp; -struct rcu_state *rsp = data; - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data); rnp = rdp->mynode;if (!(READ_ONCE (rnp->expmask) & rdp->grpmask) ||__this_cpu_read (rcu_data.cpu_no_qs.b.exp))
@@ -801,7 +800,7 @@ static void sync_sched_exp_online_cleanup(int cpu)struct rcu_node *rnp;struct rcu_state *rsp = &rcu_state; - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu); rnp = rdp->mynode;if (!(READ_ONCE (rnp->expmask) & rdp->grpmask))return ;
kernel/rcu/tree_plugin.h +12 -12
@@ -328,7 +328,7 @@ static void rcu_qs(void)void rcu_note_context_switch (bool preempt) {struct task_struct *t = current; -struct rcu_data *rdp =this_cpu_ptr (rcu_state_p->rda); +struct rcu_data *rdp =this_cpu_ptr (&rcu_data);struct rcu_node *rnp;barrier ();/* Avoid RCU read-side critical sections leaking down. */
@@ -488,7 +488,7 @@ rcu_preempt_deferred_qs_irqrestore(struct task_struct *t, unsigned long flags)* t->rcu_read_unlock_special cannot change. */ special = t->rcu_read_unlock_special; - rdp =this_cpu_ptr (rcu_state_p->rda); + rdp =this_cpu_ptr (&rcu_data);if (!special.s && !rdp->deferred_qs) {local_irq_restore (flags);return ;
@@ -911,7 +911,7 @@ dump_blkd_tasks(struct rcu_state *rsp, struct rcu_node *rnp, int ncheck) }pr_cont ("\n" );for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++) { - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu); onl = !!(rdp->grpmask &rcu_rnp_online_cpus (rnp)); pr_info("\t%d: %c online: %ld(%d) offline: %ld(%d)\n", cpu, ".o"[onl],
@@ -1437,7 +1437,7 @@ static void __init rcu_spawn_boost_kthreads(void)static void rcu_prepare_kthreads (int cpu) { -struct rcu_data *rdp =per_cpu_ptr (rcu_state_p->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);struct rcu_node *rnp = rdp->mynode;/* Fire up the incoming CPU's kthread and leaf rcu_node kthread. */
@@ -1574,7 +1574,7 @@ static bool __maybe_unused rcu_try_advance_all_cbs(void) rdtp->last_advance_all = jiffies;for_each_rcu_flavor (rsp) { - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data); rnp = rdp->mynode;/*
@@ -1692,7 +1692,7 @@ static void rcu_prepare_for_idle(void)return ; rdtp->last_accelerate = jiffies;for_each_rcu_flavor (rsp) { - rdp =this_cpu_ptr (rsp->rda); + rdp =this_cpu_ptr (&rcu_data);if (!rcu_segcblist_pend_cbs (&rdp->cblist))continue ; rnp = rdp->mynode;
@@ -1778,7 +1778,7 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu) {unsigned long delta;char fast_no_hz[72 ]; -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);struct rcu_dynticks *rdtp = rdp->dynticks;char *ticks_title;unsigned long ticks_value;
@@ -1833,7 +1833,7 @@ static void increment_cpu_stall_ticks(void)struct rcu_state *rsp;for_each_rcu_flavor (rsp) -raw_cpu_inc (rsp->rda->ticks_this_gp); +raw_cpu_inc (rcu_data.ticks_this_gp); }#ifdef CONFIG_RCU_NOCB_CPU
@@ -1965,7 +1965,7 @@ static void wake_nocb_leader_defer(struct rcu_data *rdp, int waketype,*/ static bool rcu_nocb_cpu_needs_barrier (struct rcu_state *rsp,int cpu) { -struct rcu_data *rdp =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp =per_cpu_ptr (&rcu_data, cpu);unsigned long ret;#ifdef CONFIG_PROVE_RCU struct rcu_head *rhp;
@@ -2426,7 +2426,7 @@ void __init rcu_init_nohz(void)for_each_rcu_flavor (rsp) {for_each_cpu (cpu, rcu_nocb_mask) -init_nocb_callback_list (per_cpu_ptr (rsp->rda, cpu)); +init_nocb_callback_list (per_cpu_ptr (&rcu_data, cpu));rcu_organize_nocb_kthreads (rsp); } }
@@ -2452,7 +2452,7 @@ static void rcu_spawn_one_nocb_kthread(struct rcu_state *rsp, int cpu)struct rcu_data *rdp;struct rcu_data *rdp_last;struct rcu_data *rdp_old_leader; -struct rcu_data *rdp_spawn =per_cpu_ptr (rsp->rda, cpu); +struct rcu_data *rdp_spawn =per_cpu_ptr (&rcu_data, cpu);struct task_struct *t;/*
@@ -2545,7 +2545,7 @@ static void __init rcu_organize_nocb_kthreads(struct rcu_state *rsp)* we will spawn the needed set of rcu_nocb_kthread() kthreads. */ for_each_cpu (cpu, rcu_nocb_mask) { - rdp =per_cpu_ptr (rsp->rda, cpu); + rdp =per_cpu_ptr (&rcu_data, cpu);if (rdp->cpu >= nl) {/* New leader, set up for followers & next leader. */ nl =DIV_ROUND_UP (rdp->cpu +1 , ls) * ls;
Signature · 1 line
2.17.1
Quoted 51 lines · Paul E. McKenney
Hello!
This series contains the RCU flavor consolidation, along with some initial cleanup work enabled by that consolidation (and some that became apparent while cleaning up):
1. Refactor rcu_{nmi,irq}_{enter,exit}(), saving a branch on the
idle entry/exit hotpaths, courtesy of Byungchul Park,2. Defer reporting RCU-preempt quiescent states when disabled.
This is the key commit that consolidates the RCU-bh and RCU-sched flavors into RCU, however, the RCU-bh and RCU-sched flavors still exist independently as well at this point.3. Test extended "rcu" read-side critical sections. This commit
causes rcutorture to test RCU's new-found ability to act as the combination of RCU, RCU-bh, and RCU-sched.4. Allow processing deferred QSes for exiting RCU-preempt readers.
This is a optimization.5. Remove now-unused ->b.exp_need_qs field from the
rcu_specialunion.6. Add warning to detect half-interrupts. Test the claim that
the Linux kernel no longer does half-interrupts.7. Apply RCU-bh QSes to RCU-sched and RCU-preempt when safe, that is,
make the consolidated RCU inherit RCU-bh's denial-of-service avoidance mechanism.8. Report expedited grace periods at context-switch time. This is
an optimization enabled by the RCU flavor consolidation.9. Define RCU-bh update API in terms of RCU. This commit gets rid
of the RCU-bh update mechanism.10. Update comments and help text to account for the removal of
the RCU-bh update mechanism.11. Drop "wake" parameter from
rcu_report_exp_rdp().12. Fix typo in
rcu_get_gp_kthreads_prio()header comment.13. Define RCU-sched API in terms of RCU for Tree RCU PREEMPT builds.
Although this commit gets rid of the RCU-sched update mechanism from PREEMPT builds, it of course remains as the sole RCU flavor for !PREEMPT && !SMP builds.14. Express Tiny RCU updates in terms of RCU rather than RCU-sched.
This will enable additional cleanups and code savings.
Quoted 8 lines
15. Remove
RCU_STATE_INITIALIZER()in favor of just using an open-coded
initializer for the sole remainingrcu_statestructure.16. Eliminate
rcu_statestructure's ->call field, as it is now always
justcall_rcu().17. Remove
rcu_statestructure's ->rda field, as there is now only one
set of per-CPUrcu_datastructures.
And last, but perhaps not least:
18. Remove rcu_state_p pointer to default rcu_state structure, now
that there is only one rcu_state structure.
19. Remove rcu_data_p pointer to default rcu_data structure, now that
there is only one set of per-CPU rcu_data structures.
Thanx, Paul
Quoted 18 lines
------------------------------------------------------------------------
Documentation/RCU/Design/Requirements/Requirements.html | 50 - include/linux/rcupdate.h | 48 - include/linux/rcupdate_wait.h | 6 include/linux/rcutiny.h | 61 + include/linux/rcutree.h | 31 include/linux/sched.h | 6 kernel/rcu/Kconfig | 10 kernel/rcu/rcutorture.c | 1 kernel/rcu/tiny.c | 163 +-- kernel/rcu/tree.c | 655 +++++----------- kernel/rcu/tree.h | 44 - kernel/rcu/tree_exp.h | 256 +++--- kernel/rcu/tree_plugin.h | 523 ++++++------ kernel/rcu/update.c | 2 kernel/softirq.c | 3 15 files changed, 836 insertions(+), 1023 deletions(-)
PATCH 18/19 rcu: Remove rcu_state_p pointer to default rcu_state structure
In reply to Paul E. McKenneyThe rcu_state_p pointer references the default rcu_state structure, that is, the one that call_rcu() uses, as opposed to call_rcu_bh() and sometimes call_rcu_sched(). But there is now only one rcu_state structure, so that one structure is by definition the default, which means that the rcu_state_p pointer no longer serves any useful purpose. This commit therefore removes it.
--- kernel/rcu/tree.c | 27 ++++++++++++--------------- kernel/rcu/tree_exp.h | 2 +- kernel/rcu/tree_plugin.h | 16 ++++++++-------- 3 files changed, 21 insertions(+), 24 deletions(-)
kernel/rcu/tree.c +12 -15
@@ -85,7 +85,6 @@ struct rcu_state rcu_state = { .ofl_lock = __SPIN_LOCK_UNLOCKED(rcu_state.ofl_lock), }; -static struct rcu_state *const rcu_state_p = &rcu_state;static struct rcu_data __percpu *const rcu_data_p = &rcu_data;LIST_HEAD (rcu_struct_flavors);
@@ -491,7 +490,7 @@ static int rcu_pending(void);*/ unsigned long rcu_get_gp_seq (void ) { -return READ_ONCE (rcu_state_p->gp_seq); +return READ_ONCE (rcu_state.gp_seq); }EXPORT_SYMBOL_GPL (rcu_get_gp_seq);
@@ -510,7 +509,7 @@ EXPORT_SYMBOL_GPL(rcu_sched_get_gp_seq);*/ unsigned long rcu_bh_get_gp_seq (void ) { -return READ_ONCE (rcu_state_p->gp_seq); +return READ_ONCE (rcu_state.gp_seq); }EXPORT_SYMBOL_GPL (rcu_bh_get_gp_seq);
@@ -522,7 +521,7 @@ EXPORT_SYMBOL_GPL(rcu_bh_get_gp_seq);*/ unsigned long rcu_exp_batches_completed (void ) { -return rcu_state_p->expedited_sequence; +return rcu_state.expedited_sequence; }EXPORT_SYMBOL_GPL (rcu_exp_batches_completed);
@@ -541,7 +540,7 @@ EXPORT_SYMBOL_GPL(rcu_exp_batches_completed_sched);*/ void rcu_force_quiescent_state (void ) { -force_quiescent_state (rcu_state_p); +force_quiescent_state (&rcu_state); }EXPORT_SYMBOL_GPL (rcu_force_quiescent_state);
@@ -550,7 +549,7 @@ EXPORT_SYMBOL_GPL(rcu_force_quiescent_state);*/ void rcu_bh_force_quiescent_state (void ) { -force_quiescent_state (rcu_state_p); +force_quiescent_state (&rcu_state); }EXPORT_SYMBOL_GPL (rcu_bh_force_quiescent_state);
@@ -611,7 +610,7 @@ void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,case RCU_FLAVOR :case RCU_BH_FLAVOR :case RCU_SCHED_FLAVOR : - rsp = rcu_state_p; + rsp = &rcu_state;break ;default :break ;
@@ -2291,7 +2290,6 @@ rcu_report_unblock_qs_rnp(struct rcu_state *rsp,raw_lockdep_assert_held_rcu_node (rnp);if (WARN_ON_ONCE (!IS_ENABLED (CONFIG_PREEMPT )) || -WARN_ON_ONCE (rsp != rcu_state_p) ||WARN_ON_ONCE (rcu_preempt_blocked_readers_cgp (rnp)) || rnp->qsmask !=0 ) {raw_spin_unlock_irqrestore_rcu_node (rnp, flags);
@@ -2603,7 +2601,6 @@ static void force_qs_rnp(struct rcu_state *rsp, int (*f)(struct rcu_data *rsp))raw_spin_lock_irqsave_rcu_node (rnp, flags);if (rnp->qsmask ==0 ) {if (!IS_ENABLED (CONFIG_PREEMPT ) || - rsp != rcu_state_p ||rcu_preempt_blocked_readers_cgp (rnp)) {/* * No point in scanning bits because they
@@ -2972,7 +2969,7 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func,*/ void call_rcu (struct rcu_head *head,rcu_callback_t func) { -__call_rcu (head, func, rcu_state_p,-1 ,0 ); +__call_rcu (head, func, &rcu_state,-1 ,0 ); }EXPORT_SYMBOL_GPL (call_rcu);
@@ -2999,7 +2996,7 @@ EXPORT_SYMBOL_GPL(call_rcu_sched);void kfree_call_rcu (struct rcu_head *head,rcu_callback_t func) { -__call_rcu (head, func, rcu_state_p,-1 ,1 ); +__call_rcu (head, func, &rcu_state,-1 ,1 ); }EXPORT_SYMBOL_GPL (kfree_call_rcu);
@@ -3028,7 +3025,7 @@ unsigned long get_state_synchronize_rcu(void)* before the load from ->gp_seq. */ smp_mb ();/* ^^^ */ -return rcu_seq_snap (&rcu_state_p->gp_seq); +return rcu_seq_snap (&rcu_state.gp_seq); }EXPORT_SYMBOL_GPL (get_state_synchronize_rcu);
@@ -3048,7 +3045,7 @@ EXPORT_SYMBOL_GPL(get_state_synchronize_rcu);*/ void cond_synchronize_rcu (unsigned long oldstate) { -if (!rcu_seq_done (&rcu_state_p->gp_seq, oldstate)) +if (!rcu_seq_done (&rcu_state.gp_seq, oldstate))synchronize_rcu ();else smp_mb ();/* Ensure GP ends before subsequent accesses. */
@@ -3307,7 +3304,7 @@ static void _rcu_barrier(struct rcu_state *rsp)*/ void rcu_barrier_bh (void ) { -_rcu_barrier (rcu_state_p); +_rcu_barrier (&rcu_state); }EXPORT_SYMBOL_GPL (rcu_barrier_bh);
@@ -3321,7 +3318,7 @@ EXPORT_SYMBOL_GPL(rcu_barrier_bh);*/ void rcu_barrier (void ) { -_rcu_barrier (rcu_state_p); +_rcu_barrier (&rcu_state); }EXPORT_SYMBOL_GPL (rcu_barrier);
kernel/rcu/tree_exp.h +1 -1
@@ -756,7 +756,7 @@ static void sync_sched_exp_online_cleanup(int cpu)*/ void synchronize_rcu_expedited (void ) { -struct rcu_state *rsp = rcu_state_p; +struct rcu_state *rsp = &rcu_state; RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map) || lock_is_held(&rcu_lock_map) ||
kernel/rcu/tree_plugin.h +8 -8
@@ -381,7 +381,7 @@ void rcu_note_context_switch(bool preempt)*/ rcu_qs ();if (rdp->deferred_qs) -rcu_report_exp_rdp (rcu_state_p, rdp); +rcu_report_exp_rdp (&rcu_state, rdp);trace_rcu_utilization (TPS ("End context switch" ));barrier ();/* Avoid RCU read-side critical sections leaking up. */ }
@@ -509,7 +509,7 @@ rcu_preempt_deferred_qs_irqrestore(struct task_struct *t, unsigned long flags)* blocked-tasks list below. */ if (rdp->deferred_qs) { -rcu_report_exp_rdp (rcu_state_p, rdp); +rcu_report_exp_rdp (&rcu_state, rdp);if (!t->rcu_read_unlock_special.s) {local_irq_restore (flags);return ;
@@ -566,7 +566,7 @@ rcu_preempt_deferred_qs_irqrestore(struct task_struct *t, unsigned long flags) rnp->grplo, rnp->grphi, !!rnp->gp_tasks); -rcu_report_unblock_qs_rnp (rcu_state_p, rnp, flags); +rcu_report_unblock_qs_rnp (&rcu_state, rnp, flags); }else {raw_spin_unlock_irqrestore_rcu_node (rnp, flags); }
@@ -580,7 +580,7 @@ rcu_preempt_deferred_qs_irqrestore(struct task_struct *t, unsigned long flags)* then we need to report up the rcu_node hierarchy. */ if (!empty_exp && empty_exp_now) -rcu_report_exp_rnp (rcu_state_p, rnp, true); +rcu_report_exp_rnp (&rcu_state, rnp, true); }else {local_irq_restore (flags); }
@@ -1300,7 +1300,7 @@ static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp,struct sched_param sp;struct task_struct *t; -if (rcu_state_p != rsp) +if (&rcu_state != rsp)return 0 ;if (!rcu_scheduler_fully_active ||rcu_rnp_online_cpus (rnp) ==0 )
@@ -1431,8 +1431,8 @@ static void __init rcu_spawn_boost_kthreads(void)for_each_possible_cpu (cpu)per_cpu (rcu_cpu_has_work, cpu) =0 ;BUG_ON (smpboot_register_percpu_thread (&rcu_cpu_thread_spec)); -rcu_for_each_leaf_node (rcu_state_p, rnp) - (void )rcu_spawn_one_boost_kthread (rcu_state_p, rnp); +rcu_for_each_leaf_node (&rcu_state, rnp) + (void )rcu_spawn_one_boost_kthread (&rcu_state, rnp); }static void rcu_prepare_kthreads (int cpu)
@@ -1442,7 +1442,7 @@ static void rcu_prepare_kthreads(int cpu)/* Fire up the incoming CPU's kthread and leaf rcu_node kthread. */ if (rcu_scheduler_fully_active) - (void)rcu_spawn_one_boost_kthread(rcu_state_p, rnp); + (void)rcu_spawn_one_boost_kthread(&rcu_state, rnp); }#else /* #ifdef CONFIG_RCU_BOOST */
Signature · 1 line
2.17.1
PATCH 19/19 rcu: Remove rcu_data_p pointer to default rcu_data structure
In reply to Paul E. McKenneyThe rcu_data_p pointer references the default set of per-CPU rcu_data structures, that is, those that call_rcu() uses, as opposed to call_rcu_bh() and sometimes call_rcu_sched(). But there is now only one set of per-CPU rcu_data structures, so that one set is by definition the default, which means that the rcu_data_p pointer no longer serves any useful purpose. This commit therefore removes it.
--- kernel/rcu/tree.c | 1 - kernel/rcu/tree_plugin.h | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-)
kernel/rcu/tree.c +0 -1
@@ -85,7 +85,6 @@ struct rcu_state rcu_state = { .ofl_lock = __SPIN_LOCK_UNLOCKED(rcu_state.ofl_lock), }; -static struct rcu_data __percpu *const rcu_data_p = &rcu_data;LIST_HEAD (rcu_struct_flavors);/* Dump rcu_node combining tree at boot to verify correct setup. */
kernel/rcu/tree_plugin.h +5 -5
@@ -302,11 +302,11 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp)static void rcu_qs (void ) {RCU_LOCKDEP_WARN (preemptible (),"rcu_qs() invoked with preemption enabled!!!\n" ); -if (__this_cpu_read (rcu_data_p->cpu_no_qs.s)) { +if (__this_cpu_read (rcu_data.cpu_no_qs.s)) {trace_rcu_grace_period (TPS ("rcu_preempt" ), -__this_cpu_read (rcu_data_p->gp_seq), +__this_cpu_read (rcu_data.gp_seq),TPS ("cpuqs" )); -__this_cpu_write (rcu_data_p->cpu_no_qs.b.norm, false); +__this_cpu_write (rcu_data.cpu_no_qs.b.norm, false);barrier ();/* Coordinate with rcu_flavor_check_callbacks(). */ current->rcu_read_unlock_special.b.need_qs = false; }
@@ -805,8 +805,8 @@ static void rcu_flavor_check_callbacks(int user)/* If GP is oldish, ask for help from rcu_read_unlock_special(). */ if (t->rcu_read_lock_nesting >0 && -__this_cpu_read (rcu_data_p->core_needs_qs) && -__this_cpu_read (rcu_data_p->cpu_no_qs.b.norm) && +__this_cpu_read (rcu_data.core_needs_qs) && +__this_cpu_read (rcu_data.cpu_no_qs.b.norm) && !t->rcu_read_unlock_special.b.need_qs &&time_after (jiffies, rsp->gp_start +HZ )) t->rcu_read_unlock_special.b.need_qs = true;
Signature · 1 line
2.17.1