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

Merge branch 'warn-exception'

Heiko Carstens says:

====================

v2:
- Use generic WARN() implementation in case of gcc-8, which avoids the need
to raise the minimum gcc version to gcc-9 for (only) s390

- Add couple of additional ifdef guards to avoid compile errors and
warnings

v1 ([1]):
Use the generic infrastructure introduced by Peter Zijlstra [2] to implement
an exception based WARN() and WARN_ONCE() similar to x86.

Due to some compiler oddities on s390 this requires to raise the minimum gcc
version to 9. Maybe there are ways to avoid this, but I failed to find a
working solution. Details are in the patch descriptions.

Just posting this now to also get some compile bot testing, since I'm afraid
there might be some compiler version / config option around where even this
new approach breaks.

Peter, since you were wondering: your generic infrastructure pieces work very
nice. Looking at the x86 and s390 implementation: it might be possible to make
things even more generic since both __WARN_printf(), and WARN_ONCE() are
identical; it looks like only __WARN_print_arg() needs to be provided.

[1] https://lore.kernel.org/all/20251209121701.1856271-1-hca@linux.ibm.com/
[2] https://lore.kernel.org/all/20251110114633.202485143@infradead.org/

====================

Signed-off-by: Heiko Carstens <hca@linux.ibm.com>

