/** * panic - halt the system * @fmt: The text string to print * * Display a message, then perform cleanups. * * This function never returns. */ voidpanic(constchar *fmt, ...) { ... pr_emerg("Kernel panic - not syncing: %s\n", buf); ... if (panic_timeout > 0) { //tj: 延迟重启 /* * Delay timeout seconds before rebooting the machine. * We can't use the "normal" timers since we just panicked. */ pr_emerg("Rebooting in %d seconds..\n", panic_timeout); //tj
for (i = 0; i < panic_timeout * 1000; i += PANIC_TIMER_STEP) { touch_nmi_watchdog(); if (i >= i_next) { i += panic_blink(state ^= 1); i_next = i + 3600 / PANIC_BLINK_SPD; } mdelay(PANIC_TIMER_STEP); } } if (panic_timeout != 0) { //tj: 立即reboot /* * This will not be a clean reboot, with everything * shutting down. But if there is a chance of * rebooting the system it will be rebooted. */ if (panic_reboot_mode != REBOOT_UNDEFINED) reboot_mode = panic_reboot_mode; emergency_restart(); } ... pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf); //tj: 一直卡这
/* Do not scroll important messages printed above */ suppress_printk = 1; local_irq_enable(); for (i = 0; ; i += PANIC_TIMER_STEP) { touch_softlockup_watchdog(); if (i >= i_next) { i += panic_blink(state ^= 1); i_next = i + 3600 / PANIC_BLINK_SPD; } mdelay(PANIC_TIMER_STEP); }
config PANIC_TIMEOUT int "panic timeout" default 0 help Set the timeout value (in seconds) until a reboot occurs when the the kernel panics. If n = 0, then we wait forever. A timeout value n > 0 will wait n seconds before rebooting, while a timeout value n < 0 will reboot immediately.
这个panic timeout在Kconfig里说的很清楚。
BUG() ifndef HAVE_ARCH_BUG
先看代码注释:
/* * Don't use BUG() or BUG_ON() unless there's really no way out; one * example might be detecting data structure corruption in the middle * of an operation that can't be backed out of. If the (sub)system * can somehow continue operating, perhaps with reduced functionality, * it's probably not BUG-worthy. * * If you're tempted to BUG(), think again: is completely giving up * really the *only* solution? There are usually better options, where * users don't need to reboot ASAP and can mostly shut down cleanly. */ #ifndef HAVE_ARCH_BUG #define BUG() do { \ printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ barrier_before_unreachable(); \ panic("BUG!"); \ } while (0) #endif
default: /* unknown/unrecognised bug trap type */ return DBG_HOOK_ERROR; }
/* If thread survives, skip over the BUG instruction and continue: */ arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE); return DBG_HOOK_HANDLED; }
bug = find_bug(bugaddr); if (!bug) return BUG_TRAP_TYPE_NONE; ... if (file) pr_crit("kernel BUG at %s:%u!\n", file, line); else pr_crit("Kernel BUG at %pB [verbose debug info unavailable]\n", (void *)bugaddr);
return BUG_TRAP_TYPE_BUG; }
intis_valid_bugaddr(unsignedlong addr) { /* * bug_handler() only called for BRK #BUG_BRK_IMM. * So the answer is trivial -- any spurious instances with no * bug table entry will be rejected by report_bug() and passed * back to the debug-monitors code and handled as a fatal * unexpected debug exception. */ return1; }
list = user_mode(regs) ? &user_break_hook : &kernel_break_hook;
/* * Since brk exception disables interrupt, this function is * entirely not preemptible, and we can use rcu list safely here. */ list_for_each_entry_rcu(hook, list, node) { unsignedint comment = esr & ESR_ELx_BRK64_ISS_COMMENT_MASK;
if ((comment & ~hook->mask) == hook->imm) fn = hook->fn; }
return fn ? fn(regs, esr) : DBG_HOOK_ERROR; }
就是在list里找到hook->fn,也就是bug_handler()。
那BUG_ON()怎么进入的panic(),除了中断就是靠panic_on_oops控制:
panic_on_oops
Controls the kernel’s behaviour when an oops or BUG is encountered.
= ================================================ 0 Try to continue operation. 1 Panic immediately. If the panic sysctl is also non-zero then the machine will be rebooted. = ================================================
Android一般在init.rc开启:
on init ... write /proc/sys/kernel/panic_on_oops 1
WARN()
先看注释:
/* * WARN(), WARN_ON(), WARN_ON_ONCE, and so on can be used to report * significant kernel issues that need prompt attention if they should ever * appear at runtime. * * Do not use these macros when checking for invalid external inputs * (e.g. invalid system call arguments, or invalid data coming from * network/devices), and on transient conditions like ENOMEM or EAGAIN. * These macros should be used for recoverable kernel issues only. * For invalid external inputs, transient conditions, etc use * pr_err[_once/_ratelimited]() followed by dump_stack(), if necessary. * Do not include "BUG"/"WARNING" in format strings manually to make these * conditions distinguishable from kernel issues. * * Use the versions with printk format strings to provide better diagnostics. */
if (warning) { /* this is a WARN_ON rather than BUG/BUG_ON */ __warn(file, line, (void *)bugaddr, BUG_GET_TAINT(bug), regs, NULL); return BUG_TRAP_TYPE_WARN; }
if (file) pr_crit("kernel BUG at %s:%u!\n", file, line);
if (file) pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS\n", raw_smp_processor_id(), current->pid, file, line, caller); else pr_warn("WARNING: CPU: %d PID: %d at %pS\n", raw_smp_processor_id(), current->pid, caller);
if (args) vprintk(args->fmt, args->args);
if (panic_on_warn) { //tj: here /* * This thread may hit another WARN() in the panic path. * Resetting this prevents additional WARN() from panicking the * system on this thread. Other threads are blocked by the * panic_mutex in panic(). */ panic_on_warn = 0; panic("panic_on_warn set ...\n"); }
print_modules();
if (regs) show_regs(regs); else dump_stack();
print_irqtrace_events(current);
print_oops_end_marker();
/* Just a warning, don't kill lockdep. */ add_taint(taint, LOCKDEP_STILL_OK);
what? panic还能发生在warn上?没错,就是这个panic_on_warn,看下缘由:
panic_on_warn
Calls panic() in the WARN() path when set to 1. This is useful to avoid a kernel rebuild when attempting to kdump at the location of a WARN().
= ================================================ 0 Only WARN(), default behaviour. 1 Call panic() after printing out WARN() location. = ================================================
kdump用时不用rebuild,ok。
tainted-kernels
一些Oops log会看到Tainted字样如下:
35.449908: <6> CPU: 0 PID: 1 Comm: init Tainted: G S W 4.14.117-perf+ #65
voidadd_taint(unsigned flag, enum lockdep_ok lockdep_ok) { if (lockdep_ok == LOCKDEP_NOW_UNRELIABLE && __debug_locks_off()) pr_warn("Disabling lock debugging due to kernel taint\n");
set_bit(flag, &tainted_mask); //tj
if (tainted_mask & panic_on_taint) { panic_on_taint = 0; panic("panic_on_taint set ..."); } }
污染标记到tainted_mask里。
/* This cannot be an enum because some may be used in assembly source. */ #define TAINT_PROPRIETARY_MODULE 0 #define TAINT_FORCED_MODULE 1 #define TAINT_CPU_OUT_OF_SPEC 2 #define TAINT_FORCED_RMMOD 3 #define TAINT_MACHINE_CHECK 4 #define TAINT_BAD_PAGE 5 #define TAINT_USER 6 #define TAINT_DIE 7 //tj #define TAINT_OVERRIDDEN_ACPI_TABLE 8 #define TAINT_WARN 9 #define TAINT_CRAP 10 #define TAINT_FIRMWARE_WORKAROUND 11 #define TAINT_OOT_MODULE 12 #define TAINT_UNSIGNED_MODULE 13 #define TAINT_SOFTLOCKUP 14 #define TAINT_LIVEPATCH 15 #define TAINT_AUX 16 #define TAINT_RANDSTRUCT 17 #define TAINT_FLAGS_COUNT 18
这么多污染种类。打印污染状态时:
/** * print_tainted - return a string to represent the kernel taint state. * * For individual taint flag meanings, see Documentation/admin-guide/sysctl/kernel.rst * * The string is overwritten by the next call to print_tainted(), * but is always NULL terminated. */ constchar *print_tainted(void) { staticchar buf[TAINT_FLAGS_COUNT + sizeof("Tainted: ")];
panic_on_taint= Bitmask for conditionally calling panic() in add_taint()
Format: <hex>[,nousertaint]
Hexadecimal bitmask representing the set of TAINT flags
that will cause the kernel to panic when add_taint() is
called with any of the flags in this set.
The optional switch "nousertaint" can be utilized to
prevent userspace forced crashes by writing to sysctl
/proc/sys/kernel/tainted any flagset matching with the
bitmask set on panic_on_taint.
See Documentation/admin-guide/tainted-kernels.rst for
extra details on the taint flags that users can pick
to compose the bitmask to assign to panic_on_taint.