On Wed, Jan 25, 2023 at 11:47:44AM +0100, Peter Zijlstra wrote: > On Tue, Jan 24, 2023 at 05:12:14PM +0000, Mark Rutland wrote: > > On Tue, Jan 24, 2023 at 03:44:35PM +0100, Peter Zijlstra wrote: > > > On Mon, Jan 23, 2023 at 05:07:53PM -0500, Steven Rostedt wrote: > > > > > > > Actually, perhaps we can just add this, and all you need to do is create > > > > and set CONFIG_NO_RCU_TRACING (or some other name). > > > > > > Elsewhere I've used CONFIG_ARCH_WANTS_NO_INSTR for this. > > > > Yes please; if we use CONFIG_ARCH_WANTS_NO_INSTR then arm64 will get this "for > > free" once we add the missing checks (which I assume we need) in our ftrace_prepare_return(). > > > > > Anyway, I took it for a spin and it .... doesn't seems to do the job. > > > > > > With my patch the first splat is > > > > > > "RCU not on for: cpuidle_poll_time+0x0/0x70" > > > > > > While with yours I seems to get the endless: > > > > > > "WARNING: suspicious RCU usage" > > > > > > thing. Let me see if I can figure out where it goes side-ways. > > > > Hmmm... for WARN_ONCE() don't we need to wake RCU first also? I thought we > > needed that at least for the printk machinery? > > OK, the below seems to work nice for me -- although I'm still on a > hacked up printk, but the recursive RCU not watching fail seems to be > tamed. > > Ofc. Paul might have an opinion on this glorious bodge ;-) For some definition of the word "glorious", to be sure. ;-) Am I correct that you have two things happening here? (1) Preventing trace recursion and (2) forcing RCU to pay attention when needed. I cannot resist pointing out that you have re-invented RCU_NONIDLE(), though avoiding much of the overhead when not needed. ;-) I would have objections if this ever leaks out onto a non-error code path. There are things that need doing when RCU starts and stops watching, and this approach omits those things. Which again is OK in this case, where this code is only ever executed when something is already broken, but definitely *not* OK when things are not already broken. Thanx, Paul > --- > > diff --git a/include/linux/trace_recursion.h b/include/linux/trace_recursion.h > index c303f7a114e9..d48cd92d2364 100644 > --- a/include/linux/trace_recursion.h > +++ b/include/linux/trace_recursion.h > @@ -135,6 +135,21 @@ extern void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip); > # define do_ftrace_record_recursion(ip, pip) do { } while (0) > #endif > > +#ifdef CONFIG_ARCH_WANTS_NO_INSTR > +# define trace_warn_on_no_rcu(ip) \ > + ({ \ > + bool __ret = !rcu_is_watching(); \ > + if (__ret && !trace_recursion_test(TRACE_RECORD_RECURSION_BIT)) { \ > + trace_recursion_set(TRACE_RECORD_RECURSION_BIT); \ > + WARN_ONCE(true, "RCU not on for: %pS\n", (void *)ip); \ > + trace_recursion_clear(TRACE_RECORD_RECURSION_BIT); \ > + } \ > + __ret; \ > + }) > +#else > +# define trace_warn_on_no_rcu(ip) false > +#endif > + > /* > * Preemption is promised to be disabled when return bit >= 0. > */ > @@ -144,6 +159,9 @@ static __always_inline int trace_test_and_set_recursion(unsigned long ip, unsign > unsigned int val = READ_ONCE(current->trace_recursion); > int bit; > > + if (trace_warn_on_no_rcu(ip)) > + return -1; > + > bit = trace_get_context_bit() + start; > if (unlikely(val & (1 << bit))) { > /* > diff --git a/lib/bug.c b/lib/bug.c > index c223a2575b72..0a10643ea168 100644 > --- a/lib/bug.c > +++ b/lib/bug.c > @@ -47,6 +47,7 @@ > #include <linux/sched.h> > #include <linux/rculist.h> > #include <linux/ftrace.h> > +#include <linux/context_tracking.h> > > extern struct bug_entry __start___bug_table[], __stop___bug_table[]; > > @@ -153,7 +154,7 @@ struct bug_entry *find_bug(unsigned long bugaddr) > return module_find_bug(bugaddr); > } > > -enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) > +static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *regs) > { > struct bug_entry *bug; > const char *file; > @@ -209,6 +210,30 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) > return BUG_TRAP_TYPE_BUG; > } > > +enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) > +{ > + enum bug_trap_type ret; > + bool rcu = false; > + > +#ifdef CONFIG_CONTEXT_TRACKING_IDLE > + /* > + * Horrible hack to shut up recursive RCU isn't watching fail since > + * lots of the actual reporting also relies on RCU. > + */ > + if (!rcu_is_watching()) { > + rcu = true; > + ct_state_inc(RCU_DYNTICKS_IDX); > + } > +#endif > + > + ret = __report_bug(bugaddr, regs); > + > + if (rcu) > + ct_state_inc(RCU_DYNTICKS_IDX); > + > + return ret; > +} > + > static void clear_once_table(struct bug_entry *start, struct bug_entry *end) > { > struct bug_entry *bug;