tracing: Add usecase of synchronize_rcu_tasks() and stack_tracer_disable()
Steven Rostedt
maintainerv2 cover tracing: Add usecase of synchronize_rcu_tasks() and stack_tracer_disable()
Paul,
Here's my latest. You OK with it?
-- Steve
Paul E. McKenney (1):
rcu: Fix dyntick-idle tracing
Steven Rostedt (VMware) (4):
ftrace: Add use of synchronize_rcu_tasks() with dynamic trampolines
tracing: Replace the per_cpu() with this_cpu() in trace_stack.c
tracing: Add stack_tracer_disable/enable() functions
tracing: Rename trace_active to disable_stack_tracer and inline its modification
----
Changes since v1:
. Make stack_tracer_{dis,en}able() into inlines
. Rename trace_active to disable_stack_tracer and make global
. Do not call preempt_disable() from stack_tracer_disable()
and likewise from stack_tracer_enable()
. Add check for preemption disabled when CONFIG_PREEMPT_DEBUG
include/linux/ftrace.h | 38 ++++++++++++++++++++++++++++++++++++ kernel/rcu/tree.c | 48 ++++++++++++++++++++++------------------------ kernel/trace/Kconfig | 3 ++- kernel/trace/ftrace.c | 42 +++++++++++++++++----------------------- kernel/trace/trace_stack.c | 27 +++++++++----------------- 5 files changed, 90 insertions(+), 68 deletions(-)
Diff from v1:
include/linux/ftrace.h +34 -2
@@ -287,8 +287,40 @@ stack_trace_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); -void stack_tracer_disable (void ); -void stack_tracer_enable (void ); +/* DO NOT MODIFY THIS VARIABLE DIRECTLY! */ +DECLARE_PER_CPU (int, disable_stack_tracer); + +/** +* stack_tracer_disable - temporarily disable the stack tracer +* +* There's a few locations (namely in RCU) where stack tracing +* cannot be executed. This function is used to disable stack +* tracing during those critical sections. +* +* This function must be called with preemption or interrupts +* disabled and stack_tracer_enable() must be called shortly after +* while preemption or interrupts are still disabled. +*/ +static inline void stack_tracer_disable (void ) +{ +/* Preemption or interupts must be disabled */ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_inc (disable_stack_tracer); +} + +/** +* stack_tracer_enable - re-enable the stack tracer +* +* After stack_tracer_disable() is called, stack_tracer_enable() +* must be called shortly afterward. +*/ +static inline void stack_tracer_enable (void ) +{ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_dec (disable_stack_tracer); +}#else static inline void stack_tracer_disable (void ) { }static inline void stack_tracer_enabe (void ) { }
kernel/trace/trace_stack.c +9 -37
93 lines not on this page · raw message
Steven Rostedt
maintainerPATCH v2 1/5 ftrace: Add use of synchronize_rcu_tasks() with dynamic trampolines
From: "Steven Rostedt (VMware)" <[email protected]>
The function tracer needs to be more careful than other subsystems when it comes to freeing data. Especially if that data is actually executable code. When a single function is traced, a trampoline can be dynamically allocated which is called to jump to the function trace callback. When the callback is no longer needed, the dynamic allocated trampoline needs to be freed. This is where the issues arise. The dynamically allocated trampoline must not be used again. As function tracing can trace all subsystems, including subsystems that are used to serialize aspects of freeing (namely RCU), it must take extra care when doing the freeing.
Before synchronize_rcu_tasks() was around, there was no way for the function tracer to know that nothing was using the dynamically allocated trampoline when CONFIG_PREEMPT was enabled. That's because a task could be indefinitely preempted while sitting on the trampoline. Now with synchronize_rcu_tasks(), it will wait till all tasks have either voluntarily scheduled (not on the trampoline) or goes into userspace (not on the trampoline). Then it is safe to free the trampoline even with CONFIG_PREEMPT set.
--- kernel/trace/Kconfig | 3 ++- kernel/trace/ftrace.c | 42 ++++++++++++++++++------------------------ 2 files changed, 20 insertions(+), 25 deletions(-)
kernel/trace/Kconfig +2 -1
@@ -134,7 +134,8 @@ config FUNCTION_TRACER select KALLSYMS select GENERIC_TRACER select CONTEXT_SWITCH_TRACER - select GLOB + select GLOB + select TASKS_RCU if PREEMPT help Enable the kernel to trace every kernel function. This is done by using a compiler feature to insert a small, 5-byte No-Operation
kernel/trace/ftrace.c +18 -24
58 lines not on this page · raw message
Signature · 1 line
2.10.2
Steven Rostedt
maintainerPATCH v2 2/5 tracing: Replace the per_cpu() with this_cpu() in trace_stack.c
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
The updates to the trace_active per cpu variable can be updated with the this_cpu_*() functions as it only gets updated on the CPU that the variable is on.
--- kernel/trace/trace_stack.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)
kernel/trace/trace_stack.c +7 -16
@@ -207,13 +207,12 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *pt_regs) { unsigned long stack; -int cpu;preempt_disable_notrace (); - cpu =raw_smp_processor_id ();/* no atomic needed, we only modify this variable by this cpu */ -if (per_cpu (trace_active, cpu)++ !=0 ) +this_cpu_inc (trace_active); +if (this_cpu_read (trace_active) !=1 ) goto out; ip +=MCOUNT_INSN_SIZE ;
@@ -221,7 +220,7 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip,check_stack (ip, &stack); out: -per_cpu (trace_active, cpu)--; +this_cpu_dec (trace_active);/* prevent recursion in schedule */ preempt_enable_notrace (); }
@@ -253,7 +252,6 @@ stack_max_size_write(struct file *filp, const char __user *ubuf,long *ptr = filp->private_data;unsigned long val, flags;int ret; -int cpu; ret =kstrtoul_from_user (ubuf, count,10 , &val);if (ret)
@@ -266,14 +264,13 @@ stack_max_size_write(struct file *filp, const char __user *ubuf,* we will cause circular lock, so we also need to increase * the percpu trace_active here. */ - cpu =smp_processor_id (); -per_cpu (trace_active, cpu)++; +this_cpu_inc (trace_active);arch_spin_lock (&stack_trace_max_lock); *ptr = val;arch_spin_unlock (&stack_trace_max_lock); -per_cpu (trace_active, cpu)--; +this_cpu_dec (trace_active);local_irq_restore (flags);return count;
@@ -307,12 +304,9 @@ t_next(struct seq_file *m, void *v, loff_t *pos)static void *t_start (struct seq_file *m,loff_t *pos) { -int cpu; -local_irq_disable (); - cpu =smp_processor_id (); -per_cpu (trace_active, cpu)++; +this_cpu_inc (trace_active);arch_spin_lock (&stack_trace_max_lock);
@@ -324,12 +318,9 @@ static void *t_start(struct seq_file *m, loff_t *pos)static void t_stop (struct seq_file *m,void *p) { -int cpu; -arch_spin_unlock (&stack_trace_max_lock); - cpu =smp_processor_id (); -per_cpu (trace_active, cpu)--; +this_cpu_dec (trace_active);local_irq_enable (); }
Signature · 1 line
2.10.2
Re: [PATCH 2/5 v2] tracing: Replace the per_cpu() with this_cpu() in trace_stack.c
Quoted 17 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
The updates to the
trace_activeper cpu variable can be updated with the this_cpu_*() functions as it only gets updated on the CPU that the variable is on.Signed-off-by Steven Rostedt (VMware) <[email protected]>--- kernel/trace/trace_stack.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)kernel/trace/trace_stack.c +0
-0@@ -207,13 +207,12 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *pt_regs)
kernel/trace/trace_stack.c stack_trace_call(unsigned long ip, unsigned long parent_ip, · line 214
Quoted 10 lines
kernel/trace/trace_stack.c +1
-3{unsigned long stack; -int cpu;preempt_disable_notrace (); - cpu =raw_smp_processor_id ();/* no atomic needed, we only modify this variable by this cpu */ -if (per_cpu (trace_active, cpu)++ !=0 ) +this_cpu_inc (trace_active);
For whatever it is worth...
I was about to complain that this_cpu_inc() only disables preemption, not interrupts, but then I realized that any correct interrupt handler would have to restore the per-CPU variable to its original value.
Presumably you have to sum up all the per-CPU trace_active counts, given that there is no guarantee that a process-level dec will happen on the same CPU that did the inc.
Thanx, Paul
Quoted 70 lines
kernel/trace/trace_stack.c +6
-13+if (this_cpu_read (trace_active) !=1 ) goto out; ip +=MCOUNT_INSN_SIZE ;@@ -221,7 +220,7 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip,check_stack (ip, &stack); out: -per_cpu (trace_active, cpu)--; +this_cpu_dec (trace_active);/* prevent recursion in schedule */ preempt_enable_notrace (); }@@ -253,7 +252,6 @@ stack_max_size_write(struct file *filp, const char __user *ubuf,long *ptr = filp->private_data;unsigned long val, flags;int ret; -int cpu; ret =kstrtoul_from_user (ubuf, count,10 , &val);if (ret)@@ -266,14 +264,13 @@ stack_max_size_write(struct file *filp, const char __user *ubuf,* we will cause circular lock, so we also need to increase * the percpu trace_active here. */ - cpu =smp_processor_id (); -per_cpu (trace_active, cpu)++; +this_cpu_inc (trace_active);arch_spin_lock (&stack_trace_max_lock); *ptr = val;arch_spin_unlock (&stack_trace_max_lock); -per_cpu (trace_active, cpu)--; +this_cpu_dec (trace_active);local_irq_restore (flags);return count;@@ -307,12 +304,9 @@ t_next(struct seq_file *m, void *v, loff_t *pos)static void *t_start (struct seq_file *m,loff_t *pos) { -int cpu; -local_irq_disable (); - cpu =smp_processor_id (); -per_cpu (trace_active, cpu)++; +this_cpu_inc (trace_active);arch_spin_lock (&stack_trace_max_lock);@@ -324,12 +318,9 @@ static void *t_start(struct seq_file *m, loff_t *pos)static void t_stop (struct seq_file *m,void *p) { -int cpu; -arch_spin_unlock (&stack_trace_max_lock); - cpu =smp_processor_id (); -per_cpu (trace_active, cpu)--; +this_cpu_dec (trace_active);local_irq_enable (); }Signature · 1 line
2.10.2
Steven Rostedt
maintainerRe: [PATCH 2/5 v2] tracing: Replace the per_cpu() with this_cpu() in trace_stack.c
Quoted 31 lines · On Fri, 7 Apr 2017 07:36:19 -0700 "Paul E. McKenney"…
Quoted 17 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
The updates to the
trace_activeper cpu variable can be updated with the this_cpu_*() functions as it only gets updated on the CPU that the variable is on.Signed-off-by Steven Rostedt (VMware) <[email protected]>--- kernel/trace/trace_stack.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)kernel/trace/trace_stack.c +0
-0@@ -207,13 +207,12 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *pt_regs)kernel/trace/trace_stack.c stack_trace_call(unsigned long ip, unsigned long parent_ip, · line 214
Quoted 10 lines
kernel/trace/trace_stack.c +1
-3{unsigned long stack; -int cpu;preempt_disable_notrace (); - cpu =raw_smp_processor_id ();/* no atomic needed, we only modify this variable by this cpu */ -if (per_cpu (trace_active, cpu)++ !=0 ) +this_cpu_inc (trace_active);For whatever it is worth...
Quoted 3 lines
I was about to complain that
this_cpu_inc()only disables preemption, not interrupts, but then I realized that any correct interrupt handler would have to restore the per-CPU variable to its original value.
Yep, that's the reason for the comment about "no atomic needed". This is a "stack modification". Any interruption in the flow will reset the changes back to the way it was before going back to what it interrupted.
Quoted 4 lines
Presumably you have to sum up all the per-CPU
trace_activecounts, given that there is no guarantee that a process-level dec will happen on the same CPU that did the inc.
That's why we disable preemption. We guarantee that a process-level dec *will* happen on the same CPU that did the inc.
It's also the reason for the preemption disabled check in the stack_tracer_disable() code.
-- Steve
Re: [PATCH 2/5 v2] tracing: Replace the per_cpu() with this_cpu() in trace_stack.c
Quoted 43 lines · Steven Rostedt
Quoted 31 lines · On Fri, 7 Apr 2017 07:36:19 -0700 "Paul E. McKenney"…
Quoted 17 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
The updates to the
trace_activeper cpu variable can be updated with the this_cpu_*() functions as it only gets updated on the CPU that the variable is on.Signed-off-by Steven Rostedt (VMware) <[email protected]>--- kernel/trace/trace_stack.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)kernel/trace/trace_stack.c +0
-0@@ -207,13 +207,12 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *pt_regs)kernel/trace/trace_stack.c stack_trace_call(unsigned long ip, unsigned long parent_ip, · line 214
Quoted 10 lines
kernel/trace/trace_stack.c +1
-3{unsigned long stack; -int cpu;preempt_disable_notrace (); - cpu =raw_smp_processor_id ();/* no atomic needed, we only modify this variable by this cpu */ -if (per_cpu (trace_active, cpu)++ !=0 ) +this_cpu_inc (trace_active);For whatever it is worth...
Quoted 3 lines
I was about to complain that
this_cpu_inc()only disables preemption, not interrupts, but then I realized that any correct interrupt handler would have to restore the per-CPU variable to its original value.Yep, that's the reason for the comment about "no atomic needed". This is a "stack modification". Any interruption in the flow will reset the changes back to the way it was before going back to what it interrupted.
Quoted 1 line
Quoted 6 lines
Quoted 3 lines
Presumably you have to sum up all the per-CPU
trace_activecounts, given that there is no guarantee that a process-level dec will happen on the same CPU that did the inc.That's why we disable preemption. We guarantee that a process-level dec *will* happen on the same CPU that did the inc.
But in that case, can't you use __this_cpu_inc()? Save a few cycles on RISC systems.
Thanx, Paul
Quoted 6 lines
It's also the reason for the preemption disabled check in the
stack_tracer_disable()code.-- Steve
Steven Rostedt
maintainerPATCH v2 3/5 tracing: Add stack_tracer_disable/enable() functions
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
There are certain parts of the kernel that cannot let stack tracing proceed (namely in RCU), because the stack tracer uses RCU, and parts of RCU internals cannot handle having RCU read side locks taken.
Add stack_tracer_disable() and stack_tracer_enable() functions to let RCU stop stack tracing on the current CPU when it is in those critical sections.
--- include/linux/ftrace.h | 6 ++++++ kernel/trace/trace_stack.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+)
include/linux/ftrace.h +6 -0
@@ -286,6 +286,12 @@ int stack_trace_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); + +void stack_tracer_disable (void ); +void stack_tracer_enable (void ); +#else +static inline void stack_tracer_disable (void ) { } +static inline void stack_tracer_enabe (void ) { }#endif struct ftrace_func_command {
kernel/trace/trace_stack.c +32 -0
@@ -41,6 +41,38 @@ static DEFINE_MUTEX(stack_sysctl_mutex);int stack_tracer_enabled;static int last_stack_tracer_enabled; +/** +* stack_tracer_disable - temporarily disable the stack tracer +* +* There's a few locations (namely in RCU) where stack tracing +* cannot be executed. This function is used to disable stack +* tracing during those critical sections. +* +* This function must be called with preemption or interrupts +* disabled and stack_tracer_enable() must be called shortly after +* while preemption or interrupts are still disabled. +*/ +void stack_tracer_disable (void ) +{ +/* Preemption or interupts must be disabled */ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_inc (trace_active); +} + +/** +* stack_tracer_enable - re-enable the stack tracer +* +* After stack_tracer_disable() is called, stack_tracer_enable() +* must be called shortly afterward. +*/ +void stack_tracer_enable (void ) +{ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_dec (trace_active); +} +void stack_trace_print (void ) {long i;
Signature · 1 line
2.10.2
Steven Rostedt
maintainerRe: [PATCH 3/5 v2] tracing: Add stack_tracer_disable/enable() functions
Quoted 5 lines · On Fri, 07 Apr 2017 10:01:09 -0400 Steven Rostedt <[email protected]> wrote:
include/linux/ftrace.h +0
-0
include/linux/ftrace.h int · line 294
Quoted 9 lines
include/linux/ftrace.h +6
-0@@ -286,6 +286,12 @@ int stack_trace_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); + +void stack_tracer_disable (void ); +void stack_tracer_enable (void ); +#else +static inline void stack_tracer_disable (void ) { } +static inline void stack_tracer_enabe (void ) { }
Grumble. kbot caught that this is enabe not enable.
I cut and pasted the bad name to all places, and when I compiled, it complained. I fixed the ones to make my system build, but forgot to fix the #ifndef version :-(
I'll fix this before pushing.
-- Steve
Steven Rostedt
maintainerPATCH v2 3/5 tracing: Add stack_tracer_disable/enable() functions
In reply to Steven RostedtThere are certain parts of the kernel that cannot let stack tracing proceed (namely in RCU), because the stack tracer uses RCU, and parts of RCU internals cannot handle having RCU read side locks taken.
Add stack_tracer_disable() and stack_tracer_enable() functions to let RCU stop stack tracing on the current CPU when it is in those critical sections.
--- include/linux/ftrace.h | 6 ++++++ kernel/trace/trace_stack.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+)
v2.1 change: s/stack_tracer_enabe/stack_tracer_enable/
(kbuild test robot)
include/linux/ftrace.h +6 -0
@@ -286,6 +286,12 @@ int stack_trace_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); + +void stack_tracer_disable (void ); +void stack_tracer_enable (void ); +#else +static inline void stack_tracer_disable (void ) { } +static inline void stack_tracer_enable (void ) { }#endif struct ftrace_func_command {
kernel/trace/trace_stack.c +32 -0
@@ -41,6 +41,38 @@ static DEFINE_MUTEX(stack_sysctl_mutex);int stack_tracer_enabled;static int last_stack_tracer_enabled; +/** +* stack_tracer_disable - temporarily disable the stack tracer +* +* There's a few locations (namely in RCU) where stack tracing +* cannot be executed. This function is used to disable stack +* tracing during those critical sections. +* +* This function must be called with preemption or interrupts +* disabled and stack_tracer_enable() must be called shortly after +* while preemption or interrupts are still disabled. +*/ +void stack_tracer_disable (void ) +{ +/* Preemption or interupts must be disabled */ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_inc (trace_active); +} + +/** +* stack_tracer_enable - re-enable the stack tracer +* +* After stack_tracer_disable() is called, stack_tracer_enable() +* must be called shortly afterward. +*/ +void stack_tracer_enable (void ) +{ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_dec (trace_active); +} +void stack_trace_print (void ) {long i;
Signature · 1 line
2.9.3
Steven Rostedt
maintainerPATCH v2 4/5 tracing: Rename trace_active to disable_stack_tracer and inline its modification
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
In order to eliminate a function call, make "trace_active" into "disable_stack_tracer" and convert stack_tracer_disable() and friends into static inline functions.
--- include/linux/ftrace.h | 36 +++++++++++++++++++++++++++++++-- kernel/trace/trace_stack.c | 50 +++++++++------------------------------------- 2 files changed, 43 insertions(+), 43 deletions(-)
include/linux/ftrace.h +34 -2
@@ -287,8 +287,40 @@ stack_trace_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); -void stack_tracer_disable (void ); -void stack_tracer_enable (void ); +/* DO NOT MODIFY THIS VARIABLE DIRECTLY! */ +DECLARE_PER_CPU (int, disable_stack_tracer); + +/** +* stack_tracer_disable - temporarily disable the stack tracer +* +* There's a few locations (namely in RCU) where stack tracing +* cannot be executed. This function is used to disable stack +* tracing during those critical sections. +* +* This function must be called with preemption or interrupts +* disabled and stack_tracer_enable() must be called shortly after +* while preemption or interrupts are still disabled. +*/ +static inline void stack_tracer_disable (void ) +{ +/* Preemption or interupts must be disabled */ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_inc (disable_stack_tracer); +} + +/** +* stack_tracer_enable - re-enable the stack tracer +* +* After stack_tracer_disable() is called, stack_tracer_enable() +* must be called shortly afterward. +*/ +static inline void stack_tracer_enable (void ) +{ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_dec (disable_stack_tracer); +}#else static inline void stack_tracer_disable (void ) { }static inline void stack_tracer_enabe (void ) { }
kernel/trace/trace_stack.c +9 -41
97 lines not on this page · raw message
Signature · 1 line
2.10.2
Steven Rostedt
maintainerPATCH v2 5/5 rcu: Fix dyntick-idle tracing
In reply to Steven RostedtFrom: "Paul E. McKenney" <[email protected]>
The tracing subsystem started using rcu_irq_entry() and rcu_irq_exit() (with my blessing) to allow the current _rcuidle alternative tracepoint name to be dispensed with while still maintaining good performance. Unfortunately, this causes RCU's dyntick-idle entry code's tracing to appear to RCU like an interrupt that occurs where RCU is not designed to handle interrupts.
This commit fixes this problem by moving the zeroing of ->dynticks_nesting after the offending trace_rcu_dyntick() statement, which narrows the window of vulnerability to a pair of adjacent statements that are now marked with comments to that effect.
--- kernel/rcu/tree.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-)
kernel/rcu/tree.c +23 -25
@@ -57,6 +57,7 @@#include <linux/random.h> #include <linux/trace_events.h> #include <linux/suspend.h> +#include <linux/ftrace.h> #include "tree.h" #include "rcu.h"
@@ -771,25 +772,24 @@ cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp) }/* -* rcu_eqs_enter_common - current CPU is moving towards extended quiescent state +* rcu_eqs_enter_common - current CPU is entering an extended quiescent state * -* If the new value of the ->dynticks_nesting counter now is zero, -* we really have entered idle, and must do the appropriate accounting. -* The caller must have disabled interrupts. +* Enter idle, doing appropriate accounting. The caller must have +* disabled interrupts. */ -static void rcu_eqs_enter_common (long long oldval,bool user) +static void rcu_eqs_enter_common (bool user) {struct rcu_state *rsp;struct rcu_data *rdp; - RCU_TRACE(struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);) +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -trace_rcu_dyntick (TPS ("Start" ), oldval, rdtp->dynticks_nesting); +trace_rcu_dyntick (TPS ("Start" ), rdtp->dynticks_nesting,0 );if (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current)) {struct task_struct *idle__maybe_unused =idle_task (smp_processor_id ()); -trace_rcu_dyntick (TPS ("Error on entry: not idle task" ), oldval,0 ); +trace_rcu_dyntick (TPS ("Error on entry: not idle task" ), rdtp->dynticks_nesting,0 );rcu_ftrace_dump (DUMP_ORIG ); WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", current->pid, current->comm,
@@ -800,7 +800,10 @@ static void rcu_eqs_enter_common(long long oldval, bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -rcu_dynticks_eqs_enter (); +stack_tracer_disable (); + rdtp->dynticks_nesting =0 ;/* Breaks tracing momentarily. */ +rcu_dynticks_eqs_enter ();/* After this, tracing works again. */ +stack_tracer_enable ();rcu_dynticks_task_enter ();/*
@@ -821,19 +824,15 @@ static void rcu_eqs_enter_common(long long oldval, bool user)*/ static void rcu_eqs_enter (bool user) { -long long oldval;struct rcu_dynticks *rdtp; rdtp =this_cpu_ptr (&rcu_dynticks); - oldval = rdtp->dynticks_nesting;WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && - (oldval &DYNTICK_TASK_NEST_MASK ) ==0 ); -if ((oldval &DYNTICK_TASK_NEST_MASK ) ==DYNTICK_TASK_NEST_VALUE ) { - rdtp->dynticks_nesting =0 ; -rcu_eqs_enter_common (oldval, user); - }else { + (rdtp->dynticks_nesting &DYNTICK_TASK_NEST_MASK ) ==0 ); +if ((rdtp->dynticks_nesting &DYNTICK_TASK_NEST_MASK ) ==DYNTICK_TASK_NEST_VALUE ) +rcu_eqs_enter_common (user); +else rdtp->dynticks_nesting -=DYNTICK_TASK_NEST_VALUE ; - } }/**
@@ -892,19 +891,18 @@ void rcu_user_enter(void)*/ void rcu_irq_exit (void ) { -long long oldval;struct rcu_dynticks *rdtp;RCU_LOCKDEP_WARN (!irqs_disabled (),"rcu_irq_exit() invoked with irqs enabled!!!" ); rdtp =this_cpu_ptr (&rcu_dynticks); - oldval = rdtp->dynticks_nesting; - rdtp->dynticks_nesting--;WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && - rdtp->dynticks_nesting <0 ); -if (rdtp->dynticks_nesting) -trace_rcu_dyntick (TPS ("--=" ), oldval, rdtp->dynticks_nesting); -else -rcu_eqs_enter_common (oldval, true); + rdtp->dynticks_nesting <1 ); +if (rdtp->dynticks_nesting <=1 ) { +rcu_eqs_enter_common (true); + }else { +trace_rcu_dyntick (TPS ("--=" ), rdtp->dynticks_nesting, rdtp->dynticks_nesting -1 ); + rdtp->dynticks_nesting--; + }rcu_sysidle_enter (1 ); }
Signature · 1 line
2.10.2
Re: [PATCH 5/5 v2] rcu: Fix dyntick-idle tracing
Quoted 69 lines · Steven Rostedt
From: "Paul E. McKenney" <[email protected]>
The tracing subsystem started using
rcu_irq_entry()andrcu_irq_exit()(with my blessing) to allow the current_rcuidlealternative tracepoint name to be dispensed with while still maintaining good performance. Unfortunately, this causes RCU's dyntick-idle entry code's tracing to appear to RCU like an interrupt that occurs where RCU is not designed to handle interrupts.This commit fixes this problem by moving the zeroing of ->dynticks_nesting after the offending
trace_rcu_dyntick()statement, which narrows the window of vulnerability to a pair of adjacent statements that are now marked with comments to that effect.Reported-by Steven Rostedt <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>--- kernel/rcu/tree.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-)kernel/rcu/tree.c +8
-8@@ -57,6 +57,7 @@#include <linux/random.h> #include <linux/trace_events.h> #include <linux/suspend.h> +#include <linux/ftrace.h> #include "tree.h" #include "rcu.h" @@ -771,25 +772,24 @@ cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp) }/* -* rcu_eqs_enter_common - current CPU is moving towards extended quiescent state +* rcu_eqs_enter_common - current CPU is entering an extended quiescent state * -* If the new value of the ->dynticks_nesting counter now is zero, -* we really have entered idle, and must do the appropriate accounting. -* The caller must have disabled interrupts. +* Enter idle, doing appropriate accounting. The caller must have +* disabled interrupts. */ -static void rcu_eqs_enter_common (long long oldval,bool user) +static void rcu_eqs_enter_common (bool user) {struct rcu_state *rsp;struct rcu_data *rdp; - RCU_TRACE(struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);) +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -trace_rcu_dyntick (TPS ("Start" ), oldval, rdtp->dynticks_nesting); +trace_rcu_dyntick (TPS ("Start" ), rdtp->dynticks_nesting,0 );if (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current)) {struct task_struct *idle__maybe_unused =idle_task (smp_processor_id ()); -trace_rcu_dyntick (TPS ("Error on entry: not idle task" ), oldval,0 ); +trace_rcu_dyntick (TPS ("Error on entry: not idle task" ), rdtp->dynticks_nesting,0 );rcu_ftrace_dump (DUMP_ORIG ); WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", current->pid, current->comm,
kernel/rcu/tree.c static void rcu_eqs_enter_common(long long oldval, bool user) · line 806
Quoted 8 lines
kernel/rcu/tree.c +4
-1@@ -800,7 +800,10 @@ static void rcu_eqs_enter_common(long long oldval, bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -rcu_dynticks_eqs_enter (); +stack_tracer_disable (); + rdtp->dynticks_nesting =0 ;/* Breaks tracing momentarily. */ +rcu_dynticks_eqs_enter ();/* After this, tracing works again. */ +stack_tracer_enable ();
Hmmm... There is not supposed to be any tracing in this interval, and interrupts are disabled. Wouldn't it be better to have something that made tracing illegal during this interval?
Yeah, I am a bit concerned about idle-entry latency...
Thanx, Paul
Quoted 58 lines
kernel/rcu/tree.c +11
-16rcu_dynticks_task_enter ();/* @@ -821,19 +824,15 @@ static void rcu_eqs_enter_common(long long oldval, bool user)*/ static void rcu_eqs_enter (bool user) { -long long oldval;struct rcu_dynticks *rdtp; rdtp =this_cpu_ptr (&rcu_dynticks); - oldval = rdtp->dynticks_nesting;WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && - (oldval &DYNTICK_TASK_NEST_MASK ) ==0 ); -if ((oldval &DYNTICK_TASK_NEST_MASK ) ==DYNTICK_TASK_NEST_VALUE ) { - rdtp->dynticks_nesting =0 ; -rcu_eqs_enter_common (oldval, user); - }else { + (rdtp->dynticks_nesting &DYNTICK_TASK_NEST_MASK ) ==0 ); +if ((rdtp->dynticks_nesting &DYNTICK_TASK_NEST_MASK ) ==DYNTICK_TASK_NEST_VALUE ) +rcu_eqs_enter_common (user); +else rdtp->dynticks_nesting -=DYNTICK_TASK_NEST_VALUE ; - } }/** @@ -892,19 +891,18 @@ void rcu_user_enter(void)*/ void rcu_irq_exit (void ) { -long long oldval;struct rcu_dynticks *rdtp;RCU_LOCKDEP_WARN (!irqs_disabled (),"rcu_irq_exit() invoked with irqs enabled!!!" ); rdtp =this_cpu_ptr (&rcu_dynticks); - oldval = rdtp->dynticks_nesting; - rdtp->dynticks_nesting--;WARN_ON_ONCE (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && - rdtp->dynticks_nesting <0 ); -if (rdtp->dynticks_nesting) -trace_rcu_dyntick (TPS ("--=" ), oldval, rdtp->dynticks_nesting); -else -rcu_eqs_enter_common (oldval, true); + rdtp->dynticks_nesting <1 ); +if (rdtp->dynticks_nesting <=1 ) { +rcu_eqs_enter_common (true); + }else { +trace_rcu_dyntick (TPS ("--=" ), rdtp->dynticks_nesting, rdtp->dynticks_nesting -1 ); + rdtp->dynticks_nesting--; + }rcu_sysidle_enter (1 ); }Signature · 1 line
2.10.2
Steven Rostedt
maintainerRe: [PATCH 5/5 v2] rcu: Fix dyntick-idle tracing
Quoted 64 lines · On Fri, 7 Apr 2017 07:40:11 -0700 "Paul E. McKenney"…
Quoted 63 lines · Steven Rostedt
From: "Paul E. McKenney" <[email protected]>
The tracing subsystem started using
rcu_irq_entry()andrcu_irq_exit()(with my blessing) to allow the current_rcuidlealternative tracepoint name to be dispensed with while still maintaining good performance. Unfortunately, this causes RCU's dyntick-idle entry code's tracing to appear to RCU like an interrupt that occurs where RCU is not designed to handle interrupts.This commit fixes this problem by moving the zeroing of ->dynticks_nesting after the offending
trace_rcu_dyntick()statement, which narrows the window of vulnerability to a pair of adjacent statements that are now marked with comments to that effect.Reported-by Steven Rostedt <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>--- kernel/rcu/tree.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-)kernel/rcu/tree.c +7
-7@@ -57,6 +57,7 @@#include <linux/random.h> #include <linux/trace_events.h> #include <linux/suspend.h> +#include <linux/ftrace.h> #include "tree.h" #include "rcu.h" @@ -771,25 +772,24 @@ cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp) }/* -* rcu_eqs_enter_common - current CPU is moving towards extended quiescent state +* rcu_eqs_enter_common - current CPU is entering an extended quiescent state * -* If the new value of the ->dynticks_nesting counter now is zero, -* we really have entered idle, and must do the appropriate accounting. -* The caller must have disabled interrupts. +* Enter idle, doing appropriate accounting. The caller must have +* disabled interrupts. */ -static void rcu_eqs_enter_common (long long oldval,bool user) +static void rcu_eqs_enter_common (bool user) {struct rcu_state *rsp;struct rcu_data *rdp; - RCU_TRACE(struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);) +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -trace_rcu_dyntick (TPS ("Start" ), oldval, rdtp->dynticks_nesting); +trace_rcu_dyntick (TPS ("Start" ), rdtp->dynticks_nesting,0 );if (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current)) {struct task_struct *idle__maybe_unused =idle_task (smp_processor_id ());
Quoted 16 lines
Quoted 6 lines
kernel/rcu/tree.c +1
-1-trace_rcu_dyntick (TPS ("Error on entry: not idle task" ), oldval,0 ); +trace_rcu_dyntick (TPS ("Error on entry: not idle task" ), rdtp->dynticks_nesting,0 );rcu_ftrace_dump (DUMP_ORIG ); WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", current->pid, current->comm,kernel/rcu/tree.c static void rcu_eqs_enter_common(long long oldval, bool user) · line 806
Quoted 8 lines
kernel/rcu/tree.c +4
-1@@ -800,7 +800,10 @@ static void rcu_eqs_enter_common(long long oldval, bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -rcu_dynticks_eqs_enter (); +stack_tracer_disable (); + rdtp->dynticks_nesting =0 ;/* Breaks tracing momentarily. */ +rcu_dynticks_eqs_enter ();/* After this, tracing works again. */ +stack_tracer_enable ();Hmmm... There is not supposed to be any tracing in this interval,
Why not? function tracing happens without an issue. But then again, function tracing doesn't depend on RCU.
Quoted 2 lines
and interrupts are disabled. Wouldn't it be better to have something that made tracing illegal during this interval?
I don't see an issue here. Function tracing is fine. There should be no trace_events() as those are static events and shouldn't dynamically appear in this interval.
The problem I hit is that stack tracing uses function tracing to check the stack of all functions. It doesn't need RCU either, unless it hits a new "max stack", which it then calls save_stack_trace(), which does a lot, and it does perform an rcu_read_lock(), which is what broke.
I'm fine with tracing, as that works. What doesn't work is tracing a new max stack.
Quoted 3 lines
Yeah, I am a bit concerned about idle-entry latency...
Which should now be fine because of the inlined this_cpu_inc/dec() which is very efficient and made for fast paths like this.
-- Steve
Re: [PATCH 5/5 v2] rcu: Fix dyntick-idle tracing
Quoted 103 lines · Steven Rostedt
Quoted 64 lines · On Fri, 7 Apr 2017 07:40:11 -0700 "Paul E. McKenney"…
Quoted 63 lines · Steven Rostedt
From: "Paul E. McKenney" <[email protected]>
The tracing subsystem started using
rcu_irq_entry()andrcu_irq_exit()(with my blessing) to allow the current_rcuidlealternative tracepoint name to be dispensed with while still maintaining good performance. Unfortunately, this causes RCU's dyntick-idle entry code's tracing to appear to RCU like an interrupt that occurs where RCU is not designed to handle interrupts.This commit fixes this problem by moving the zeroing of ->dynticks_nesting after the offending
trace_rcu_dyntick()statement, which narrows the window of vulnerability to a pair of adjacent statements that are now marked with comments to that effect.Reported-by Steven Rostedt <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>--- kernel/rcu/tree.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-)kernel/rcu/tree.c +7
-7@@ -57,6 +57,7 @@#include <linux/random.h> #include <linux/trace_events.h> #include <linux/suspend.h> +#include <linux/ftrace.h> #include "tree.h" #include "rcu.h" @@ -771,25 +772,24 @@ cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp) }/* -* rcu_eqs_enter_common - current CPU is moving towards extended quiescent state +* rcu_eqs_enter_common - current CPU is entering an extended quiescent state * -* If the new value of the ->dynticks_nesting counter now is zero, -* we really have entered idle, and must do the appropriate accounting. -* The caller must have disabled interrupts. +* Enter idle, doing appropriate accounting. The caller must have +* disabled interrupts. */ -static void rcu_eqs_enter_common (long long oldval,bool user) +static void rcu_eqs_enter_common (bool user) {struct rcu_state *rsp;struct rcu_data *rdp; - RCU_TRACE(struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);) +struct rcu_dynticks *rdtp =this_cpu_ptr (&rcu_dynticks); -trace_rcu_dyntick (TPS ("Start" ), oldval, rdtp->dynticks_nesting); +trace_rcu_dyntick (TPS ("Start" ), rdtp->dynticks_nesting,0 );if (IS_ENABLED (CONFIG_RCU_EQS_DEBUG ) && !user && !is_idle_task (current)) {struct task_struct *idle__maybe_unused =idle_task (smp_processor_id ());Quoted 16 lines
Quoted 6 lines
kernel/rcu/tree.c +1
-1-trace_rcu_dyntick (TPS ("Error on entry: not idle task" ), oldval,0 ); +trace_rcu_dyntick (TPS ("Error on entry: not idle task" ), rdtp->dynticks_nesting,0 );rcu_ftrace_dump (DUMP_ORIG ); WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s", current->pid, current->comm,kernel/rcu/tree.c static void rcu_eqs_enter_common(long long oldval, bool user) · line 806
Quoted 8 lines
kernel/rcu/tree.c +4
-1@@ -800,7 +800,10 @@ static void rcu_eqs_enter_common(long long oldval, bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -rcu_dynticks_eqs_enter (); +stack_tracer_disable (); + rdtp->dynticks_nesting =0 ;/* Breaks tracing momentarily. */ +rcu_dynticks_eqs_enter ();/* After this, tracing works again. */ +stack_tracer_enable ();Hmmm... There is not supposed to be any tracing in this interval,
Why not? function tracing happens without an issue. But then again, function tracing doesn't depend on RCU.
Quoted 2 lines
and interrupts are disabled. Wouldn't it be better to have something that made tracing illegal during this interval?
I don't see an issue here. Function tracing is fine. There should be no
trace_events()as those are static events and shouldn't dynamically appear in this interval.The problem I hit is that stack tracing uses function tracing to check the stack of all functions. It doesn't need RCU either, unless it hits a new "max stack", which it then calls
save_stack_trace(), which does a lot, and it does perform anrcu_read_lock(), which is what broke.I'm fine with tracing, as that works. What doesn't work is tracing a new max stack.
Quoted 1 line
Quoted 5 lines
Quoted 2 lines
Yeah, I am a bit concerned about idle-entry latency...
Which should now be fine because of the inlined this_cpu_inc/dec() which is very efficient and made for fast paths like this.
OK, I am willing to let people complain if they can measure the difference. If they can measure the difference, there are some things we can do.
Thanx, Paul
Steven Rostedt
maintainerRe: [PATCH 5/5 v2] rcu: Fix dyntick-idle tracing
Quoted 10 lines · On Fri, 7 Apr 2017 08:09:22 -0700 "Paul E. McKenney"…
Quoted 6 lines
Quoted 3 lines
Yeah, I am a bit concerned about idle-entry latency...
Which should now be fine because of the inlined this_cpu_inc/dec() which is very efficient and made for fast paths like this.
OK, I am willing to let people complain if they can measure the difference. If they can measure the difference, there are some things we can do.
Again, I was getting this_cpu_inc/dec confused with __this_cpu_inc/dec. I'll update my patch to fix that.
-- Steve
Quoted 3 lines · Steven Rostedt
Paul,
Here's my latest. You OK with it?
Given your update to 3/5, I suspect that we could live with it. I am expecting some complaints about increases in idle-entry latency, but might be best to wait for complaints rather than complexifying too proactively.
That said, there isn't supposed to be any tracing during the now very small interval where RCU's idle-entry is incomplete. Mightn't it be better to (under CONFIG_PROVE_RCU or some such) give splats if tracing showed up in that interval?
Thanx, Paul
Quoted 184 lines
-- Steve
Paul E. McKenney (1):
rcu: Fix dyntick-idle tracingSteven Rostedt (VMware) (4):
ftrace: Add use ofsynchronize_rcu_tasks()with dynamic trampolines
tracing: Replace theper_cpu()withthis_cpu()intrace_stack.c
tracing: Add stack_tracer_disable/enable() functions
tracing: Renametrace_activetodisable_stack_tracerand inline its modification----
Changes since v1:. Make stack_tracer_{dis,en}able() into inlines
. Rename
trace_activetodisable_stack_tracerand make global. Do not call
preempt_disable()fromstack_tracer_disable()
and likewise fromstack_tracer_enable(). Add check for preemption disabled when
CONFIG_PREEMPT_DEBUGinclude/linux/ftrace.h | 38 ++++++++++++++++++++++++++++++++++++ kernel/rcu/tree.c | 48 ++++++++++++++++++++++------------------------ kernel/trace/Kconfig | 3 ++- kernel/trace/ftrace.c | 42 +++++++++++++++++----------------------- kernel/trace/trace_stack.c | 27 +++++++++----------------- 5 files changed, 90 insertions(+), 68 deletions(-)Diff from v1:
include/linux/ftrace.h +34
-2@@ -287,8 +287,40 @@ stack_trace_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); -void stack_tracer_disable (void ); -void stack_tracer_enable (void ); +/* DO NOT MODIFY THIS VARIABLE DIRECTLY! */ +DECLARE_PER_CPU (int, disable_stack_tracer); + +/** +* stack_tracer_disable - temporarily disable the stack tracer +* +* There's a few locations (namely in RCU) where stack tracing +* cannot be executed. This function is used to disable stack +* tracing during those critical sections. +* +* This function must be called with preemption or interrupts +* disabled and stack_tracer_enable() must be called shortly after +* while preemption or interrupts are still disabled. +*/ +static inline void stack_tracer_disable (void ) +{ +/* Preemption or interupts must be disabled */ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_inc (disable_stack_tracer); +} + +/** +* stack_tracer_enable - re-enable the stack tracer +* +* After stack_tracer_disable() is called, stack_tracer_enable() +* must be called shortly afterward. +*/ +static inline void stack_tracer_enable (void ) +{ +if (IS_ENABLED (CONFIG_PREEMPT_DEBUG )) +WARN_ON_ONCE (!preempt_count () || !irqs_disabled ()); +this_cpu_dec (disable_stack_tracer); +}#else static inline void stack_tracer_disable (void ) { }static inline void stack_tracer_enabe (void ) { }kernel/trace/trace_stack.c +9
-37@@ -35,40 +35,12 @@ unsigned long stack_trace_max_size;arch_spinlock_t stack_trace_max_lock = (arch_spinlock_t )__ARCH_SPIN_LOCK_UNLOCKED; -static DEFINE_PER_CPU(int, trace_active); +DEFINE_PER_CPU (int, disable_stack_tracer);static DEFINE_MUTEX (stack_sysctl_mutex);int stack_tracer_enabled;static int last_stack_tracer_enabled; -/** -* stack_tracer_disable - temporarily disable the stack tracer -* -* There's a few locations (namely in RCU) where stack tracing -* can not be executed. This function is used to disable stack -* tracing during those critical sections. -* -* This function will disable preemption. stack_tracer_enable() -* must be called shortly after this is called. -*/ -void stack_tracer_disable (void ) -{ -preempt_disable_notrace (); -this_cpu_inc (trace_active); -} - -/** -* stack_tracer_enable - re-enable the stack tracer -* -* After stack_tracer_disable() is called, stack_tracer_enable() -* must shortly be called afterward. -*/ -void stack_tracer_enable (void ) -{ -this_cpu_dec (trace_active); -preempt_enable_notrace (); -} -void stack_trace_print (void ) {long i;@@ -239,8 +211,8 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip,preempt_disable_notrace ();/* no atomic needed, we only modify this variable by this cpu */ -this_cpu_inc (trace_active); -if (this_cpu_read (trace_active) !=1 ) +this_cpu_inc (disable_stack_tracer); +if (this_cpu_read (disable_stack_tracer) !=1 ) goto out; ip +=MCOUNT_INSN_SIZE ;@@ -248,7 +220,7 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip,check_stack (ip, &stack); out: -this_cpu_dec (trace_active); +this_cpu_dec (disable_stack_tracer);/* prevent recursion in schedule */ preempt_enable_notrace (); }@@ -290,15 +262,15 @@ stack_max_size_write(struct file *filp, const char __user *ubuf,/* * In case we trace inside arch_spin_lock() or after (NMI), * we will cause circular lock, so we also need to increase -* the percpu trace_active here. +* the percpu disable_stack_tracer here. */ -this_cpu_inc (trace_active); +this_cpu_inc (disable_stack_tracer);arch_spin_lock (&stack_trace_max_lock); *ptr = val;arch_spin_unlock (&stack_trace_max_lock); -this_cpu_dec (trace_active); +this_cpu_dec (disable_stack_tracer);local_irq_restore (flags);return count;@@ -334,7 +306,7 @@ static void *t_start(struct seq_file *m, loff_t *pos) {local_irq_disable (); -this_cpu_inc (trace_active); +this_cpu_inc (disable_stack_tracer);arch_spin_lock (&stack_trace_max_lock);@@ -348,7 +320,7 @@ static void t_stop(struct seq_file *m, void *p) {arch_spin_unlock (&stack_trace_max_lock); -this_cpu_dec (trace_active); +this_cpu_dec (disable_stack_tracer);local_irq_enable (); }
Steven Rostedt
maintainerQuoted 8 lines · On Fri, 7 Apr 2017 07:43:35 -0700 "Paul E. McKenney"…
Quoted 3 lines · Steven Rostedt
Paul,
Here's my latest. You OK with it?
Given your update to 3/5, I suspect that we could live with it. I am expecting some complaints about increases in idle-entry latency, but might be best to wait for complaints rather than complexifying too proactively.
We only added a this_cpu_inc() and this_cpu_dec() which are very fast operations. I highly doubt it will be measurable. Although, I'm talking about x86, IIRC, the this_cpu_inc/dec were be poorly written for other archs in the past. I'm not sure if that was fixed though.
Quoted 6 lines
That said, there isn't supposed to be any tracing during the now very small interval where RCU's idle-entry is incomplete. Mightn't it be better to (under
CONFIG_PROVE_RCUor some such) give splats if tracing showed up in that interval?
Again, tracing is not the issue. I do function tracing in that location without any problems. The issue here was the stack tracer.
Maybe we can create a new variable that is more cache local to the RCU code.
What about calling it "rcu_disabled"? Then tracing that depends on RCU can simply check that.
s/stack_trace_disable/disable_rcu/
s/stack_trace_enable/enable_rcu/
export a per cpu variable rcu_disabled
Then I can have the stack tracer check that variable as well. And we could even put in a WARN_ON(this_cpu_read(rcu_disabled) in the TRACE_EVENT() macros.
Thoughts?
-- Steve
Quoted 8 lines · Steven Rostedt
Quoted 5 lines · On Fri, 7 Apr 2017 07:43:35 -0700 "Paul E. McKenney"…
Quoted 3 lines · Steven Rostedt
Paul,
Here's my latest. You OK with it?
Quoted 8 lines
Quoted 3 lines
Given your update to 3/5, I suspect that we could live with it. I am expecting some complaints about increases in idle-entry latency, but might be best to wait for complaints rather than complexifying too proactively.
We only added a
this_cpu_inc()andthis_cpu_dec()which are very fast operations. I highly doubt it will be measurable. Although, I'm talking about x86, IIRC, the this_cpu_inc/dec were be poorly written for other archs in the past. I'm not sure if that was fixed though.
That is an issue for CPUs that don't have a to-memory increment instruction. How about __this_cpu_inc() and __this_cpu_dec(), given that preemption is disabled?
Quoted 17 lines
Quoted 4 lines
That said, there isn't supposed to be any tracing during the now very small interval where RCU's idle-entry is incomplete. Mightn't it be better to (under
CONFIG_PROVE_RCUor some such) give splats if tracing showed up in that interval?Again, tracing is not the issue. I do function tracing in that location without any problems. The issue here was the stack tracer.
Maybe we can create a new variable that is more cache local to the RCU code.
What about calling it "
rcu_disabled"? Then tracing that depends on RCU can simply check that.s/stack_trace_disable/disable_rcu/
s/stack_trace_enable/enable_rcu/
Quoted 7 lines
export a per cpu variable
rcu_disabledThen I can have the stack tracer check that variable as well. And we could even put in a WARN_ON(this_cpu_read(rcu_disabled) in the
TRACE_EVENT()macros.Thoughts?
At this point, if you can use the "__" versions, the change should be small. With that change, if no one else complains, I am OK.
Thanx, Paul
Steven Rostedt
maintainerQuoted 13 lines · On Fri, 7 Apr 2017 08:11:46 -0700 "Paul E. McKenney"…
Quoted 12 lines · Steven Rostedt
Quoted 8 lines · On Fri, 7 Apr 2017 07:43:35 -0700 "Paul E. McKenney"…
Quoted 3 lines · Steven Rostedt
Paul,
Here's my latest. You OK with it?
Given your update to 3/5, I suspect that we could live with it. I am expecting some complaints about increases in idle-entry latency, but might be best to wait for complaints rather than complexifying too proactively.
Quoted 8 lines
Quoted 4 lines
We only added a
this_cpu_inc()andthis_cpu_dec()which are very fast operations. I highly doubt it will be measurable. Although, I'm talking about x86, IIRC, the this_cpu_inc/dec were be poorly written for other archs in the past. I'm not sure if that was fixed though.That is an issue for CPUs that don't have a to-memory increment instruction. How about
__this_cpu_inc()and__this_cpu_dec(), given that preemption is disabled?
Ah, so the issue still exists. I remember complaining to Christoph Lamater about this. Yeah, switching to __this_cpu_inc() works as well.
Quoted 20 lines
Quoted 20 lines
Quoted 5 lines
That said, there isn't supposed to be any tracing during the now very small interval where RCU's idle-entry is incomplete. Mightn't it be better to (under
CONFIG_PROVE_RCUor some such) give splats if tracing showed up in that interval?Again, tracing is not the issue. I do function tracing in that location without any problems. The issue here was the stack tracer.
Maybe we can create a new variable that is more cache local to the RCU code.
What about calling it "
rcu_disabled"? Then tracing that depends on RCU can simply check that.s/stack_trace_disable/disable_rcu/
s/stack_trace_enable/enable_rcu/export a per cpu variable
rcu_disabled
Quoted 8 lines
Quoted 5 lines
Then I can have the stack tracer check that variable as well. And we could even put in a WARN_ON(this_cpu_read(rcu_disabled) in the
TRACE_EVENT()macros.Thoughts?
At this point, if you can use the "__" versions, the change should be small. With that change, if no one else complains, I am OK.
Yeah, I was thinking this_cpu_inc() was the same as __this_cpu_inc(), (where I simply forgot about the existence of __this_cpu_inc() otherwise I would have used it from the beginning.
But that said, I wonder if a rcu_disabled would make more sense. I kinda feel we are currently doing this backwards. The real state change is that rcu is currently doing something that will definitely break any rcu_irq_enter() call. Anything doing that to wake up RCU should be informed that that doesn't currently work. Having a way for RCU to tell others that "rcu_irq_enter() wont do anything right now" seems more appropriate to me, as we can then do checks in the trace_event_rcuidle() code to do the same thing.
Having RCU just pick stack tracing seems to be backward to me.
-- Steve
Steven Rostedt
maintainer[PATCH 6/5]rcu/tracing: Add rcu_disabled to denote when rcu_irq_enter() will not work
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Tracing uses rcu_irq_enter() as a way to make sure that RCU is watching when it needs to use rcu_read_lock() and friends. This is because tracing can happen as RCU is about to enter user space, or about to go idle, and RCU does not watch for RCU read side critical sections as it makes the transition.
There is a small location within the RCU infrastructure that rcu_irq_enter() itself will not work. If tracing were to occur in that section it will break if it tries to use rcu_irq_enter().
Originally, this happens with the stack_tracer, because it will call save_stack_trace when it encounters stack usage that is greater than any stack usage it had encountered previously. There was a case where that happened in the RCU section where rcu_irq_enter() did not work, and lockdep complained loudly about it. To fix it, stack tracing added a call to be disabled and RCU would disable stack tracing during the critical section that rcu_irq_enter() was inoperable. This solution worked, but there are other cases that use rcu_irq_enter() and it would be a good idea to let RCU give a way to let others know that rcu_irq_enter() will not work. For example, in trace events.
Another helpful aspect of this change is that it also moves the per cpu variable called in the RCU critical section into a cache locale along with other RCU per cpu variables used in that same location.
I'm keeping the stack_trace_disable() code, as that still could be used in the future by places that really need to disable it. And since it's only a static inline, it wont take up any kernel text if it is not used.
--- include/linux/rcupdate.h | 5 +++++ kernel/rcu/tree.c | 18 ++++++++++++++++-- kernel/trace/trace_stack.c | 8 ++++++++ 3 files changed, 29 insertions(+), 2 deletions(-)
include/linux/rcupdate.h +5 -0
@@ -97,6 +97,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,unsigned long secs, unsigned long c_old, unsigned long c); +bool rcu_is_disabled (void );#else static inline void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,
@@ -113,6 +114,10 @@ static inline void rcutorture_record_test_transition(void)static inline void rcutorture_record_progress (unsigned long vernum) { } +bool rcu_is_disabled (void ) +{ +return false; +}#ifdef CONFIG_RCU_TRACE void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp,
kernel/rcu/tree.c +16 -2
@@ -285,6 +285,20 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { };/* +* There's a few places, currently just in the tracing infrastructure, +* that uses rcu_irq_enter() to make sure RCU is watching. But there's +* a small location where that will not even work. In those cases +* rcu_is_disabled() needs to be checked to make sure rcu_irq_enter() +* can be called. +*/ +static DEFINE_PER_CPU(bool, rcu_disabled); + +bool rcu_is_disabled (void ) +{ +return this_cpu_read (rcu_disabled); +} + +/* * Record entry into an extended quiescent state. This is only to be * called when not already in an extended quiescent state. */
@@ -800,10 +814,10 @@ static void rcu_eqs_enter_common(bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -stack_tracer_disable (); +__this_cpu_inc (rcu_disabled); rdtp->dynticks_nesting =0 ;/* Breaks tracing momentarily. */ rcu_dynticks_eqs_enter ();/* After this, tracing works again. */ -stack_tracer_enable (); +__this_cpu_dec (rcu_disabled);rcu_dynticks_task_enter ();/*
kernel/trace/trace_stack.c +8 -0
@@ -96,6 +96,14 @@ check_stack(unsigned long ip, unsigned long *stack)if (in_nmi ())return ; +/* +* There's a slight chance that we are tracing inside the +* RCU infrastructure, and rcu_irq_enter() will not work +* as expected. +*/ +if (unlikely (rcu_is_disabled ())) +return ; +local_irq_save (flags);arch_spin_lock (&stack_trace_max_lock);
Signature · 1 line
2.9.3
Re: [PATCH 6/5]rcu/tracing: Add rcu_disabled to denote when rcu_irq_enter() will not work
Quoted 27 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Tracing uses
rcu_irq_enter()as a way to make sure that RCU is watching when it needs to usercu_read_lock()and friends. This is because tracing can happen as RCU is about to enter user space, or about to go idle, and RCU does not watch for RCU read side critical sections as it makes the transition.There is a small location within the RCU infrastructure that
rcu_irq_enter()itself will not work. If tracing were to occur in that section it will break if it tries to usercu_irq_enter().Originally, this happens with the
stack_tracer, because it will callsave_stack_tracewhen it encounters stack usage that is greater than any stack usage it had encountered previously. There was a case where that happened in the RCU section wherercu_irq_enter()did not work, and lockdep complained loudly about it. To fix it, stack tracing added a call to be disabled and RCU would disable stack tracing during the critical section thatrcu_irq_enter()was inoperable. This solution worked, but there are other cases that usercu_irq_enter()and it would be a good idea to let RCU give a way to let others know thatrcu_irq_enter()will not work. For example, in trace events.Another helpful aspect of this change is that it also moves the per cpu variable called in the RCU critical section into a cache locale along with other RCU per cpu variables used in that same location.
Quoted 5 lines
I'm keeping the
stack_trace_disable()code, as that still could be used in the future by places that really need to disable it. And since it's only a static inline, it wont take up any kernel text if it is not used.Signed-off-by Steven Rostedt (VMware) <[email protected]>
Looks better, especially __this_cpu_inc() and __this_cpu_dec().
How about rcu_irq_enter_disabled instead of rcu_disabled? We aren't really disabling all of RCU. ;-)
Thanx, Paul
Quoted 89 lines
--- include/linux/rcupdate.h | 5 +++++ kernel/rcu/tree.c | 18 ++++++++++++++++-- kernel/trace/trace_stack.c | 8 ++++++++ 3 files changed, 29 insertions(+), 2 deletions(-)include/linux/rcupdate.h +5
-0@@ -97,6 +97,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,unsigned long secs, unsigned long c_old, unsigned long c); +bool rcu_is_disabled (void );#else static inline void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,@@ -113,6 +114,10 @@ static inline void rcutorture_record_test_transition(void)static inline void rcutorture_record_progress (unsigned long vernum) { } +bool rcu_is_disabled (void ) +{ +return false; +}#ifdef CONFIG_RCU_TRACE void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp,kernel/rcu/tree.c +16
-2@@ -285,6 +285,20 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { };/* +* There's a few places, currently just in the tracing infrastructure, +* that uses rcu_irq_enter() to make sure RCU is watching. But there's +* a small location where that will not even work. In those cases +* rcu_is_disabled() needs to be checked to make sure rcu_irq_enter() +* can be called. +*/ +static DEFINE_PER_CPU(bool, rcu_disabled); + +bool rcu_is_disabled (void ) +{ +return this_cpu_read (rcu_disabled); +} + +/* * Record entry into an extended quiescent state. This is only to be * called when not already in an extended quiescent state. */ @@ -800,10 +814,10 @@ static void rcu_eqs_enter_common(bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -stack_tracer_disable (); +__this_cpu_inc (rcu_disabled); rdtp->dynticks_nesting =0 ;/* Breaks tracing momentarily. */ rcu_dynticks_eqs_enter ();/* After this, tracing works again. */ -stack_tracer_enable (); +__this_cpu_dec (rcu_disabled);rcu_dynticks_task_enter ();/* kernel/trace/trace_stack.c +8
-0@@ -96,6 +96,14 @@ check_stack(unsigned long ip, unsigned long *stack)if (in_nmi ())return ; +/* +* There's a slight chance that we are tracing inside the +* RCU infrastructure, and rcu_irq_enter() will not work +* as expected. +*/ +if (unlikely (rcu_is_disabled ())) +return ; +local_irq_save (flags);arch_spin_lock (&stack_trace_max_lock);Signature · 1 line
2.9.3
Steven Rostedt
maintainerRe: [PATCH 6/5]rcu/tracing: Add rcu_disabled to denote when rcu_irq_enter() will not work
Quoted 32 lines · On Fri, 7 Apr 2017 09:42:20 -0700 "Paul E. McKenney"…
Quoted 31 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Tracing uses
rcu_irq_enter()as a way to make sure that RCU is watching when it needs to usercu_read_lock()and friends. This is because tracing can happen as RCU is about to enter user space, or about to go idle, and RCU does not watch for RCU read side critical sections as it makes the transition.There is a small location within the RCU infrastructure that
rcu_irq_enter()itself will not work. If tracing were to occur in that section it will break if it tries to usercu_irq_enter().Originally, this happens with the
stack_tracer, because it will callsave_stack_tracewhen it encounters stack usage that is greater than any stack usage it had encountered previously. There was a case where that happened in the RCU section wherercu_irq_enter()did not work, and lockdep complained loudly about it. To fix it, stack tracing added a call to be disabled and RCU would disable stack tracing during the critical section thatrcu_irq_enter()was inoperable. This solution worked, but there are other cases that usercu_irq_enter()and it would be a good idea to let RCU give a way to let others know thatrcu_irq_enter()will not work. For example, in trace events.Another helpful aspect of this change is that it also moves the per cpu variable called in the RCU critical section into a cache locale along with other RCU per cpu variables used in that same location.
I'm keeping the
stack_trace_disable()code, as that still could be used in the future by places that really need to disable it. And since it's only a static inline, it wont take up any kernel text if it is not used.
Quoted 6 lines
Quoted 1 line
Signed-off-by Steven Rostedt (VMware) <[email protected]>Looks better, especially
__this_cpu_inc()and__this_cpu_dec().How about
rcu_irq_enter_disabledinstead ofrcu_disabled? We aren't really disabling all of RCU. ;-)
OK, I'll make the update and send a 6.1 (and also a new 7/5)!
-- Steve
Re: [PATCH 6/5]rcu/tracing: Add rcu_disabled to denote when rcu_irq_enter() will not work
Quoted 35 lines · Steven Rostedt
Quoted 32 lines · On Fri, 7 Apr 2017 09:42:20 -0700 "Paul E. McKenney"…
Quoted 31 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Tracing uses
rcu_irq_enter()as a way to make sure that RCU is watching when it needs to usercu_read_lock()and friends. This is because tracing can happen as RCU is about to enter user space, or about to go idle, and RCU does not watch for RCU read side critical sections as it makes the transition.There is a small location within the RCU infrastructure that
rcu_irq_enter()itself will not work. If tracing were to occur in that section it will break if it tries to usercu_irq_enter().Originally, this happens with the
stack_tracer, because it will callsave_stack_tracewhen it encounters stack usage that is greater than any stack usage it had encountered previously. There was a case where that happened in the RCU section wherercu_irq_enter()did not work, and lockdep complained loudly about it. To fix it, stack tracing added a call to be disabled and RCU would disable stack tracing during the critical section thatrcu_irq_enter()was inoperable. This solution worked, but there are other cases that usercu_irq_enter()and it would be a good idea to let RCU give a way to let others know thatrcu_irq_enter()will not work. For example, in trace events.Another helpful aspect of this change is that it also moves the per cpu variable called in the RCU critical section into a cache locale along with other RCU per cpu variables used in that same location.
I'm keeping the
stack_trace_disable()code, as that still could be used in the future by places that really need to disable it. And since it's only a static inline, it wont take up any kernel text if it is not used.
Quoted 8 lines
Quoted 6 lines
Quoted 1 line
Signed-off-by Steven Rostedt (VMware) <[email protected]>Looks better, especially
__this_cpu_inc()and__this_cpu_dec().How about
rcu_irq_enter_disabledinstead ofrcu_disabled? We aren't really disabling all of RCU. ;-)OK, I'll make the update and send a 6.1 (and also a new 7/5)!
So the ex-Borg member was really 9 of 7 rather than 7 of 9, then? ;-)
Thanx, Paul
Steven Rostedt
maintainer[PATCH 6/5 v2] rcu/tracing: Add rcu_disabled to denote when rcu_irq_enter() will not work
In reply to Paul E. McKenneyFrom: "Steven Rostedt (VMware)" <[email protected]>
Tracing uses rcu_irq_enter() as a way to make sure that RCU is watching when it needs to use rcu_read_lock() and friends. This is because tracing can happen as RCU is about to enter user space, or about to go idle, and RCU does not watch for RCU read side critical sections as it makes the transition.
There is a small location within the RCU infrastructure that rcu_irq_enter() itself will not work. If tracing were to occur in that section it will break if it tries to use rcu_irq_enter().
Originally, this happens with the stack_tracer, because it will call save_stack_trace when it encounters stack usage that is greater than any stack usage it had encountered previously. There was a case where that happened in the RCU section where rcu_irq_enter() did not work, and lockdep complained loudly about it. To fix it, stack tracing added a call to be disabled and RCU would disable stack tracing during the critical section that rcu_irq_enter() was inoperable. This solution worked, but there are other cases that use rcu_irq_enter() and it would be a good idea to let RCU give a way to let others know that rcu_irq_enter() will not work. For example, in trace events.
Another helpful aspect of this change is that it also moves the per cpu variable called in the RCU critical section into a cache locale along with other RCU per cpu variables used in that same location.
I'm keeping the stack_trace_disable() code, as that still could be used in the future by places that really need to disable it. And since it's only a static inline, it wont take up any kernel text if it is not used.
--- include/linux/rcupdate.h | 5 +++++ kernel/rcu/tree.c | 18 ++++++++++++++++-- kernel/trace/trace_stack.c | 8 ++++++++ 3 files changed, 29 insertions(+), 2 deletions(-)
include/linux/rcupdate.h +5 -0
@@ -97,6 +97,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,unsigned long secs, unsigned long c_old, unsigned long c); +bool rcu_irq_enter_disabled (void );#else static inline void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,
@@ -113,6 +114,10 @@ static inline void rcutorture_record_test_transition(void)static inline void rcutorture_record_progress (unsigned long vernum) { } +bool rcu_irq_enter_disabled (void ) +{ +return false; +}#ifdef CONFIG_RCU_TRACE void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp,
kernel/rcu/tree.c +16 -2
@@ -285,6 +285,20 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { };/* +* There's a few places, currently just in the tracing infrastructure, +* that uses rcu_irq_enter() to make sure RCU is watching. But there's +* a small location where that will not even work. In those cases +* rcu_irq_enter_disabled() needs to be checked to make sure rcu_irq_enter() +* can be called. +*/ +static DEFINE_PER_CPU(bool, disable_rcu_irq_enter); + +bool rcu_irq_enter_disabled (void ) +{ +return this_cpu_read (disable_rcu_irq_enter); +} + +/* * Record entry into an extended quiescent state. This is only to be * called when not already in an extended quiescent state. */
@@ -800,10 +814,10 @@ static void rcu_eqs_enter_common(bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -stack_tracer_disable (); +__this_cpu_inc (disable_rcu_irq_enter); rdtp->dynticks_nesting =0 ;/* Breaks tracing momentarily. */ rcu_dynticks_eqs_enter ();/* After this, tracing works again. */ -stack_tracer_enable (); +__this_cpu_dec (disable_rcu_irq_enter);rcu_dynticks_task_enter ();/*
kernel/trace/trace_stack.c +8 -0
@@ -96,6 +96,14 @@ check_stack(unsigned long ip, unsigned long *stack)if (in_nmi ())return ; +/* +* There's a slight chance that we are tracing inside the +* RCU infrastructure, and rcu_irq_enter() will not work +* as expected. +*/ +if (unlikely (rcu_irq_enter_disabled ())) +return ; +local_irq_save (flags);arch_spin_lock (&stack_trace_max_lock);
Signature · 1 line
2.9.3
Paul E. McKenney
Acked-byRe: [PATCH 6/5 v2] rcu/tracing: Add rcu_disabled to denote when rcu_irq_enter() will not work
Quoted 27 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Tracing uses
rcu_irq_enter()as a way to make sure that RCU is watching when it needs to usercu_read_lock()and friends. This is because tracing can happen as RCU is about to enter user space, or about to go idle, and RCU does not watch for RCU read side critical sections as it makes the transition.There is a small location within the RCU infrastructure that
rcu_irq_enter()itself will not work. If tracing were to occur in that section it will break if it tries to usercu_irq_enter().Originally, this happens with the
stack_tracer, because it will callsave_stack_tracewhen it encounters stack usage that is greater than any stack usage it had encountered previously. There was a case where that happened in the RCU section wherercu_irq_enter()did not work, and lockdep complained loudly about it. To fix it, stack tracing added a call to be disabled and RCU would disable stack tracing during the critical section thatrcu_irq_enter()was inoperable. This solution worked, but there are other cases that usercu_irq_enter()and it would be a good idea to let RCU give a way to let others know thatrcu_irq_enter()will not work. For example, in trace events.Another helpful aspect of this change is that it also moves the per cpu variable called in the RCU critical section into a cache locale along with other RCU per cpu variables used in that same location.
Quoted 5 lines
I'm keeping the
stack_trace_disable()code, as that still could be used in the future by places that really need to disable it. And since it's only a static inline, it wont take up any kernel text if it is not used.Signed-off-by Steven Rostedt (VMware) <[email protected]>
Looks good!
Quoted 89 lines
--- include/linux/rcupdate.h | 5 +++++ kernel/rcu/tree.c | 18 ++++++++++++++++-- kernel/trace/trace_stack.c | 8 ++++++++ 3 files changed, 29 insertions(+), 2 deletions(-)include/linux/rcupdate.h +5
-0@@ -97,6 +97,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,unsigned long secs, unsigned long c_old, unsigned long c); +bool rcu_irq_enter_disabled (void );#else static inline void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,@@ -113,6 +114,10 @@ static inline void rcutorture_record_test_transition(void)static inline void rcutorture_record_progress (unsigned long vernum) { } +bool rcu_irq_enter_disabled (void ) +{ +return false; +}#ifdef CONFIG_RCU_TRACE void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp,kernel/rcu/tree.c +16
-2@@ -285,6 +285,20 @@ static DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = { };/* +* There's a few places, currently just in the tracing infrastructure, +* that uses rcu_irq_enter() to make sure RCU is watching. But there's +* a small location where that will not even work. In those cases +* rcu_irq_enter_disabled() needs to be checked to make sure rcu_irq_enter() +* can be called. +*/ +static DEFINE_PER_CPU(bool, disable_rcu_irq_enter); + +bool rcu_irq_enter_disabled (void ) +{ +return this_cpu_read (disable_rcu_irq_enter); +} + +/* * Record entry into an extended quiescent state. This is only to be * called when not already in an extended quiescent state. */ @@ -800,10 +814,10 @@ static void rcu_eqs_enter_common(bool user)do_nocb_deferred_wakeup (rdp); }rcu_prepare_for_idle (); -stack_tracer_disable (); +__this_cpu_inc (disable_rcu_irq_enter); rdtp->dynticks_nesting =0 ;/* Breaks tracing momentarily. */ rcu_dynticks_eqs_enter ();/* After this, tracing works again. */ -stack_tracer_enable (); +__this_cpu_dec (disable_rcu_irq_enter);rcu_dynticks_task_enter ();/* kernel/trace/trace_stack.c +8
-0@@ -96,6 +96,14 @@ check_stack(unsigned long ip, unsigned long *stack)if (in_nmi ())return ; +/* +* There's a slight chance that we are tracing inside the +* RCU infrastructure, and rcu_irq_enter() will not work +* as expected. +*/ +if (unlikely (rcu_irq_enter_disabled ())) +return ; +local_irq_save (flags);arch_spin_lock (&stack_trace_max_lock);Signature · 1 line
2.9.3
Steven Rostedt
maintainer[PATCH 7/5] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
In reply to Steven RostedtFrom: "Steven Rostedt (VMware)" <[email protected]>
Stack tracing discovered that there's a small location inside the RCU infrastructure that calling rcu_irq_enter() does not work. As trace events use rcu_irq_enter() it must make sure that it is functionable. A check against rcu_irq_enter_disabled() is added with a WARN_ON_ONCE() as no trace event should ever be used in that part of RCU. If the warning is triggered, then the trace event is ignored.
--- include/linux/tracepoint.h | 2 ++ 1 file changed, 2 insertions(+)
include/linux/tracepoint.h +2 -0
@@ -159,6 +159,8 @@ extern void syscall_unregfunc(void);TP_PROTO (data_proto), \TP_ARGS (data_args ), \TP_CONDITION (cond ), \ + if (WARN_ON_ONCE(rcu_irq_enter_disabled())) \ + return; \rcu_irq_enter_irqson (), \ rcu_irq_exit_irqson()); \ }
Signature · 1 line
2.9.3
Paul E. McKenney
Acked-byRe: [PATCH 7/5] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
Quoted 11 lines · Steven Rostedt
From: "Steven Rostedt (VMware)" <[email protected]>
Stack tracing discovered that there's a small location inside the RCU infrastructure that calling
rcu_irq_enter()does not work. As trace events usercu_irq_enter()it must make sure that it is functionable. A check againstrcu_irq_enter_disabled()is added with aWARN_ON_ONCE()as no trace event should ever be used in that part of RCU. If the warning is triggered, then the trace event is ignored.Cc Mathieu Desnoyers <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>
Quoted 20 lines
--- include/linux/tracepoint.h | 2 ++ 1 file changed, 2 insertions(+)include/linux/tracepoint.h +2
-0@@ -159,6 +159,8 @@ extern void syscall_unregfunc(void);TP_PROTO (data_proto), \TP_ARGS (data_args ), \TP_CONDITION (cond ), \ + if (WARN_ON_ONCE(rcu_irq_enter_disabled())) \ + return; \rcu_irq_enter_irqson (), \ rcu_irq_exit_irqson()); \ }Signature · 1 line
2.9.3
Mathieu Desnoyers
reviewerRe: [PATCH 7/5] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
In reply to Steven RostedtQuoted 4 lines · ----- On Apr 7, 2017, at 1:06 PM, rostedt [email protected] wrote:
From: "Steven Rostedt (VMware)" <[email protected]>
Stack tracing discovered that there's a small location inside the RCU infrastructure that calling
rcu_irq_enter()does not work. As trace events
that -> where
Do you have a link to the lkml thread where this stack tracing discovery happened ?
Quoted 1 line
use
rcu_irq_enter()it must make sure that it is functionable. A check
I don't think functionable is the word you are looking for here. Perhaps "must make sure that it can be invoked" ?
Quoted 15 lines
against
rcu_irq_enter_disabled()is added with aWARN_ON_ONCE()as no trace event should ever be used in that part of RCU. If the warning is triggered, then the trace event is ignored.Cc Mathieu Desnoyers <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>---
include/linux/tracepoint.h| 2 ++1 file changed, 2 insertions(+)include/linux/tracepoint.h +0
-0
include/linux/tracepoint.h extern void syscall_unregfunc(void); · line 163
Quoted 5 lines
include/linux/tracepoint.h +2
-0@@ -159,6 +159,8 @@ extern void syscall_unregfunc(void);TP_PROTO (data_proto), \TP_ARGS (data_args ), \ TP_CONDITION(cond), \ + if (WARN_ON_ONCE(rcu_irq_enter_disabled())) \ + return; \
I must admit that it's a bit odd to have:
if (WARN_ON_ONCE(rcu_irq_enter_disabled()))
return;
rcu_irq_enter_irqson()
as one argument to the __DO_TRACE() macro. To me it's a bit unexpected coding-style wise. Am I the only one not comfortable with the proposed syntax ?
Thanks,
Mathieu
Quoted 5 lines
include/linux/tracepoint.h +0
-1rcu_irq_enter_irqson (), \ rcu_irq_exit_irqson()); \ } --2.9.3
Signature · 3 lines
Mathieu Desnoyers EfficiOS Inc. http://www.efficios.com
Steven Rostedt
maintainerRe: [PATCH 7/5] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
Quoted 8 lines · On Fri, 7 Apr 2017 17:19:05 +0000 (UTC) Mathieu Desnoyers…
Quoted 4 lines · ----- On Apr 7, 2017, at 1:06 PM, rostedt [email protected] wrote:
From: "Steven Rostedt (VMware)" <[email protected]>
Stack tracing discovered that there's a small location inside the RCU infrastructure that calling
rcu_irq_enter()does not work. As trace eventsthat -> where
ok
Quoted 3 lines
Do you have a link to the lkml thread where this stack tracing discovery happened ?
Actually it's this thread. But here:
Version 1 of the patch series:
http://lkml.kernel.org/r/[email protected]
Version 2 of the patch series:
http://lkml.kernel.org/r/[email protected]
Quoted 29 lines
Quoted 2 lines
use
rcu_irq_enter()it must make sure that it is functionable. A checkI don't think functionable is the word you are looking for here. Perhaps "must make sure that it can be invoked" ?
Quoted 15 lines
against
rcu_irq_enter_disabled()is added with aWARN_ON_ONCE()as no trace event should ever be used in that part of RCU. If the warning is triggered, then the trace event is ignored.Cc Mathieu Desnoyers <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>---
include/linux/tracepoint.h| 2 ++1 file changed, 2 insertions(+)include/linux/tracepoint.h +0
-0include/linux/tracepoint.h extern void syscall_unregfunc(void); · line 163
Quoted 5 lines
include/linux/tracepoint.h +2
-0@@ -159,6 +159,8 @@ extern void syscall_unregfunc(void);TP_PROTO (data_proto), \TP_ARGS (data_args ), \ TP_CONDITION(cond), \ + if (WARN_ON_ONCE(rcu_irq_enter_disabled())) \ + return; \I must admit that it's a bit odd to have:
Quoted 3 lines
if (WARN_ON_ONCE(rcu_irq_enter_disabled()))
return;
rcu_irq_enter_irqson()
Welcome to MACRO MAGIC!
Quoted 4 lines
as one argument to the
__DO_TRACE()macro. To me it's a bit unexpected coding-style wise. Am I the only one not comfortable with the proposed syntax ?
The entire TRACE_EVENT()/__DO_TRACE() is special.
I thought about add yet another parameter, but as it doesn't change much, I figured this was good enough. We could beak it up if you like:
#define RCU_IRQ_ENTER_CHECK \ if (WARN_ON_ONCE(rcu_irq_enter_disabled()) \ return; \ rcu_irq_enter_irqson();
[..]
__DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ TP_CONDITION(cond), \ PARAMS(RCU_IRQ_ENTER_CHECK), \ rcu_irq_exit_irqson()); \
Would that make you feel more comfortable?
-- Steve
Steven Rostedt
maintainerRe: [PATCH 7/5] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
Quoted 10 lines · On Fri, 7 Apr 2017 13:26:13 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 2 lines
Do you have a link to the lkml thread where this stack tracing discovery happened ?
Actually it's this thread. But here:
Version 1 of the patch series:
http://lkml.kernel.org/r/[email protected]Version 2 of the patch series:
http://lkml.kernel.org/r/[email protected]
Oh, and I did post a bug report on it. I should add this link in the change log:
http://lkml.kernel.org/r/[email protected]
-- Steve
Mathieu Desnoyers
reviewerRe: [PATCH 7/5] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
In reply to Steven RostedtQuoted 3 lines · ----- On Apr 7, 2017, at 1:26 PM, rostedt [email protected] wrote:
On Fri, 7 Apr 2017 17:19:05 +0000 (UTC)
Mathieu Desnoyers <[email protected]> wrote:
[...]
Quoted 47 lines
Quoted 17 lines
Quoted 9 lines
---
include/linux/tracepoint.h| 2 ++1 file changed, 2 insertions(+)include/linux/tracepoint.h +0
-0include/linux/tracepoint.h extern void syscall_unregfunc(void); · line 163
Quoted 5 lines
include/linux/tracepoint.h +2
-0@@ -159,6 +159,8 @@ extern void syscall_unregfunc(void);TP_PROTO (data_proto), \TP_ARGS (data_args ), \ TP_CONDITION(cond), \ + if (WARN_ON_ONCE(rcu_irq_enter_disabled())) \ + return; \I must admit that it's a bit odd to have:
Quoted 3 lines
if (WARN_ON_ONCE(rcu_irq_enter_disabled()))
return;
rcu_irq_enter_irqson()Welcome to MACRO MAGIC!
Quoted 4 lines
as one argument to the
__DO_TRACE()macro. To me it's a bit unexpected coding-style wise. Am I the only one not comfortable with the proposed syntax ?The entire TRACE_EVENT()/__DO_TRACE() is special.
I thought about add yet another parameter, but as it doesn't change much, I figured this was good enough. We could beak it up if you like:
#define RCU_IRQ_ENTER_CHECK \ if (WARN_ON_ONCE(rcu_irq_enter_disabled()) \ return; \ rcu_irq_enter_irqson();[..]
__DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ TP_CONDITION(cond), \ PARAMS(RCU_IRQ_ENTER_CHECK), \ rcu_irq_exit_irqson()); \
Quoted 1 line
Would that make you feel more comfortable?
No, it's almost worse and adds still adds a return that apply within __DO_TRACE(), but which is passed as an argument (code as macro argument), which I find really unsettling.
I would prefer to add a new argument to __DO_TRACE, which we can call "checkrcu", e.g.:
#define __DO_TRACE(tp, proto, args, cond, checkrcu, prercu, postrcu) \
do { \
struct tracepoint_func *it_func_ptr; \
void *it_func; \
void *__data; \
\
if (!((cond) && (checkrcu))) \
return; \
prercu; \
rcu_read_lock_sched_notrace(); \
it_func_ptr = rcu_dereference_sched((tp)->funcs); \
if (it_func_ptr) { \
do { \
it_func = (it_func_ptr)->func; \
__data = (it_func_ptr)->data; \
((void(*)(proto))(it_func))(args); \
} while ((++it_func_ptr)->func); \
} \
rcu_read_unlock_sched_notrace(); \
postrcu; \
} while (0)And use it like this:
#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args) \
static inline void trace_##name##_rcuidle(proto) \
{ \
if (static_key_false(&__tracepoint_##name.key)) \
__DO_TRACE(&__tracepoint_##name, \
TP_PROTO(data_proto), \
TP_ARGS(data_args), \
TP_CONDITION(cond), \
!WARN_ON_ONCE(rcu_irq_enter_disabled()),\
rcu_irq_enter_irqson(), \
rcu_irq_exit_irqson()); \
}This way we only pass evaluated expression (not code with "return" that changes the flow) as arguments to __DO_TRACE, which makes it behave more like a "sub-function", which is what we usually expect.
Thanks,
Mathieu
Signature · 3 lines
Mathieu Desnoyers EfficiOS Inc. http://www.efficios.com
Steven Rostedt
maintainerRe: [PATCH 7/5] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
Quoted 1 line · On Fri, 7 Apr 2017 17:49:17 +0000 (UTC) Mathieu Desnoyers…
Quoted 1 line
Welcome to MACRO MAGIC!
Somebody is not wizardly happy.
Quoted 25 lines
Quoted 25 lines
Quoted 5 lines
as one argument to the
__DO_TRACE()macro. To me it's a bit unexpected coding-style wise. Am I the only one not comfortable with the proposed syntax ?The entire TRACE_EVENT()/__DO_TRACE() is special.
I thought about add yet another parameter, but as it doesn't change much, I figured this was good enough. We could beak it up if you like:
#define RCU_IRQ_ENTER_CHECK \ if (WARN_ON_ONCE(rcu_irq_enter_disabled()) \ return; \ rcu_irq_enter_irqson();[..]
__DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ TP_CONDITION(cond), \ PARAMS(RCU_IRQ_ENTER_CHECK), \ rcu_irq_exit_irqson()); \
Quoted 5 lines
Quoted 1 line
Would that make you feel more comfortable?
No, it's almost worse and adds still adds a return that apply within
__DO_TRACE(), but which is passed as an argument (code as macro argument), which I find really unsettling.
/me finds it strangely enjoyable to make Mathieu unsettled.
Quoted 5 lines
I would prefer to add a new argument to
__DO_TRACE, which we can call "checkrcu", e.g.:#define
__DO_TRACE(tp, proto, args, cond, checkrcu, prercu, postrcu)\
Grumble. I was trying to avoid making the patch more intrusive. But I do understand your concern.
Quoted 36 lines
do { \ struct tracepoint_func *it_func_ptr; \ void *it_func; \ void *__data; \ \ if (!((cond) && (checkrcu))) \ return; \ prercu; \ rcu_read_lock_sched_notrace(); \ it_func_ptr = rcu_dereference_sched((tp)->funcs); \ if (it_func_ptr) { \ do { \ it_func = (it_func_ptr)->func; \ __data = (it_func_ptr)->data; \ ((void(*)(proto))(it_func))(args); \ } while ((++it_func_ptr)->func); \ } \ rcu_read_unlock_sched_notrace(); \ postrcu; \ } while (0)And use it like this:
#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args) \ static inline void trace_##name##_rcuidle(proto) \ { \ if (static_key_false(&__tracepoint_##name.key)) \ __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ TP_CONDITION(cond), \ !WARN_ON_ONCE(rcu_irq_enter_disabled()),\ rcu_irq_enter_irqson(), \ rcu_irq_exit_irqson()); \ }
Quoted 3 lines
This way we only pass evaluated expression (not code with "return" that changes the flow) as arguments to
__DO_TRACE, which makes it behave more like a "sub-function", which is what we usually expect.
I understand what you are getting at, and I will concede your point. OK, I'll do it your way, but I still think you take all the fun out of it. ;-)
-- Steve
Steven Rostedt
maintainer[PATCH 7/5 v3] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
In reply to Mathieu DesnoyersFrom: "Steven Rostedt (VMware)" <[email protected]>
Stack tracing discovered that there's a small location inside the RCU infrastructure where calling rcu_irq_enter() does not work. As trace events use rcu_irq_enter() it must make sure that it is functionable. A check against rcu_irq_enter_disabled() is added with a WARN_ON_ONCE() as no trace event should ever be used in that part of RCU. If the warning is triggered, then the trace event is ignored.
---
Mathieu,
There! Are you now satisfied?
include/linux/tracepoint.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
include/linux/tracepoint.h +6 -3
@@ -128,7 +128,7 @@ extern void syscall_unregfunc(void);* as "(void *, void)". The DECLARE_TRACE_NOARGS() will pass in just * "void *data", where as the DECLARE_TRACE() will pass in "void *data, proto". */ -#define __DO_TRACE (tp, proto, args, cond, prercu, postrcu) \ +#define __DO_TRACE (tp, proto, args, cond, rcucheck, prercu, postrcu) \ do { \ struct tracepoint_func *it_func_ptr; \ void *it_func; \
@@ -136,6 +136,8 @@ extern void syscall_unregfunc(void); \if (!(cond)) \return ; \ +if (rcucheck) \ +return ; \ prercu; \rcu_read_lock_sched_notrace (); \ it_func_ptr =rcu_dereference_sched ((tp)->funcs); \
@@ -151,7 +153,7 @@ extern void syscall_unregfunc(void); } while (0)#ifndef MODULE -#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ +#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ static inline void trace_##name##_rcuidle(proto) \ { \ if (static_key_false(&__tracepoint_##name.key)) \
@@ -159,6 +161,7 @@ extern void syscall_unregfunc(void);TP_PROTO (data_proto), \TP_ARGS (data_args), \TP_CONDITION (cond), \ +WARN_ON_ONCE (rcu_irq_enter_disabled ()),\rcu_irq_enter_irqson (), \ rcu_irq_exit_irqson()); \ }
@@ -186,7 +189,7 @@ extern void syscall_unregfunc(void); __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ - TP_CONDITION(cond),,); \ + TP_CONDITION(cond),0,,); \ if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { \ rcu_read_lock_sched_notrace(); \ rcu_dereference_sched(__tracepoint_##name.funcs);\
Signature · 1 line
2.9.3
Re: [PATCH 7/5 v3] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
Quoted 11 lines · ----- On Apr 7, 2017, at 2:10 PM, rostedt [email protected] wrote:
From: "Steven Rostedt (VMware)" <[email protected]>
Stack tracing discovered that there's a small location inside the RCU infrastructure where calling
rcu_irq_enter()does not work. As trace events usercu_irq_enter()it must make sure that it is functionable. A check againstrcu_irq_enter_disabled()is added with aWARN_ON_ONCE()as no trace event should ever be used in that part of RCU. If the warning is triggered, then the trace event is ignored.
Quoted 7 lines
Cc Mathieu (making code boring) Desnoyers <[email protected]>Signed-off-by Steven Rostedt (VMware) <[email protected]>---
Mathieu,
There! Are you now satisfied?
Since you reversed the boolean logic from my proposal, perhaps rename the "rcucheck" argument to "rcudisabled" to reflect the logic swap ?
Other than that:
Thanks,
Mathieu
Quoted 56 lines
include/linux/tracepoint.h| 9 ++++++---1 file changed, 6 insertions(+), 3 deletions(-)include/linux/tracepoint.h +6
-4@@ -128,7 +128,7 @@ extern void syscall_unregfunc(void);* as "(void *, void)". The DECLARE_TRACE_NOARGS() will pass in just * "void *data", where as the DECLARE_TRACE() will pass in "void *data, proto". */ -#define __DO_TRACE (tp, proto, args, cond, prercu, postrcu) \ +#define __DO_TRACE (tp, proto, args, cond, rcucheck, prercu, postrcu) \ do { \ struct tracepoint_func *it_func_ptr; \ void *it_func; \@@ -136,6 +136,8 @@ extern void syscall_unregfunc(void); \if (!(cond)) \return ; \ +if (rcucheck) \ +return ; \ prercu; \rcu_read_lock_sched_notrace (); \ it_func_ptr =rcu_dereference_sched ((tp)->funcs); \@@ -151,7 +153,7 @@ extern void syscall_unregfunc(void); } while (0)#ifndef MODULE -#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ +#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ static inline void trace_##name##_rcuidle(proto) \ { \ if (static_key_false(&__tracepoint_##name.key)) \@@ -159,6 +161,7 @@ extern void syscall_unregfunc(void);TP_PROTO (data_proto), \TP_ARGS (data_args), \TP_CONDITION (cond), \ +WARN_ON_ONCE (rcu_irq_enter_disabled ()),\rcu_irq_enter_irqson (), \ rcu_irq_exit_irqson()); \ }@@ -186,7 +189,7 @@ extern void syscall_unregfunc(void); __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ - TP_CONDITION(cond),,); \ + TP_CONDITION(cond),0,,); \ if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { \ rcu_read_lock_sched_notrace(); \ rcu_dereference_sched(__tracepoint_##name.funcs);\ --2.9.3
Signature · 3 lines
Mathieu Desnoyers EfficiOS Inc. http://www.efficios.com
Steven Rostedt
maintainer[PATCH 7/5 v4] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
From: "Steven Rostedt (VMware)" <[email protected]>
Stack tracing discovered that there's a small location inside the RCU infrastructure where calling rcu_irq_enter() does not work. As trace events use rcu_irq_enter() it must make sure that it is functionable. A check against rcu_irq_enter_disabled() is added with a WARN_ON_ONCE() as no trace event should ever be used in that part of RCU. If the warning is triggered, then the trace event is ignored.
Restructure the __DO_TRACE() a bit to get rid of the prercu and postrcu, and just have an rcucheck that does the work from within the _DO_TRACE() macro. gcc optimization will compile out the rcucheck=0 case.
--- include/linux/tracepoint.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
include/linux/tracepoint.h +11 -8
@@ -128,7 +128,7 @@ extern void syscall_unregfunc(void);* as "(void *, void)". The DECLARE_TRACE_NOARGS() will pass in just * "void *data", where as the DECLARE_TRACE() will pass in "void *data, proto". */ -#define __DO_TRACE (tp, proto, args, cond, prercu, postrcu) \ +#define __DO_TRACE (tp, proto, args, cond, rcucheck) \ do { \ struct tracepoint_func *it_func_ptr; \ void *it_func; \
@@ -136,7 +136,11 @@ extern void syscall_unregfunc(void); \if (!(cond)) \return ; \ - prercu; \ +if (rcucheck) { \ +if (WARN_ON_ONCE (rcu_irq_enter_disabled ())) \ +return ; \ +rcu_irq_enter_irqson (); \ + } \rcu_read_lock_sched_notrace (); \ it_func_ptr =rcu_dereference_sched ((tp)->funcs); \if (it_func_ptr) { \
@@ -147,20 +151,19 @@ extern void syscall_unregfunc(void); }while ((++it_func_ptr)->func); \ } \rcu_read_unlock_sched_notrace (); \ - postrcu; \ +if (rcucheck) \ +rcu_irq_exit_irqson (); \ } while (0)#ifndef MODULE -#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ +#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ static inline void trace_##name##_rcuidle(proto) \ { \ if (static_key_false(&__tracepoint_##name.key)) \ __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ - TP_CONDITION(cond), \ - rcu_irq_enter_irqson(), \ - rcu_irq_exit_irqson()); \ + TP_CONDITION(cond),1); \ }#else #define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args)
@@ -186,7 +189,7 @@ extern void syscall_unregfunc(void); __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ - TP_CONDITION(cond),,); \ + TP_CONDITION(cond),0); \ if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { \ rcu_read_lock_sched_notrace(); \ rcu_dereference_sched(__tracepoint_##name.funcs);\
Signature · 1 line
2.9.3
Steven Rostedt
maintainerRe: [PATCH 7/5 v4] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
include/linux/tracepoint.h extern void syscall_unregfunc(void); · line 166
Quoted 13 lines · On Fri, 7 Apr 2017 15:41:17 -0400 Steven Rostedt <[email protected]> wrote:
#ifndef MODULE
include/linux/tracepoint.h +2
-4-#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ +#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ static inline void trace_##name##_rcuidle(proto) \ { \ if (static_key_false(&__tracepoint_##name.key)) \ __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ - TP_CONDITION(cond), \ - rcu_irq_enter_irqson(), \ - rcu_irq_exit_irqson()); \ + TP_CONDITION(cond),1); \
I'm going to update this patch to add a space before the 1.
include/linux/tracepoint.h extern void syscall_unregfunc(void); · line 192
Quoted 9 lines
include/linux/tracepoint.h +1
-1}#else #define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args)@@ -186,7 +189,7 @@ extern void syscall_unregfunc(void); __DO_TRACE(&__tracepoint_##name, \TP_PROTO (data_proto), \TP_ARGS (data_args), \ - TP_CONDITION(cond),,); \ +TP_CONDITION (cond),0 ); \
And before the 0.
-- Steve
Quoted 3 lines
include/linux/tracepoint.h +0
-0if (IS_ENABLED (CONFIG_LOCKDEP ) && (cond)) { \rcu_read_lock_sched_notrace (); \ rcu_dereference_sched(__tracepoint_##name.funcs);\
Re: [PATCH 7/5 v4] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
Quoted 19 lines · ----- On Apr 7, 2017, at 3:43 PM, rostedt [email protected] wrote:
include/linux/tracepoint.h extern void syscall_unregfunc(void); · line 166
Quoted 13 lines · On Fri, 7 Apr 2017 15:41:17 -0400 Steven Rostedt <[email protected]> wrote:
#ifndef MODULE
include/linux/tracepoint.h +2
-4-#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ +#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ static inline void trace_##name##_rcuidle(proto) \ { \ if (static_key_false(&__tracepoint_##name.key)) \ __DO_TRACE(&__tracepoint_##name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ - TP_CONDITION(cond), \ - rcu_irq_enter_irqson(), \ - rcu_irq_exit_irqson()); \ + TP_CONDITION(cond),1); \I'm going to update this patch to add a space before the 1.
Quoted 11 lines
include/linux/tracepoint.h extern void syscall_unregfunc(void); · line 192
Quoted 9 lines
include/linux/tracepoint.h +1
-1}#else #define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args)@@ -186,7 +189,7 @@ extern void syscall_unregfunc(void); __DO_TRACE(&__tracepoint_##name, \TP_PROTO (data_proto), \TP_ARGS (data_args), \ - TP_CONDITION(cond),,); \ +TP_CONDITION (cond),0 ); \And before the 0.
With those cosmetic changes, I'm ok with it.
Thanks!
Mathieu
Quoted 6 lines
-- Steve
Quoted 3 lines
include/linux/tracepoint.h +0
-0if (IS_ENABLED (CONFIG_LOCKDEP ) && (cond)) { \rcu_read_lock_sched_notrace (); \ rcu_dereference_sched(__tracepoint_##name.funcs);\
Signature · 3 lines
Mathieu Desnoyers EfficiOS Inc. http://www.efficios.com
Steven Rostedt
maintainerRe: [PATCH 7/5] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
In reply to Mathieu DesnoyersQuoted 5 lines
Quoted 2 lines
use
rcu_irq_enter()it must make sure that it is functionable. A checkI don't think functionable is the word you are looking for here. Perhaps "must make sure that it can be invoked" ?
Well, rcu_irq_enter() doesn't function in that location. And it's a change log, not in the code. I think it gets the point across enough.
-- Steve
Steven Rostedt
maintainer[PATCH 7/5 v2] tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
In reply to Mathieu DesnoyersFrom: "Steven Rostedt (VMware)" <[email protected]>
Stack tracing discovered that there's a small location inside the RCU infrastructure where calling rcu_irq_enter() does not work. As trace events use rcu_irq_enter() it must make sure that it is functionable. A check against rcu_irq_enter_disabled() is added with a WARN_ON_ONCE() as no trace event should ever be used in that part of RCU. If the warning is triggered, then the trace event is ignored.
---
Mathieu, is this better? (yeah, i left in functionable, because I like that word ;-)
include/linux/tracepoint.h | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
include/linux/tracepoint.h +7 -2
@@ -151,7 +151,12 @@ extern void syscall_unregfunc(void); } while (0)#ifndef MODULE -#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ +#define TRACE_RCU_IRQ_ENTER_CHECK \ + if (WARN_ON_ONCE(rcu_irq_enter_disabled())) \ + return; \ + rcu_irq_enter_irqson() + +#define __DECLARE_TRACE_RCU (name, proto, args, cond, data_proto, data_args) \ static inline void trace_##name##_rcuidle(proto) \ { \ if (static_key_false(&__tracepoint_##name.key)) \
@@ -159,7 +164,7 @@ extern void syscall_unregfunc(void);TP_PROTO (data_proto), \TP_ARGS (data_args), \TP_CONDITION (cond), \ -rcu_irq_enter_irqson (), \ +PARAMS (TRACE_RCU_IRQ_ENTER_CHECK ), \ rcu_irq_exit_irqson()); \ }#else
Signature · 1 line
2.9.3