On Sat, Feb 25, 2017 at 10:18:23AM +0100, Ingo Molnar wrote: > > Sadly this only works for WARN_ON_ONCE(), since the others have > > printk() statements prior to triggering the trap. > > Which one is problematic to convert, WARN_ONCE()?
Yes, WARN_ONCE(), all the ones that have printf fmt crud in. If we want report_bug() to do the _ONCE thing, we also need that to do the printk(). I tried the below hackery (beware eye and brain damage ahead) which actually compiles, but generates horrific junk -- far larger than without. The __builtin_va_*() crud was really not meant for things like this. --- --- a/arch/x86/include/asm/bug.h +++ b/arch/x86/include/asm/bug.h @@ -66,6 +66,11 @@ do { \ #define __WARN_FLAGS(flags) _BUG_FLAGS(ASM_UD0, BUGFLAG_WARNING|(flags)) +#define __WARN_ARGS_FLAGS(args, flags) do { \ + asm volatile ("" : : "a" (args)); \ + _BUG_FLAGS(ASM_UD0, BUGFLAG_WARNING|(flags)); \ +} while (0) + #include <asm-generic/bug.h> #endif /* _ASM_X86_BUG_H */ --- a/include/asm-generic/bug.h +++ b/include/asm-generic/bug.h @@ -7,6 +7,7 @@ #define BUGFLAG_WARNING (1 << 0) #define BUGFLAG_ONCE (1 << 1) #define BUGFLAG_DONE (1 << 2) +#define BUGFLAG_ARGS (1 << 3) #define BUGFLAG_TAINT(taint) (BUGFLAG_WARNING | ((taint) << 8)) #define BUG_GET_TAINT(bug) ((bug)->flags >> 8) #endif @@ -67,8 +68,28 @@ struct bug_entry { __WARN_ONCE_TAINT(TAINT_WARN); \ unlikely(__ret_warn_on); \ }) + +#ifdef __WARN_ARGS_FLAGS +static inline void __va_hack(va_list *ap, ...) +{ + va_start(*ap, ap); +} + +#define __WARN_printf(arg...) do { \ + va_list __ap; \ + __va_hack(&__ap, arg); \ + __WARN_ARGS_FLAGS(&__ap, BUGFLAG_ARGS|BUGFLAG_TAINT(TAINT_WARN)); \ +} while (0) + +#define __WARN_printf_taint(taint, arg...) do { \ + va_list __ap; \ + __va_hack(&__ap, arg); \ + __WARN_ARGS_FLAGS(&__ap, BUGFLAG_ARGS|BUGFLAG_TAINT(taint)); \ +} while (0) #endif +#endif /* __WARN_FLAGS */ + /* * WARN(), WARN_ON(), WARN_ON_ONCE, and so on can be used to report * significant issues that need prompt attention if they should ever @@ -90,10 +111,12 @@ extern void warn_slowpath_null(const cha warn_slowpath_fmt_taint(__FILE__, __LINE__, taint, arg) #else #define __WARN() __WARN_TAINT(TAINT_WARN) +#ifndef __WARN_ARGS_FLAGS #define __WARN_printf(arg...) do { printk(arg); __WARN(); } while (0) #define __WARN_printf_taint(taint, arg...) \ do { printk(arg); __WARN_TAINT(taint); } while (0) #endif +#endif /* used internally by panic.c */ struct warn_args; --- a/lib/bug.c +++ b/lib/bug.c @@ -143,7 +143,8 @@ enum bug_trap_type report_bug(unsigned l { struct bug_entry *bug; const char *file; - unsigned line, warning, once, done; + unsigned line, warning, once, done, args; + va_list *ap; if (!is_valid_bugaddr(bugaddr)) return BUG_TRAP_TYPE_NONE; @@ -166,6 +167,7 @@ enum bug_trap_type report_bug(unsigned l warning = (bug->flags & BUGFLAG_WARNING) != 0; once = (bug->flags & BUGFLAG_ONCE) != 0; done = (bug->flags & BUGFLAG_DONE) != 0; + args = (bug->flags & BUGFLAG_ARGS) != 0; if (warning && once) { if (done) @@ -176,9 +178,16 @@ enum bug_trap_type report_bug(unsigned l */ bug->flags |= BUGFLAG_DONE; } + + if (args) + ap = (va_list *)regs->ax; } if (warning) { + if (args) { + const char *fmt = va_arg(*ap, const char *); + vprintk(fmt, *ap); + } /* this is a WARN_ON rather than BUG/BUG_ON */ __warn(file, line, (void *)bugaddr, BUG_GET_TAINT(bug), regs, NULL);