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

arch/tile: support signal "exception-trace" hook

This change adds support for /proc/sys/debug/exception-trace to tile.
Like x86 and sparc, by default it is set to "1", generating a one-line
printk whenever a user process crashes. By setting it to "2", we get
a much more complete userspace diagnostic at crash time, including
a user-space backtrace, register dump, and memory dump around the
address of the crash.

Some vestiges of the Tilera-internal version of this support are
removed with this patch (the show_crashinfo variable and the
arch_coredump_signal function). We retain a "crashinfo" boot parameter
which allows you to set the boot-time value of exception-trace.

Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>

+151 -23
-7
arch/tile/include/asm/processor.h
··· 257 257 barrier(); 258 258 } 259 259 260 - struct siginfo; 261 - extern void arch_coredump_signal(struct siginfo *, struct pt_regs *); 262 - #define arch_coredump_signal arch_coredump_signal 263 - 264 260 /* Info on this processor (see fs/proc/cpuinfo.c) */ 265 261 struct seq_operations; 266 262 extern const struct seq_operations cpuinfo_op; ··· 266 270 267 271 /* Data on which physical memory controller corresponds to which NUMA node. */ 268 272 extern int node_controller[]; 269 - 270 - /* Do we dump information to the console when a user application crashes? */ 271 - extern int show_crashinfo; 272 273 273 274 #if CHIP_HAS_CBOX_HOME_MAP() 274 275 /* Does the heap allocator return hash-for-home pages by default? */
+4
arch/tile/include/asm/signal.h
··· 28 28 int restore_sigcontext(struct pt_regs *, struct sigcontext __user *); 29 29 int setup_sigcontext(struct sigcontext __user *, struct pt_regs *); 30 30 void do_signal(struct pt_regs *regs); 31 + void signal_fault(const char *type, struct pt_regs *, 32 + void __user *frame, int sig); 33 + void trace_unhandled_signal(const char *type, struct pt_regs *regs, 34 + unsigned long address, int signo); 31 35 #endif 32 36 33 37 #endif /* _ASM_TILE_SIGNAL_H */
+2 -2
arch/tile/kernel/compat_signal.c
··· 317 317 return 0; 318 318 319 319 badframe: 320 - force_sig(SIGSEGV, current); 320 + signal_fault("bad sigreturn frame", regs, frame, 0); 321 321 return 0; 322 322 } 323 323 ··· 431 431 return 0; 432 432 433 433 give_sigsegv: 434 - force_sigsegv(sig, current); 434 + signal_fault("bad setup frame", regs, frame, sig); 435 435 return -EFAULT; 436 436 }
+124 -4
arch/tile/kernel/signal.c
··· 39 39 40 40 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) 41 41 42 - 43 42 SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss, 44 43 stack_t __user *, uoss, struct pt_regs *, regs) 45 44 { ··· 77 78 return err; 78 79 } 79 80 81 + void signal_fault(const char *type, struct pt_regs *regs, 82 + void __user *frame, int sig) 83 + { 84 + trace_unhandled_signal(type, regs, (unsigned long)frame, SIGSEGV); 85 + force_sigsegv(sig, current); 86 + } 87 + 80 88 /* The assembly shim for this function arranges to ignore the return value. */ 81 89 SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs) 82 90 { ··· 111 105 return 0; 112 106 113 107 badframe: 114 - force_sig(SIGSEGV, current); 108 + signal_fault("bad sigreturn frame", regs, frame, 0); 115 109 return 0; 116 110 } 117 111 ··· 237 231 return 0; 238 232 239 233 give_sigsegv: 240 - force_sigsegv(sig, current); 234 + signal_fault("bad setup frame", regs, frame, sig); 241 235 return -EFAULT; 242 236 } 243 237 ··· 250 244 struct pt_regs *regs) 251 245 { 252 246 int ret; 253 - 254 247 255 248 /* Are we from a system call? */ 256 249 if (regs->faultnum == INT_SWINT_1) { ··· 367 362 done: 368 363 /* Avoid double syscall restart if there are nested signals. */ 369 364 regs->faultnum = INT_SWINT_1_SIGRETURN; 365 + } 366 + 367 + int show_unhandled_signals = 1; 368 + 369 + static int __init crashinfo(char *str) 370 + { 371 + unsigned long val; 372 + const char *word; 373 + 374 + if (*str == '\0') 375 + val = 2; 376 + else if (*str != '=' || strict_strtoul(++str, 0, &val) != 0) 377 + return 0; 378 + show_unhandled_signals = val; 379 + switch (show_unhandled_signals) { 380 + case 0: 381 + word = "No"; 382 + break; 383 + case 1: 384 + word = "One-line"; 385 + break; 386 + default: 387 + word = "Detailed"; 388 + break; 389 + } 390 + pr_info("%s crash reports will be generated on the console\n", word); 391 + return 1; 392 + } 393 + __setup("crashinfo", crashinfo); 394 + 395 + static void dump_mem(void __user *address) 396 + { 397 + void __user *addr; 398 + enum { region_size = 256, bytes_per_line = 16 }; 399 + int i, j, k; 400 + int found_readable_mem = 0; 401 + 402 + pr_err("\n"); 403 + if (!access_ok(VERIFY_READ, address, 1)) { 404 + pr_err("Not dumping at address 0x%lx (kernel address)\n", 405 + (unsigned long)address); 406 + return; 407 + } 408 + 409 + addr = (void __user *) 410 + (((unsigned long)address & -bytes_per_line) - region_size/2); 411 + if (addr > address) 412 + addr = NULL; 413 + for (i = 0; i < region_size; 414 + addr += bytes_per_line, i += bytes_per_line) { 415 + unsigned char buf[bytes_per_line]; 416 + char line[100]; 417 + if (copy_from_user(buf, addr, bytes_per_line)) 418 + continue; 419 + if (!found_readable_mem) { 420 + pr_err("Dumping memory around address 0x%lx:\n", 421 + (unsigned long)address); 422 + found_readable_mem = 1; 423 + } 424 + j = sprintf(line, REGFMT":", (unsigned long)addr); 425 + for (k = 0; k < bytes_per_line; ++k) 426 + j += sprintf(&line[j], " %02x", buf[k]); 427 + pr_err("%s\n", line); 428 + } 429 + if (!found_readable_mem) 430 + pr_err("No readable memory around address 0x%lx\n", 431 + (unsigned long)address); 432 + } 433 + 434 + void trace_unhandled_signal(const char *type, struct pt_regs *regs, 435 + unsigned long address, int sig) 436 + { 437 + struct task_struct *tsk = current; 438 + 439 + if (show_unhandled_signals == 0) 440 + return; 441 + 442 + /* If the signal is handled, don't show it here. */ 443 + if (!is_global_init(tsk)) { 444 + void __user *handler = 445 + tsk->sighand->action[sig-1].sa.sa_handler; 446 + if (handler != SIG_IGN && handler != SIG_DFL) 447 + return; 448 + } 449 + 450 + /* Rate-limit the one-line output, not the detailed output. */ 451 + if (show_unhandled_signals <= 1 && !printk_ratelimit()) 452 + return; 453 + 454 + printk("%s%s[%d]: %s at %lx pc "REGFMT" signal %d", 455 + task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG, 456 + tsk->comm, task_pid_nr(tsk), type, address, regs->pc, sig); 457 + 458 + print_vma_addr(KERN_CONT " in ", regs->pc); 459 + 460 + printk(KERN_CONT "\n"); 461 + 462 + if (show_unhandled_signals > 1) { 463 + switch (sig) { 464 + case SIGILL: 465 + case SIGFPE: 466 + case SIGSEGV: 467 + case SIGBUS: 468 + pr_err("User crash: signal %d," 469 + " trap %ld, address 0x%lx\n", 470 + sig, regs->faultnum, address); 471 + show_regs(regs); 472 + dump_mem((void __user *)address); 473 + break; 474 + default: 475 + pr_err("User crash: signal %d, trap %ld\n", 476 + sig, regs->faultnum); 477 + break; 478 + } 479 + } 370 480 }
+4
arch/tile/kernel/single_step.c
··· 186 186 .si_code = SEGV_MAPERR, 187 187 .si_addr = addr 188 188 }; 189 + trace_unhandled_signal("segfault", regs, 190 + (unsigned long)addr, SIGSEGV); 189 191 force_sig_info(info.si_signo, &info, current); 190 192 return (tile_bundle_bits) 0; 191 193 } ··· 198 196 .si_code = BUS_ADRALN, 199 197 .si_addr = addr 200 198 }; 199 + trace_unhandled_signal("unaligned trap", regs, 200 + (unsigned long)addr, SIGBUS); 201 201 force_sig_info(info.si_signo, &info, current); 202 202 return (tile_bundle_bits) 0; 203 203 }
+1
arch/tile/kernel/traps.c
··· 308 308 info.si_addr = (void __user *)address; 309 309 if (signo == SIGILL) 310 310 info.si_trapno = fault_num; 311 + trace_unhandled_signal("trap", regs, address, signo); 311 312 force_sig_info(signo, &info, current); 312 313 } 313 314
+15 -9
arch/tile/mm/fault.c
··· 43 43 44 44 #include <arch/interrupts.h> 45 45 46 - static noinline void force_sig_info_fault(int si_signo, int si_code, 47 - unsigned long address, int fault_num, struct task_struct *tsk) 46 + static noinline void force_sig_info_fault(const char *type, int si_signo, 47 + int si_code, unsigned long address, 48 + int fault_num, 49 + struct task_struct *tsk, 50 + struct pt_regs *regs) 48 51 { 49 52 siginfo_t info; 50 53 ··· 62 59 info.si_code = si_code; 63 60 info.si_addr = (void __user *)address; 64 61 info.si_trapno = fault_num; 62 + trace_unhandled_signal(type, regs, address, si_signo); 65 63 force_sig_info(si_signo, &info, tsk); 66 64 } 67 65 ··· 75 71 struct pt_regs *, regs) 76 72 { 77 73 if (address >= PAGE_OFFSET) 78 - force_sig_info_fault(SIGSEGV, SEGV_MAPERR, address, 79 - INT_DTLB_MISS, current); 74 + force_sig_info_fault("atomic segfault", SIGSEGV, SEGV_MAPERR, 75 + address, INT_DTLB_MISS, current, regs); 80 76 else 81 - force_sig_info_fault(SIGBUS, BUS_ADRALN, address, 82 - INT_UNALIGN_DATA, current); 77 + force_sig_info_fault("atomic alignment fault", SIGBUS, 78 + BUS_ADRALN, address, 79 + INT_UNALIGN_DATA, current, regs); 83 80 84 81 /* 85 82 * Adjust pc to point at the actual instruction, which is unusual ··· 476 471 */ 477 472 local_irq_enable(); 478 473 479 - force_sig_info_fault(SIGSEGV, si_code, address, 480 - fault_num, tsk); 474 + force_sig_info_fault("segfault", SIGSEGV, si_code, address, 475 + fault_num, tsk, regs); 481 476 return 0; 482 477 } 483 478 ··· 552 547 if (is_kernel_mode) 553 548 goto no_context; 554 549 555 - force_sig_info_fault(SIGBUS, BUS_ADRERR, address, fault_num, tsk); 550 + force_sig_info_fault("bus error", SIGBUS, BUS_ADRERR, address, 551 + fault_num, tsk, regs); 556 552 return 0; 557 553 } 558 554
+1 -1
kernel/sysctl.c
··· 1496 1496 1497 1497 static struct ctl_table debug_table[] = { 1498 1498 #if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || \ 1499 - defined(CONFIG_S390) 1499 + defined(CONFIG_S390) || defined(CONFIG_TILE) 1500 1500 { 1501 1501 .procname = "exception-trace", 1502 1502 .data = &show_unhandled_signals,