+171 -43
+6
arch/s390/Kconfig
··· 69 69 Clang versions before 19.1.0 do not support A, 70 70 O, and R inline assembly format flags. 71 71 72 + config CC_HAS_ASM_IMMEDIATE_STRINGS 73 + def_bool !(CC_IS_GCC && GCC_VERSION < 90000) 74 + help 75 + GCC versions before 9.0.0 cannot handle strings as immediate 76 + input operands in inline assemblies. 77 + 72 78 config CC_HAS_STACKPROTECTOR_GLOBAL 73 79 def_bool $(cc-option, -mstack-protector-guard=global -mstack-protector-guard-record) 74 80
+1
arch/s390/include/asm/asm-prototypes.h
··· 3 3 4 4 #include <linux/kvm_host.h> 5 5 #include <linux/ftrace.h> 6 + #include <asm/bug.h> 6 7 #include <asm/fpu.h> 7 8 #include <asm/nospec-branch.h> 8 9 #include <asm-generic/asm-prototypes.h>
+106 -39
arch/s390/include/asm/bug.h
··· 2 2 #ifndef _ASM_S390_BUG_H 3 3 #define _ASM_S390_BUG_H 4 4 5 - #include <linux/stringify.h> 5 + #include <linux/compiler.h> 6 + #include <linux/const.h> 6 7 7 - #ifdef CONFIG_BUG 8 + #define MONCODE_BUG _AC(0, U) 9 + #define MONCODE_BUG_ARG _AC(1, U) 8 10 9 - #ifndef CONFIG_DEBUG_BUGVERBOSE 10 - #define _BUGVERBOSE_LOCATION(file, line) 11 + #ifndef __ASSEMBLER__ 12 + #if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) 13 + 14 + #ifdef CONFIG_DEBUG_BUGVERBOSE 15 + #define __BUG_ENTRY_VERBOSE(format, file, line) \ 16 + " .long " format " - . # bug_entry::format\n" \ 17 + " .long " file " - . # bug_entry::file\n" \ 18 + " .short " line " # bug_entry::line\n" 11 19 #else 12 - #define __BUGVERBOSE_LOCATION(file, line) \ 13 - .pushsection .rodata.str, "aMS", @progbits, 1; \ 14 - .align 2; \ 15 - 10002: .ascii file "\0"; \ 16 - .popsection; \ 17 - \ 18 - .long 10002b - .; \ 19 - .short line; 20 - #define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line) 20 + #define __BUG_ENTRY_VERBOSE(format, file, line) 21 21 #endif 22 22 23 - #ifndef CONFIG_GENERIC_BUG 24 - #define __BUG_ENTRY(cond_str, flags) 23 + #ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED 24 + #define WARN_CONDITION_STR(cond_str) cond_str 25 25 #else 26 - #define __BUG_ENTRY(cond_str, flags) \ 27 - .pushsection __bug_table, "aw"; \ 28 - .align 4; \ 29 - 10000: .long 10001f - .; \ 30 - _BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \ 31 - .short flags; \ 32 - .popsection; \ 33 - 10001: 26 + #define WARN_CONDITION_STR(cond_str) "" 34 27 #endif 35 28 36 - #define ASM_BUG_FLAGS(cond_str, flags) \ 37 - __BUG_ENTRY(cond_str, flags) \ 38 - mc 0,0 29 + #define __BUG_ENTRY(format, file, line, flags, size) \ 30 + " .section __bug_table,\"aw\"\n" \ 31 + "1: .long 0b - . # bug_entry::bug_addr\n" \ 32 + __BUG_ENTRY_VERBOSE(format, file, line) \ 33 + " .short "flags" # bug_entry::flags\n" \ 34 + " .org 1b+"size"\n" \ 35 + " .previous" 39 36 40 - #define ASM_BUG() ASM_BUG_FLAGS("", 0) 41 - 42 - #define __BUG_FLAGS(cond_str, flags) \ 43 - asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags))); 44 - 45 - #define __WARN_FLAGS(cond_str, flags) \ 46 - do { \ 47 - __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)); \ 37 + #define __BUG_ASM(cond_str, flags) \ 38 + do { \ 39 + asm_inline volatile("\n" \ 40 + "0: mc %[monc](%%r0),0\n" \ 41 + __BUG_ENTRY("%[frmt]", "%[file]", "%[line]", \ 42 + "%[flgs]", "%[size]") \ 43 + : \ 44 + : [monc] "i" (MONCODE_BUG), \ 45 + [frmt] "i" (WARN_CONDITION_STR(cond_str)), \ 46 + [file] "i" (__FILE__), \ 47 + [line] "i" (__LINE__), \ 48 + [flgs] "i" (flags), \ 49 + [size] "i" (sizeof(struct bug_entry))); \ 48 50 } while (0) 49 51 50 - #define BUG() \ 51 - do { \ 52 - __BUG_FLAGS("", 0); \ 53 - unreachable(); \ 52 + #define BUG() \ 53 + do { \ 54 + __BUG_ASM("", 0); \ 55 + unreachable(); \ 54 56 } while (0) 57 + 58 + #define __WARN_FLAGS(cond_str, flags) \ 59 + do { \ 60 + __BUG_ASM(cond_str, BUGFLAG_WARNING | (flags)); \ 61 + } while (0) 62 + 63 + #define __WARN_bug_entry(flags, format) \ 64 + ({ \ 65 + struct bug_entry *bug; \ 66 + \ 67 + asm_inline volatile("\n" \ 68 + "0: larl %[bug],1f\n" \ 69 + __BUG_ENTRY("%[frmt]", "%[file]", "%[line]", \ 70 + "%[flgs]", "%[size]") \ 71 + : [bug] "=d" (bug) \ 72 + : [frmt] "i" (format), \ 73 + [file] "i" (__FILE__), \ 74 + [line] "i" (__LINE__), \ 75 + [flgs] "i" (flags), \ 76 + [size] "i" (sizeof(struct bug_entry))); \ 77 + bug; \ 78 + }) 79 + 80 + /* 81 + * Variable Argument List (va_list) as defined in ELF Application 82 + * Binary Interface s390x Supplement documentation. 83 + */ 84 + struct arch_va_list { 85 + long __gpr; 86 + long __fpr; 87 + void *__overflow_arg_area; 88 + void *__reg_save_area; 89 + }; 90 + 91 + struct bug_entry; 92 + struct pt_regs; 93 + 94 + void *__warn_args(struct arch_va_list *args, struct pt_regs *regs); 95 + void __WARN_trap(struct bug_entry *bug, ...); 96 + 97 + #define __WARN_print_arg(flags, format, arg...) \ 98 + do { \ 99 + int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS; \ 100 + \ 101 + __WARN_trap(__WARN_bug_entry(__flags, format), ## arg); \ 102 + /* prevent tail-call optimization */ \ 103 + asm(""); \ 104 + } while (0) 105 + 106 + #define __WARN_printf(taint, fmt, arg...) \ 107 + __WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg) 108 + 109 + #define WARN_ONCE(cond, format, arg...) \ 110 + ({ \ 111 + int __ret_warn_on = !!(cond); \ 112 + \ 113 + if (unlikely(__ret_warn_on)) { \ 114 + __WARN_print_arg(BUGFLAG_ONCE|BUGFLAG_TAINT(TAINT_WARN),\ 115 + format, ## arg); \ 116 + } \ 117 + __ret_warn_on; \ 118 + }) 55 119 56 120 #define HAVE_ARCH_BUG 121 + #define HAVE_ARCH_BUG_FORMAT 122 + #define HAVE_ARCH_BUG_FORMAT_ARGS 57 123 58 - #endif /* CONFIG_BUG */ 124 + #endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ 125 + #endif /* __ASSEMBLER__ */ 59 126 60 127 #include <asm-generic/bug.h> 61 128
+4 -1
arch/s390/include/asm/ptrace.h
··· 120 120 unsigned long gprs[NUM_GPRS]; 121 121 }; 122 122 }; 123 - unsigned long orig_gpr2; 123 + union { 124 + unsigned long orig_gpr2; 125 + unsigned long monitor_code; 126 + }; 124 127 union { 125 128 struct { 126 129 unsigned int int_code;
+11
arch/s390/kernel/entry.S
··· 23 23 #include <asm/unistd.h> 24 24 #include <asm/page.h> 25 25 #include <asm/sigp.h> 26 + #include <asm/bug.h> 26 27 #include <asm/irq.h> 27 28 #include <asm/fpu-insn.h> 28 29 #include <asm/setup.h> ··· 173 172 lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task 174 173 BR_EX %r14 175 174 SYM_FUNC_END(__switch_to_asm) 175 + 176 + #if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) 177 + 178 + SYM_FUNC_START(__WARN_trap) 179 + mc MONCODE_BUG_ARG(%r0),0 180 + BR_EX %r14 181 + SYM_FUNC_END(__WARN_trap) 182 + EXPORT_SYMBOL(__WARN_trap) 183 + 184 + #endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ 176 185 177 186 #if IS_ENABLED(CONFIG_KVM) 178 187 /*
+43 -3
arch/s390/kernel/traps.c
··· 23 23 #include <linux/cpu.h> 24 24 #include <linux/entry-common.h> 25 25 #include <linux/kmsan.h> 26 + #include <linux/bug.h> 26 27 #include <asm/asm-extable.h> 27 28 #include <asm/irqflags.h> 28 29 #include <asm/ptrace.h> ··· 221 220 do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event"); 222 221 } 223 222 223 + #if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) 224 + 225 + void *__warn_args(struct arch_va_list *args, struct pt_regs *regs) 226 + { 227 + struct stack_frame *stack_frame; 228 + 229 + /* 230 + * Generate va_list from pt_regs. See ELF Application Binary Interface 231 + * s390x Supplement documentation for details. 232 + * 233 + * - __overflow_arg_area needs to point to the parameter area, which 234 + * is right above the standard stack frame (160 bytes) 235 + * 236 + * - __reg_save_area needs to point to a register save area where 237 + * general registers (%r2 - %r6) can be found at offset 16. Which 238 + * means that the gprs save area of pt_regs can be used 239 + * 240 + * - __gpr must be set to one, since the first parameter has been 241 + * processed (pointer to bug_entry) 242 + */ 243 + stack_frame = (struct stack_frame *)regs->gprs[15]; 244 + args->__overflow_arg_area = stack_frame + 1; 245 + args->__reg_save_area = regs->gprs; 246 + args->__gpr = 1; 247 + return args; 248 + } 249 + 250 + #endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */ 251 + 224 252 static void monitor_event_exception(struct pt_regs *regs) 225 253 { 254 + enum bug_trap_type btt; 255 + 226 256 if (user_mode(regs)) 227 257 return; 228 - switch (report_bug(regs->psw.addr - (regs->int_code >> 16), regs)) { 258 + if (regs->monitor_code == MONCODE_BUG_ARG) { 259 + regs->psw.addr = regs->gprs[14]; 260 + btt = report_bug_entry((struct bug_entry *)regs->gprs[2], regs); 261 + } else { 262 + btt = report_bug(regs->psw.addr - (regs->int_code >> 16), regs); 263 + } 264 + switch (btt) { 229 265 case BUG_TRAP_TYPE_NONE: 230 266 fixup_exception(regs); 231 267 break; ··· 296 258 if (!IS_ENABLED(CONFIG_BUG)) 297 259 return; 298 260 asm_inline volatile( 299 - " mc 0,0\n" 261 + " mc %[monc](%%r0),0\n" 300 262 "0: lhi %[val],0\n" 301 263 "1:\n" 302 264 EX_TABLE(0b, 1b) 303 - : [val] "+d" (val)); 265 + : [val] "+d" (val) 266 + : [monc] "i" (MONCODE_BUG)); 304 267 if (!val) 305 268 panic("Monitor call doesn't work!\n"); 306 269 } ··· 336 297 teid.val = lc->trans_exc_code; 337 298 regs->int_code = lc->pgm_int_code; 338 299 regs->int_parm_long = teid.val; 300 + regs->monitor_code = lc->monitor_code; 339 301 /* 340 302 * In case of a guest fault, short-circuit the fault handler and return. 341 303 * This way the sie64a() function will return 0; fault address and