diff options
Diffstat (limited to 'drivers/tty/sysrq.c')
| -rw-r--r-- | drivers/tty/sysrq.c | 175 |
1 files changed, 132 insertions, 43 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 3687f0cad64..454b65898e2 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -43,14 +43,21 @@ #include <linux/input.h> #include <linux/uaccess.h> #include <linux/moduleparam.h> +#include <linux/jiffies.h> +#include <linux/syscalls.h> +#include <linux/of.h> +#include <linux/rcupdate.h> #include <asm/ptrace.h> #include <asm/irq_regs.h> /* Whether we react on sysrq keys or just ignore them */ -static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE; +static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; static bool __read_mostly sysrq_always_enabled; +unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED }; +int sysrq_reset_downtime_ms __weak; + static bool sysrq_on(void) { return sysrq_enabled || sysrq_always_enabled; @@ -82,7 +89,7 @@ static void sysrq_handle_loglevel(int key) int i; i = key - '0'; - console_loglevel = 7; + console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; printk("Loglevel set to %d\n", i); console_loglevel = i; } @@ -101,7 +108,7 @@ static void sysrq_handle_SAK(int key) } static struct sysrq_key_op sysrq_SAK_op = { .handler = sysrq_handle_SAK, - .help_msg = "saK", + .help_msg = "sak(k)", .action_msg = "SAK", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; @@ -117,7 +124,7 @@ static void sysrq_handle_unraw(int key) static struct sysrq_key_op sysrq_unraw_op = { .handler = sysrq_handle_unraw, - .help_msg = "unRaw", + .help_msg = "unraw(r)", .action_msg = "Keyboard mode set to system default", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; @@ -135,7 +142,7 @@ static void sysrq_handle_crash(int key) } static struct sysrq_key_op sysrq_crash_op = { .handler = sysrq_handle_crash, - .help_msg = "Crash", + .help_msg = "crash(c)", .action_msg = "Trigger a crash", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -148,7 +155,7 @@ static void sysrq_handle_reboot(int key) } static struct sysrq_key_op sysrq_reboot_op = { .handler = sysrq_handle_reboot, - .help_msg = "reBoot", + .help_msg = "reboot(b)", .action_msg = "Resetting", .enable_mask = SYSRQ_ENABLE_BOOT, }; @@ -159,7 +166,7 @@ static void sysrq_handle_sync(int key) } static struct sysrq_key_op sysrq_sync_op = { .handler = sysrq_handle_sync, - .help_msg = "Sync", + .help_msg = "sync(s)", .action_msg = "Emergency Sync", .enable_mask = SYSRQ_ENABLE_SYNC, }; @@ -171,7 +178,7 @@ static void sysrq_handle_show_timers(int key) static struct sysrq_key_op sysrq_show_timers_op = { .handler = sysrq_handle_show_timers, - .help_msg = "show-all-timers(Q)", + .help_msg = "show-all-timers(q)", .action_msg = "Show clockevent devices & pending hrtimers (no others)", }; @@ -181,7 +188,7 @@ static void sysrq_handle_mountro(int key) } static struct sysrq_key_op sysrq_mountro_op = { .handler = sysrq_handle_mountro, - .help_msg = "Unmount", + .help_msg = "unmount(u)", .action_msg = "Emergency Remount R/O", .enable_mask = SYSRQ_ENABLE_REMOUNT, }; @@ -194,7 +201,7 @@ static void sysrq_handle_showlocks(int key) static struct sysrq_key_op sysrq_showlocks_op = { .handler = sysrq_handle_showlocks, - .help_msg = "show-all-locks(D)", + .help_msg = "show-all-locks(d)", .action_msg = "Show Locks Held", }; #else @@ -245,7 +252,7 @@ static void sysrq_handle_showallcpus(int key) static struct sysrq_key_op sysrq_showallcpus_op = { .handler = sysrq_handle_showallcpus, - .help_msg = "show-backtrace-all-active-cpus(L)", + .help_msg = "show-backtrace-all-active-cpus(l)", .action_msg = "Show backtrace of all active CPUs", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -260,7 +267,7 @@ static void sysrq_handle_showregs(int key) } static struct sysrq_key_op sysrq_showregs_op = { .handler = sysrq_handle_showregs, - .help_msg = "show-registers(P)", + .help_msg = "show-registers(p)", .action_msg = "Show Regs", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -271,7 +278,7 @@ static void sysrq_handle_showstate(int key) } static struct sysrq_key_op sysrq_showstate_op = { .handler = sysrq_handle_showstate, - .help_msg = "show-task-states(T)", + .help_msg = "show-task-states(t)", .action_msg = "Show State", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -282,7 +289,7 @@ static void sysrq_handle_showstate_blocked(int key) } static struct sysrq_key_op sysrq_showstate_blocked_op = { .handler = sysrq_handle_showstate_blocked, - .help_msg = "show-blocked-tasks(W)", + .help_msg = "show-blocked-tasks(w)", .action_msg = "Show Blocked State", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -296,7 +303,7 @@ static void sysrq_ftrace_dump(int key) } static struct sysrq_key_op sysrq_ftrace_dump_op = { .handler = sysrq_ftrace_dump, - .help_msg = "dump-ftrace-buffer(Z)", + .help_msg = "dump-ftrace-buffer(z)", .action_msg = "Dump ftrace buffer", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -310,7 +317,7 @@ static void sysrq_handle_showmem(int key) } static struct sysrq_key_op sysrq_showmem_op = { .handler = sysrq_handle_showmem, - .help_msg = "show-memory-usage(M)", + .help_msg = "show-memory-usage(m)", .action_msg = "Show Memory", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -337,11 +344,11 @@ static void send_sig_all(int sig) static void sysrq_handle_term(int key) { send_sig_all(SIGTERM); - console_loglevel = 8; + console_loglevel = CONSOLE_LOGLEVEL_DEBUG; } static struct sysrq_key_op sysrq_term_op = { .handler = sysrq_handle_term, - .help_msg = "terminate-all-tasks(E)", + .help_msg = "terminate-all-tasks(e)", .action_msg = "Terminate All Tasks", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -360,7 +367,7 @@ static void sysrq_handle_moom(int key) } static struct sysrq_key_op sysrq_moom_op = { .handler = sysrq_handle_moom, - .help_msg = "memory-full-oom-kill(F)", + .help_msg = "memory-full-oom-kill(f)", .action_msg = "Manual OOM execution", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -372,7 +379,7 @@ static void sysrq_handle_thaw(int key) } static struct sysrq_key_op sysrq_thaw_op = { .handler = sysrq_handle_thaw, - .help_msg = "thaw-filesystems(J)", + .help_msg = "thaw-filesystems(j)", .action_msg = "Emergency Thaw of all frozen filesystems", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -381,11 +388,11 @@ static struct sysrq_key_op sysrq_thaw_op = { static void sysrq_handle_kill(int key) { send_sig_all(SIGKILL); - console_loglevel = 8; + console_loglevel = CONSOLE_LOGLEVEL_DEBUG; } static struct sysrq_key_op sysrq_kill_op = { .handler = sysrq_handle_kill, - .help_msg = "kill-all-tasks(I)", + .help_msg = "kill-all-tasks(i)", .action_msg = "Kill All Tasks", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -396,7 +403,7 @@ static void sysrq_handle_unrt(int key) } static struct sysrq_key_op sysrq_unrt_op = { .handler = sysrq_handle_unrt, - .help_msg = "nice-all-RT-tasks(N)", + .help_msg = "nice-all-RT-tasks(n)", .action_msg = "Nice All RT Tasks", .enable_mask = SYSRQ_ENABLE_RTNICE, }; @@ -504,9 +511,9 @@ void __handle_sysrq(int key, bool check_mask) struct sysrq_key_op *op_p; int orig_log_level; int i; - unsigned long flags; - spin_lock_irqsave(&sysrq_key_table_lock, flags); + rcu_sysrq_start(); + rcu_read_lock(); /* * Raise the apparent loglevel to maximum so that the sysrq header * is shown to provide the user with positive feedback. We do not @@ -514,7 +521,7 @@ void __handle_sysrq(int key, bool check_mask) * routing in the consumers of /proc/kmsg. */ orig_log_level = console_loglevel; - console_loglevel = 7; + console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; printk(KERN_INFO "SysRq : "); op_p = __sysrq_get_key_op(key); @@ -548,7 +555,8 @@ void __handle_sysrq(int key, bool check_mask) printk("\n"); console_loglevel = orig_log_level; } - spin_unlock_irqrestore(&sysrq_key_table_lock, flags); + rcu_read_unlock(); + rcu_sysrq_end(); } void handle_sysrq(int key) @@ -582,10 +590,12 @@ struct sysrq_state { /* reset sequence handling */ bool reset_canceled; + bool reset_requested; unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)]; int reset_seq_len; int reset_seq_cnt; int reset_seq_version; + struct timer_list keyreset_timer; }; #define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ @@ -619,30 +629,94 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state) state->reset_seq_version = sysrq_reset_seq_version; } -static bool sysrq_detect_reset_sequence(struct sysrq_state *state, +static void sysrq_do_reset(unsigned long _state) +{ + struct sysrq_state *state = (struct sysrq_state *) _state; + + state->reset_requested = true; + + sys_sync(); + kernel_restart(NULL); +} + +static void sysrq_handle_reset_request(struct sysrq_state *state) +{ + if (state->reset_requested) + __handle_sysrq(sysrq_xlate[KEY_B], false); + + if (sysrq_reset_downtime_ms) + mod_timer(&state->keyreset_timer, + jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms)); + else + sysrq_do_reset((unsigned long)state); +} + +static void sysrq_detect_reset_sequence(struct sysrq_state *state, unsigned int code, int value) { if (!test_bit(code, state->reset_keybit)) { /* * Pressing any key _not_ in reset sequence cancels - * the reset sequence. + * the reset sequence. Also cancelling the timer in + * case additional keys were pressed after a reset + * has been requested. */ - if (value && state->reset_seq_cnt) + if (value && state->reset_seq_cnt) { state->reset_canceled = true; + del_timer(&state->keyreset_timer); + } } else if (value == 0) { - /* key release */ + /* + * Key release - all keys in the reset sequence need + * to be pressed and held for the reset timeout + * to hold. + */ + del_timer(&state->keyreset_timer); + if (--state->reset_seq_cnt == 0) state->reset_canceled = false; } else if (value == 1) { /* key press, not autorepeat */ if (++state->reset_seq_cnt == state->reset_seq_len && !state->reset_canceled) { - return true; + sysrq_handle_reset_request(state); } } +} + +#ifdef CONFIG_OF +static void sysrq_of_get_keyreset_config(void) +{ + u32 key; + struct device_node *np; + struct property *prop; + const __be32 *p; + + np = of_find_node_by_path("/chosen/linux,sysrq-reset-seq"); + if (!np) { + pr_debug("No sysrq node found"); + return; + } - return false; + /* Reset in case a __weak definition was present */ + sysrq_reset_seq_len = 0; + + of_property_for_each_u32(np, "keyset", prop, p, key) { + if (key == KEY_RESERVED || key > KEY_MAX || + sysrq_reset_seq_len == SYSRQ_KEY_RESET_MAX) + break; + + sysrq_reset_seq[sysrq_reset_seq_len++] = (unsigned short)key; + } + + /* Get reset timeout if any. */ + of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms); } +#else +static void sysrq_of_get_keyreset_config(void) +{ +} +#endif static void sysrq_reinject_alt_sysrq(struct work_struct *work) { @@ -748,10 +822,8 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq, if (was_active) schedule_work(&sysrq->reinject_work); - if (sysrq_detect_reset_sequence(sysrq, code, value)) { - /* Force emergency reboot */ - __handle_sysrq(sysrq_xlate[KEY_B], false); - } + /* Check for reset sequence */ + sysrq_detect_reset_sequence(sysrq, code, value); } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { /* @@ -812,6 +884,8 @@ static int sysrq_connect(struct input_handler *handler, sysrq->handle.handler = handler; sysrq->handle.name = "sysrq"; sysrq->handle.private = sysrq; + setup_timer(&sysrq->keyreset_timer, + sysrq_do_reset, (unsigned long)sysrq); error = input_register_handle(&sysrq->handle); if (error) { @@ -841,6 +915,7 @@ static void sysrq_disconnect(struct input_handle *handle) input_close_device(handle); cancel_work_sync(&sysrq->reinject_work); + del_timer_sync(&sysrq->keyreset_timer); input_unregister_handle(handle); kfree(sysrq); } @@ -870,14 +945,13 @@ static struct input_handler sysrq_handler = { static bool sysrq_handler_registered; -unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED }; - static inline void sysrq_register_handler(void) { unsigned short key; int error; int i; + /* First check if a __weak interface was instantiated. */ for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) { key = platform_sysrq_reset_seq[i]; if (key == KEY_RESERVED || key > KEY_MAX) @@ -886,6 +960,12 @@ static inline void sysrq_register_handler(void) sysrq_reset_seq[sysrq_reset_seq_len++] = key; } + /* + * DT configuration takes precedence over anything that would + * have been defined via the __weak interface. + */ + sysrq_of_get_keyreset_config(); + error = input_register_handler(&sysrq_handler); if (error) pr_err("Failed to register input handler, error %d", error); @@ -907,7 +987,7 @@ static int sysrq_reset_seq_param_set(const char *buffer, unsigned long val; int error; - error = strict_strtoul(buffer, 0, &val); + error = kstrtoul(buffer, 0, &val); if (error < 0) return error; @@ -931,6 +1011,8 @@ static struct kernel_param_ops param_ops_sysrq_reset_seq = { module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, &sysrq_reset_seq_len, 0644); +module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644); + #else static inline void sysrq_register_handler(void) @@ -963,16 +1045,23 @@ static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, struct sysrq_key_op *remove_op_p) { int retval; - unsigned long flags; - spin_lock_irqsave(&sysrq_key_table_lock, flags); + spin_lock(&sysrq_key_table_lock); if (__sysrq_get_key_op(key) == remove_op_p) { __sysrq_put_key_op(key, insert_op_p); retval = 0; } else { retval = -1; } - spin_unlock_irqrestore(&sysrq_key_table_lock, flags); + spin_unlock(&sysrq_key_table_lock); + + /* + * A concurrent __handle_sysrq either got the old op or the new op. + * Wait for it to go away before returning, so the code for an old + * op is not freed (eg. on module unload) while it is in use. + */ + synchronize_rcu(); + return retval; } |
