Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpuidle: lib/bug: Disable rcu_is_watching() during WARN/BUG

In order to avoid WARN/BUG from generating nested or even recursive
warnings, force rcu_is_watching() true during
WARN/lockdep_rcu_suspicious().

Notably things like unwinding the stack can trigger rcu_dereference()
warnings, which then triggers more unwinding which then triggers more
warnings etc..

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20230126151323.408156109@infradead.org

authored by

Peter Zijlstra and committed by
Ingo Molnar
5a5d7e9b 393e2ea3

+49 -1
+27
include/linux/context_tracking.h
··· 130 130 return arch_atomic_add_return(incby, this_cpu_ptr(&context_tracking.state)); 131 131 } 132 132 133 + static __always_inline bool warn_rcu_enter(void) 134 + { 135 + bool ret = false; 136 + 137 + /* 138 + * Horrible hack to shut up recursive RCU isn't watching fail since 139 + * lots of the actual reporting also relies on RCU. 140 + */ 141 + preempt_disable_notrace(); 142 + if (rcu_dynticks_curr_cpu_in_eqs()) { 143 + ret = true; 144 + ct_state_inc(RCU_DYNTICKS_IDX); 145 + } 146 + 147 + return ret; 148 + } 149 + 150 + static __always_inline void warn_rcu_exit(bool rcu) 151 + { 152 + if (rcu) 153 + ct_state_inc(RCU_DYNTICKS_IDX); 154 + preempt_enable_notrace(); 155 + } 156 + 133 157 #else 134 158 static inline void ct_idle_enter(void) { } 135 159 static inline void ct_idle_exit(void) { } 160 + 161 + static __always_inline bool warn_rcu_enter(void) { return false; } 162 + static __always_inline void warn_rcu_exit(bool rcu) { } 136 163 #endif /* !CONFIG_CONTEXT_TRACKING_IDLE */ 137 164 138 165 #endif
+3
kernel/locking/lockdep.c
··· 55 55 #include <linux/rcupdate.h> 56 56 #include <linux/kprobes.h> 57 57 #include <linux/lockdep.h> 58 + #include <linux/context_tracking.h> 58 59 59 60 #include <asm/sections.h> 60 61 ··· 6556 6555 { 6557 6556 struct task_struct *curr = current; 6558 6557 int dl = READ_ONCE(debug_locks); 6558 + bool rcu = warn_rcu_enter(); 6559 6559 6560 6560 /* Note: the following can be executed concurrently, so be careful. */ 6561 6561 pr_warn("\n"); ··· 6597 6595 lockdep_print_held_locks(curr); 6598 6596 pr_warn("\nstack backtrace:\n"); 6599 6597 dump_stack(); 6598 + warn_rcu_exit(rcu); 6600 6599 } 6601 6600 EXPORT_SYMBOL_GPL(lockdep_rcu_suspicious);
+5
kernel/panic.c
··· 34 34 #include <linux/ratelimit.h> 35 35 #include <linux/debugfs.h> 36 36 #include <linux/sysfs.h> 37 + #include <linux/context_tracking.h> 37 38 #include <trace/events/error_report.h> 38 39 #include <asm/sections.h> 39 40 ··· 680 679 void warn_slowpath_fmt(const char *file, int line, unsigned taint, 681 680 const char *fmt, ...) 682 681 { 682 + bool rcu = warn_rcu_enter(); 683 683 struct warn_args args; 684 684 685 685 pr_warn(CUT_HERE); ··· 695 693 va_start(args.args, fmt); 696 694 __warn(file, line, __builtin_return_address(0), taint, NULL, &args); 697 695 va_end(args.args); 696 + warn_rcu_exit(rcu); 698 697 } 699 698 EXPORT_SYMBOL(warn_slowpath_fmt); 700 699 #else 701 700 void __warn_printk(const char *fmt, ...) 702 701 { 702 + bool rcu = warn_rcu_enter(); 703 703 va_list args; 704 704 705 705 pr_warn(CUT_HERE); ··· 709 705 va_start(args, fmt); 710 706 vprintk(fmt, args); 711 707 va_end(args); 708 + warn_rcu_exit(rcu); 712 709 } 713 710 EXPORT_SYMBOL(__warn_printk); 714 711 #endif
+14 -1
lib/bug.c
··· 47 47 #include <linux/sched.h> 48 48 #include <linux/rculist.h> 49 49 #include <linux/ftrace.h> 50 + #include <linux/context_tracking.h> 50 51 51 52 extern struct bug_entry __start___bug_table[], __stop___bug_table[]; 52 53 ··· 154 153 return module_find_bug(bugaddr); 155 154 } 156 155 157 - enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) 156 + static enum bug_trap_type __report_bug(unsigned long bugaddr, struct pt_regs *regs) 158 157 { 159 158 struct bug_entry *bug; 160 159 const char *file; ··· 208 207 (void *)bugaddr); 209 208 210 209 return BUG_TRAP_TYPE_BUG; 210 + } 211 + 212 + enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) 213 + { 214 + enum bug_trap_type ret; 215 + bool rcu = false; 216 + 217 + rcu = warn_rcu_enter(); 218 + ret = __report_bug(bugaddr, regs); 219 + warn_rcu_exit(rcu); 220 + 221 + return ret; 211 222 } 212 223 213 224 static void clear_once_table(struct bug_entry *start, struct bug_entry *end)