diff options
Diffstat (limited to 'drivers/tty/sysrq.c')
| -rw-r--r-- | drivers/tty/sysrq.c | 191 |
1 files changed, 126 insertions, 65 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index b6e70c5cfa17..1f78b0db3b25 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -98,14 +98,13 @@ static int __init sysrq_always_enabled_setup(char *str) __setup("sysrq_always_enabled", sysrq_always_enabled_setup); -static void sysrq_handle_loglevel(int key) +static void sysrq_handle_loglevel(u8 key) { - int i; + u8 loglevel = key - '0'; - i = key - '0'; console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; - pr_info("Loglevel set to %d\n", i); - console_loglevel = i; + pr_info("Loglevel set to %u\n", loglevel); + console_loglevel = loglevel; } static const struct sysrq_key_op sysrq_loglevel_op = { .handler = sysrq_handle_loglevel, @@ -115,7 +114,7 @@ static const struct sysrq_key_op sysrq_loglevel_op = { }; #ifdef CONFIG_VT -static void sysrq_handle_SAK(int key) +static void sysrq_handle_SAK(u8 key) { struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work; @@ -132,7 +131,7 @@ static const struct sysrq_key_op sysrq_SAK_op = { #endif #ifdef CONFIG_VT -static void sysrq_handle_unraw(int key) +static void sysrq_handle_unraw(u8 key) { vt_reset_unicode(fg_console); } @@ -147,7 +146,7 @@ static const struct sysrq_key_op sysrq_unraw_op = { #define sysrq_unraw_op (*(const struct sysrq_key_op *)NULL) #endif /* CONFIG_VT */ -static void sysrq_handle_crash(int key) +static void sysrq_handle_crash(u8 key) { /* release the RCU read lock before crashing */ rcu_read_unlock(); @@ -161,7 +160,7 @@ static const struct sysrq_key_op sysrq_crash_op = { .enable_mask = SYSRQ_ENABLE_DUMP, }; -static void sysrq_handle_reboot(int key) +static void sysrq_handle_reboot(u8 key) { lockdep_off(); local_irq_enable(); @@ -176,7 +175,7 @@ static const struct sysrq_key_op sysrq_reboot_op = { const struct sysrq_key_op *__sysrq_reboot_op = &sysrq_reboot_op; -static void sysrq_handle_sync(int key) +static void sysrq_handle_sync(u8 key) { emergency_sync(); } @@ -187,7 +186,7 @@ static const struct sysrq_key_op sysrq_sync_op = { .enable_mask = SYSRQ_ENABLE_SYNC, }; -static void sysrq_handle_show_timers(int key) +static void sysrq_handle_show_timers(u8 key) { sysrq_timer_list_show(); } @@ -198,7 +197,7 @@ static const struct sysrq_key_op sysrq_show_timers_op = { .action_msg = "Show clockevent devices & pending hrtimers (no others)", }; -static void sysrq_handle_mountro(int key) +static void sysrq_handle_mountro(u8 key) { emergency_remount(); } @@ -210,7 +209,7 @@ static const struct sysrq_key_op sysrq_mountro_op = { }; #ifdef CONFIG_LOCKDEP -static void sysrq_handle_showlocks(int key) +static void sysrq_handle_showlocks(u8 key) { debug_show_all_locks(); } @@ -250,7 +249,7 @@ static void sysrq_showregs_othercpus(struct work_struct *dummy) static DECLARE_WORK(sysrq_showallcpus, sysrq_showregs_othercpus); -static void sysrq_handle_showallcpus(int key) +static void sysrq_handle_showallcpus(u8 key) { /* * Fall back to the workqueue based printing if the @@ -263,13 +262,14 @@ static void sysrq_handle_showallcpus(int key) if (in_hardirq()) regs = get_irq_regs(); - pr_info("CPU%d:\n", smp_processor_id()); + pr_info("CPU%d:\n", get_cpu()); if (regs) show_regs(regs); else show_stack(NULL, NULL, KERN_INFO); schedule_work(&sysrq_showallcpus); + put_cpu(); } } @@ -283,7 +283,7 @@ static const struct sysrq_key_op sysrq_showallcpus_op = { #define sysrq_showallcpus_op (*(const struct sysrq_key_op *)NULL) #endif -static void sysrq_handle_showregs(int key) +static void sysrq_handle_showregs(u8 key) { struct pt_regs *regs = NULL; @@ -300,7 +300,7 @@ static const struct sysrq_key_op sysrq_showregs_op = { .enable_mask = SYSRQ_ENABLE_DUMP, }; -static void sysrq_handle_showstate(int key) +static void sysrq_handle_showstate(u8 key) { show_state(); show_all_workqueues(); @@ -312,7 +312,7 @@ static const struct sysrq_key_op sysrq_showstate_op = { .enable_mask = SYSRQ_ENABLE_DUMP, }; -static void sysrq_handle_showstate_blocked(int key) +static void sysrq_handle_showstate_blocked(u8 key) { show_state_filter(TASK_UNINTERRUPTIBLE); } @@ -326,7 +326,7 @@ static const struct sysrq_key_op sysrq_showstate_blocked_op = { #ifdef CONFIG_TRACING #include <linux/ftrace.h> -static void sysrq_ftrace_dump(int key) +static void sysrq_ftrace_dump(u8 key) { ftrace_dump(DUMP_ALL); } @@ -340,9 +340,9 @@ static const struct sysrq_key_op sysrq_ftrace_dump_op = { #define sysrq_ftrace_dump_op (*(const struct sysrq_key_op *)NULL) #endif -static void sysrq_handle_showmem(int key) +static void sysrq_handle_showmem(u8 key) { - show_mem(0, NULL); + show_mem(); } static const struct sysrq_key_op sysrq_showmem_op = { .handler = sysrq_handle_showmem, @@ -370,7 +370,7 @@ static void send_sig_all(int sig) read_unlock(&tasklist_lock); } -static void sysrq_handle_term(int key) +static void sysrq_handle_term(u8 key) { send_sig_all(SIGTERM); console_loglevel = CONSOLE_LOGLEVEL_DEBUG; @@ -401,7 +401,7 @@ static void moom_callback(struct work_struct *ignored) static DECLARE_WORK(moom_work, moom_callback); -static void sysrq_handle_moom(int key) +static void sysrq_handle_moom(u8 key) { schedule_work(&moom_work); } @@ -413,7 +413,7 @@ static const struct sysrq_key_op sysrq_moom_op = { }; #ifdef CONFIG_BLOCK -static void sysrq_handle_thaw(int key) +static void sysrq_handle_thaw(u8 key) { emergency_thaw_all(); } @@ -427,7 +427,7 @@ static const struct sysrq_key_op sysrq_thaw_op = { #define sysrq_thaw_op (*(const struct sysrq_key_op *)NULL) #endif -static void sysrq_handle_kill(int key) +static void sysrq_handle_kill(u8 key) { send_sig_all(SIGKILL); console_loglevel = CONSOLE_LOGLEVEL_DEBUG; @@ -439,7 +439,7 @@ static const struct sysrq_key_op sysrq_kill_op = { .enable_mask = SYSRQ_ENABLE_SIGNAL, }; -static void sysrq_handle_unrt(int key) +static void sysrq_handle_unrt(u8 key) { normalize_rt_tasks(); } @@ -450,6 +450,17 @@ static const struct sysrq_key_op sysrq_unrt_op = { .enable_mask = SYSRQ_ENABLE_RTNICE, }; +static void sysrq_handle_replay_logs(u8 key) +{ + console_try_replay_all(); +} +static struct sysrq_key_op sysrq_replay_logs_op = { + .handler = sysrq_handle_replay_logs, + .help_msg = "replay-kernel-logs(R)", + .action_msg = "Replay kernel logs on consoles", + .enable_mask = SYSRQ_ENABLE_DUMP, +}; + /* Key Operations table and lock */ static DEFINE_SPINLOCK(sysrq_key_table_lock); @@ -519,7 +530,8 @@ static const struct sysrq_key_op *sysrq_key_table[62] = { NULL, /* O */ NULL, /* P */ NULL, /* Q */ - NULL, /* R */ + &sysrq_replay_logs_op, /* R */ + /* S: May be registered by sched_ext for resetting */ NULL, /* S */ NULL, /* T */ NULL, /* U */ @@ -531,25 +543,24 @@ static const struct sysrq_key_op *sysrq_key_table[62] = { }; /* key2index calculation, -1 on invalid index */ -static int sysrq_key_table_key2index(int key) +static int sysrq_key_table_key2index(u8 key) { - int retval; - - if ((key >= '0') && (key <= '9')) - retval = key - '0'; - else if ((key >= 'a') && (key <= 'z')) - retval = key + 10 - 'a'; - else if ((key >= 'A') && (key <= 'Z')) - retval = key + 36 - 'A'; - else - retval = -1; - return retval; + switch (key) { + case '0' ... '9': + return key - '0'; + case 'a' ... 'z': + return key - 'a' + 10; + case 'A' ... 'Z': + return key - 'A' + 10 + 26; + default: + return -1; + } } /* * get and put functions for the table, exposed to modules. */ -static const struct sysrq_key_op *__sysrq_get_key_op(int key) +static const struct sysrq_key_op *__sysrq_get_key_op(u8 key) { const struct sysrq_key_op *op_p = NULL; int i; @@ -561,7 +572,7 @@ static const struct sysrq_key_op *__sysrq_get_key_op(int key) return op_p; } -static void __sysrq_put_key_op(int key, const struct sysrq_key_op *op_p) +static void __sysrq_put_key_op(u8 key, const struct sysrq_key_op *op_p) { int i = sysrq_key_table_key2index(key); @@ -569,10 +580,9 @@ static void __sysrq_put_key_op(int key, const struct sysrq_key_op *op_p) sysrq_key_table[i] = op_p; } -void __handle_sysrq(int key, bool check_mask) +void __handle_sysrq(u8 key, bool check_mask) { const struct sysrq_key_op *op_p; - int orig_log_level; int orig_suppress_printk; int i; @@ -582,13 +592,12 @@ void __handle_sysrq(int key, bool check_mask) 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 - * simply emit this at KERN_EMERG as that would change message - * routing in the consumers of /proc/kmsg. + * Enter in the force_console context so that sysrq header is shown to + * provide the user with positive feedback. We do not simply emit this + * at KERN_EMERG as that would change message routing in the consumers + * of /proc/kmsg. */ - orig_log_level = console_loglevel; - console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; + printk_force_console_enter(); op_p = __sysrq_get_key_op(key); if (op_p) { @@ -598,11 +607,11 @@ void __handle_sysrq(int key, bool check_mask) */ if (!check_mask || sysrq_on_mask(op_p->enable_mask)) { pr_info("%s\n", op_p->action_msg); - console_loglevel = orig_log_level; + printk_force_console_exit(); op_p->handler(key); } else { pr_info("This sysrq operation is disabled.\n"); - console_loglevel = orig_log_level; + printk_force_console_exit(); } } else { pr_info("HELP : "); @@ -620,7 +629,7 @@ void __handle_sysrq(int key, bool check_mask) } } pr_cont("\n"); - console_loglevel = orig_log_level; + printk_force_console_exit(); } rcu_read_unlock(); rcu_sysrq_end(); @@ -628,7 +637,7 @@ void __handle_sysrq(int key, bool check_mask) suppress_printk = orig_suppress_printk; } -void handle_sysrq(int key) +void handle_sysrq(u8 key) { if (sysrq_on()) __handle_sysrq(key, true); @@ -703,7 +712,8 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state) static void sysrq_do_reset(struct timer_list *t) { - struct sysrq_state *state = from_timer(state, t, keyreset_timer); + struct sysrq_state *state = timer_container_of(state, t, + keyreset_timer); state->reset_requested = true; @@ -734,7 +744,7 @@ static void sysrq_detect_reset_sequence(struct sysrq_state *state, */ if (value && state->reset_seq_cnt) { state->reset_canceled = true; - del_timer(&state->keyreset_timer); + timer_delete(&state->keyreset_timer); } } else if (value == 0) { /* @@ -742,7 +752,7 @@ static void sysrq_detect_reset_sequence(struct sysrq_state *state, * to be pressed and held for the reset timeout * to hold. */ - del_timer(&state->keyreset_timer); + timer_delete(&state->keyreset_timer); if (--state->reset_seq_cnt == 0) state->reset_canceled = false; @@ -760,8 +770,6 @@ 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) { @@ -772,7 +780,7 @@ static void sysrq_of_get_keyreset_config(void) /* Reset in case a __weak definition was present */ sysrq_reset_seq_len = 0; - of_property_for_each_u32(np, "keyset", prop, p, key) { + of_property_for_each_u32(np, "keyset", key) { if (key == KEY_RESERVED || key > KEY_MAX || sysrq_reset_seq_len == SYSRQ_KEY_RESET_MAX) break; @@ -1112,7 +1120,47 @@ int sysrq_toggle_support(int enable_mask) } EXPORT_SYMBOL_GPL(sysrq_toggle_support); -static int __sysrq_swap_key_ops(int key, const struct sysrq_key_op *insert_op_p, +static int sysrq_sysctl_handler(const struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) +{ + int tmp, ret; + struct ctl_table t = *table; + + tmp = sysrq_mask(); + t.data = &tmp; + + /* + * Behaves like do_proc_dointvec as t does not have min nor max. + */ + ret = proc_dointvec_minmax(&t, write, buffer, lenp, ppos); + if (ret) + return ret; + + if (write) + sysrq_toggle_support(tmp); + + return 0; +} + +static const struct ctl_table sysrq_sysctl_table[] = { + { + .procname = "sysrq", + .data = NULL, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = sysrq_sysctl_handler, + }, +}; + +static int __init init_sysrq_sysctl(void) +{ + register_sysctl_init("kernel", sysrq_sysctl_table); + return 0; +} + +subsys_initcall(init_sysrq_sysctl); + +static int __sysrq_swap_key_ops(u8 key, const struct sysrq_key_op *insert_op_p, const struct sysrq_key_op *remove_op_p) { int retval; @@ -1136,13 +1184,13 @@ static int __sysrq_swap_key_ops(int key, const struct sysrq_key_op *insert_op_p, return retval; } -int register_sysrq_key(int key, const struct sysrq_key_op *op_p) +int register_sysrq_key(u8 key, const struct sysrq_key_op *op_p) { return __sysrq_swap_key_ops(key, op_p, NULL); } EXPORT_SYMBOL(register_sysrq_key); -int unregister_sysrq_key(int key, const struct sysrq_key_op *op_p) +int unregister_sysrq_key(u8 key, const struct sysrq_key_op *op_p) { return __sysrq_swap_key_ops(key, NULL, op_p); } @@ -1151,16 +1199,29 @@ EXPORT_SYMBOL(unregister_sysrq_key); #ifdef CONFIG_PROC_FS /* * writing 'C' to /proc/sysrq-trigger is like sysrq-C + * Normally, only the first character written is processed. + * However, if the first character is an underscore, + * all characters are processed. */ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - if (count) { + bool bulk = false; + size_t i; + + for (i = 0; i < count; i++) { char c; - if (get_user(c, buf)) + if (get_user(c, buf + i)) return -EFAULT; - __handle_sysrq(c, false); + + if (c == '_') + bulk = true; + else + __handle_sysrq(c, false); + + if (!bulk) + break; } return count; |
