aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/sysrq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/sysrq.c')
-rw-r--r--drivers/tty/sysrq.c195
1 files changed, 130 insertions, 65 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index f724962a5906..d2b2720db6ca 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -19,6 +19,7 @@
#include <linux/sched/rt.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
+#include <linux/ctype.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
@@ -63,6 +64,19 @@ static bool sysrq_on(void)
return sysrq_enabled || sysrq_always_enabled;
}
+/**
+ * sysrq_mask - Getter for sysrq_enabled mask.
+ *
+ * Return: 1 if sysrq is always enabled, enabled sysrq_key_op mask otherwise.
+ */
+int sysrq_mask(void)
+{
+ if (sysrq_always_enabled)
+ return 1;
+ return sysrq_enabled;
+}
+EXPORT_SYMBOL_GPL(sysrq_mask);
+
/*
* A value of 1 means 'all', other nonzero values are an op mask:
*/
@@ -93,7 +107,7 @@ static void sysrq_handle_loglevel(int key)
pr_info("Loglevel set to %d\n", i);
console_loglevel = i;
}
-static struct sysrq_key_op sysrq_loglevel_op = {
+static const struct sysrq_key_op sysrq_loglevel_op = {
.handler = sysrq_handle_loglevel,
.help_msg = "loglevel(0-9)",
.action_msg = "Changing Loglevel",
@@ -104,16 +118,17 @@ static struct sysrq_key_op sysrq_loglevel_op = {
static void sysrq_handle_SAK(int key)
{
struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work;
+
schedule_work(SAK_work);
}
-static struct sysrq_key_op sysrq_SAK_op = {
+static const struct sysrq_key_op sysrq_SAK_op = {
.handler = sysrq_handle_SAK,
.help_msg = "sak(k)",
.action_msg = "SAK",
.enable_mask = SYSRQ_ENABLE_KEYBOARD,
};
#else
-#define sysrq_SAK_op (*(struct sysrq_key_op *)NULL)
+#define sysrq_SAK_op (*(const struct sysrq_key_op *)NULL)
#endif
#ifdef CONFIG_VT
@@ -122,14 +137,14 @@ static void sysrq_handle_unraw(int key)
vt_reset_unicode(fg_console);
}
-static struct sysrq_key_op sysrq_unraw_op = {
+static const struct sysrq_key_op sysrq_unraw_op = {
.handler = sysrq_handle_unraw,
.help_msg = "unraw(r)",
.action_msg = "Keyboard mode set to system default",
.enable_mask = SYSRQ_ENABLE_KEYBOARD,
};
#else
-#define sysrq_unraw_op (*(struct sysrq_key_op *)NULL)
+#define sysrq_unraw_op (*(const struct sysrq_key_op *)NULL)
#endif /* CONFIG_VT */
static void sysrq_handle_crash(int key)
@@ -139,7 +154,7 @@ static void sysrq_handle_crash(int key)
panic("sysrq triggered crash\n");
}
-static struct sysrq_key_op sysrq_crash_op = {
+static const struct sysrq_key_op sysrq_crash_op = {
.handler = sysrq_handle_crash,
.help_msg = "crash(c)",
.action_msg = "Trigger a crash",
@@ -152,18 +167,20 @@ static void sysrq_handle_reboot(int key)
local_irq_enable();
emergency_restart();
}
-static struct sysrq_key_op sysrq_reboot_op = {
+static const struct sysrq_key_op sysrq_reboot_op = {
.handler = sysrq_handle_reboot,
.help_msg = "reboot(b)",
.action_msg = "Resetting",
.enable_mask = SYSRQ_ENABLE_BOOT,
};
+const struct sysrq_key_op *__sysrq_reboot_op = &sysrq_reboot_op;
+
static void sysrq_handle_sync(int key)
{
emergency_sync();
}
-static struct sysrq_key_op sysrq_sync_op = {
+static const struct sysrq_key_op sysrq_sync_op = {
.handler = sysrq_handle_sync,
.help_msg = "sync(s)",
.action_msg = "Emergency Sync",
@@ -175,7 +192,7 @@ static void sysrq_handle_show_timers(int key)
sysrq_timer_list_show();
}
-static struct sysrq_key_op sysrq_show_timers_op = {
+static const struct sysrq_key_op sysrq_show_timers_op = {
.handler = sysrq_handle_show_timers,
.help_msg = "show-all-timers(q)",
.action_msg = "Show clockevent devices & pending hrtimers (no others)",
@@ -185,7 +202,7 @@ static void sysrq_handle_mountro(int key)
{
emergency_remount();
}
-static struct sysrq_key_op sysrq_mountro_op = {
+static const struct sysrq_key_op sysrq_mountro_op = {
.handler = sysrq_handle_mountro,
.help_msg = "unmount(u)",
.action_msg = "Emergency Remount R/O",
@@ -198,13 +215,13 @@ static void sysrq_handle_showlocks(int key)
debug_show_all_locks();
}
-static struct sysrq_key_op sysrq_showlocks_op = {
+static const struct sysrq_key_op sysrq_showlocks_op = {
.handler = sysrq_handle_showlocks,
.help_msg = "show-all-locks(d)",
.action_msg = "Show Locks Held",
};
#else
-#define sysrq_showlocks_op (*(struct sysrq_key_op *)NULL)
+#define sysrq_showlocks_op (*(const struct sysrq_key_op *)NULL)
#endif
#ifdef CONFIG_SMP
@@ -215,12 +232,14 @@ static void showacpu(void *dummy)
unsigned long flags;
/* Idle CPUs have no interesting backtrace. */
- if (idle_cpu(smp_processor_id()))
+ if (idle_cpu(smp_processor_id())) {
+ pr_info("CPU%d: backtrace skipped as idling\n", smp_processor_id());
return;
+ }
raw_spin_lock_irqsave(&show_lock, flags);
pr_info("CPU%d:\n", smp_processor_id());
- show_stack(NULL, NULL);
+ show_stack(NULL, NULL, KERN_INFO);
raw_spin_unlock_irqrestore(&show_lock, flags);
}
@@ -241,35 +260,40 @@ static void sysrq_handle_showallcpus(int key)
if (!trigger_all_cpu_backtrace()) {
struct pt_regs *regs = NULL;
- if (in_irq())
+ if (in_hardirq())
regs = get_irq_regs();
- if (regs) {
- pr_info("CPU%d:\n", smp_processor_id());
+
+ pr_info("CPU%d:\n", smp_processor_id());
+ if (regs)
show_regs(regs);
- }
+ else
+ show_stack(NULL, NULL, KERN_INFO);
+
schedule_work(&sysrq_showallcpus);
}
}
-static struct sysrq_key_op sysrq_showallcpus_op = {
+static const struct sysrq_key_op sysrq_showallcpus_op = {
.handler = sysrq_handle_showallcpus,
.help_msg = "show-backtrace-all-active-cpus(l)",
.action_msg = "Show backtrace of all active CPUs",
.enable_mask = SYSRQ_ENABLE_DUMP,
};
+#else
+#define sysrq_showallcpus_op (*(const struct sysrq_key_op *)NULL)
#endif
static void sysrq_handle_showregs(int key)
{
struct pt_regs *regs = NULL;
- if (in_irq())
+ if (in_hardirq())
regs = get_irq_regs();
if (regs)
show_regs(regs);
perf_event_print_debug();
}
-static struct sysrq_key_op sysrq_showregs_op = {
+static const struct sysrq_key_op sysrq_showregs_op = {
.handler = sysrq_handle_showregs,
.help_msg = "show-registers(p)",
.action_msg = "Show Regs",
@@ -279,9 +303,9 @@ static struct sysrq_key_op sysrq_showregs_op = {
static void sysrq_handle_showstate(int key)
{
show_state();
- show_workqueue_state();
+ show_all_workqueues();
}
-static struct sysrq_key_op sysrq_showstate_op = {
+static const struct sysrq_key_op sysrq_showstate_op = {
.handler = sysrq_handle_showstate,
.help_msg = "show-task-states(t)",
.action_msg = "Show State",
@@ -292,7 +316,7 @@ static void sysrq_handle_showstate_blocked(int key)
{
show_state_filter(TASK_UNINTERRUPTIBLE);
}
-static struct sysrq_key_op sysrq_showstate_blocked_op = {
+static const struct sysrq_key_op sysrq_showstate_blocked_op = {
.handler = sysrq_handle_showstate_blocked,
.help_msg = "show-blocked-tasks(w)",
.action_msg = "Show Blocked State",
@@ -306,21 +330,21 @@ static void sysrq_ftrace_dump(int key)
{
ftrace_dump(DUMP_ALL);
}
-static struct sysrq_key_op sysrq_ftrace_dump_op = {
+static const struct sysrq_key_op sysrq_ftrace_dump_op = {
.handler = sysrq_ftrace_dump,
.help_msg = "dump-ftrace-buffer(z)",
.action_msg = "Dump ftrace buffer",
.enable_mask = SYSRQ_ENABLE_DUMP,
};
#else
-#define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)NULL)
+#define sysrq_ftrace_dump_op (*(const struct sysrq_key_op *)NULL)
#endif
static void sysrq_handle_showmem(int key)
{
show_mem(0, NULL);
}
-static struct sysrq_key_op sysrq_showmem_op = {
+static const struct sysrq_key_op sysrq_showmem_op = {
.handler = sysrq_handle_showmem,
.help_msg = "show-memory-usage(m)",
.action_msg = "Show Memory",
@@ -351,7 +375,7 @@ static void sysrq_handle_term(int key)
send_sig_all(SIGTERM);
console_loglevel = CONSOLE_LOGLEVEL_DEBUG;
}
-static struct sysrq_key_op sysrq_term_op = {
+static const struct sysrq_key_op sysrq_term_op = {
.handler = sysrq_handle_term,
.help_msg = "terminate-all-tasks(e)",
.action_msg = "Terminate All Tasks",
@@ -381,7 +405,7 @@ static void sysrq_handle_moom(int key)
{
schedule_work(&moom_work);
}
-static struct sysrq_key_op sysrq_moom_op = {
+static const struct sysrq_key_op sysrq_moom_op = {
.handler = sysrq_handle_moom,
.help_msg = "memory-full-oom-kill(f)",
.action_msg = "Manual OOM execution",
@@ -393,12 +417,14 @@ static void sysrq_handle_thaw(int key)
{
emergency_thaw_all();
}
-static struct sysrq_key_op sysrq_thaw_op = {
+static const struct sysrq_key_op sysrq_thaw_op = {
.handler = sysrq_handle_thaw,
.help_msg = "thaw-filesystems(j)",
.action_msg = "Emergency Thaw of all frozen filesystems",
.enable_mask = SYSRQ_ENABLE_SIGNAL,
};
+#else
+#define sysrq_thaw_op (*(const struct sysrq_key_op *)NULL)
#endif
static void sysrq_handle_kill(int key)
@@ -406,7 +432,7 @@ static void sysrq_handle_kill(int key)
send_sig_all(SIGKILL);
console_loglevel = CONSOLE_LOGLEVEL_DEBUG;
}
-static struct sysrq_key_op sysrq_kill_op = {
+static const struct sysrq_key_op sysrq_kill_op = {
.handler = sysrq_handle_kill,
.help_msg = "kill-all-tasks(i)",
.action_msg = "Kill All Tasks",
@@ -417,7 +443,7 @@ static void sysrq_handle_unrt(int key)
{
normalize_rt_tasks();
}
-static struct sysrq_key_op sysrq_unrt_op = {
+static const struct sysrq_key_op sysrq_unrt_op = {
.handler = sysrq_handle_unrt,
.help_msg = "nice-all-RT-tasks(n)",
.action_msg = "Nice All RT Tasks",
@@ -427,7 +453,7 @@ static struct sysrq_key_op sysrq_unrt_op = {
/* Key Operations table and lock */
static DEFINE_SPINLOCK(sysrq_key_table_lock);
-static struct sysrq_key_op *sysrq_key_table[36] = {
+static const struct sysrq_key_op *sysrq_key_table[62] = {
&sysrq_loglevel_op, /* 0 */
&sysrq_loglevel_op, /* 1 */
&sysrq_loglevel_op, /* 2 */
@@ -453,17 +479,9 @@ static struct sysrq_key_op *sysrq_key_table[36] = {
NULL, /* g */
NULL, /* h - reserved for help */
&sysrq_kill_op, /* i */
-#ifdef CONFIG_BLOCK
&sysrq_thaw_op, /* j */
-#else
- NULL, /* j */
-#endif
&sysrq_SAK_op, /* k */
-#ifdef CONFIG_SMP
&sysrq_showallcpus_op, /* l */
-#else
- NULL, /* l */
-#endif
&sysrq_showmem_op, /* m */
&sysrq_unrt_op, /* n */
/* o: This will often be registered as 'Off' at init time */
@@ -484,6 +502,32 @@ static struct sysrq_key_op *sysrq_key_table[36] = {
/* y: May be registered on sparc64 for global register dump */
NULL, /* y */
&sysrq_ftrace_dump_op, /* z */
+ NULL, /* A */
+ NULL, /* B */
+ NULL, /* C */
+ NULL, /* D */
+ NULL, /* E */
+ NULL, /* F */
+ NULL, /* G */
+ NULL, /* H */
+ NULL, /* I */
+ NULL, /* J */
+ NULL, /* K */
+ NULL, /* L */
+ NULL, /* M */
+ NULL, /* N */
+ NULL, /* O */
+ NULL, /* P */
+ NULL, /* Q */
+ NULL, /* R */
+ NULL, /* S */
+ NULL, /* T */
+ NULL, /* U */
+ NULL, /* V */
+ NULL, /* W */
+ NULL, /* X */
+ NULL, /* Y */
+ NULL, /* Z */
};
/* key2index calculation, -1 on invalid index */
@@ -495,6 +539,8 @@ static int sysrq_key_table_key2index(int key)
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;
@@ -503,29 +549,29 @@ static int sysrq_key_table_key2index(int key)
/*
* get and put functions for the table, exposed to modules.
*/
-struct sysrq_key_op *__sysrq_get_key_op(int key)
+static const struct sysrq_key_op *__sysrq_get_key_op(int key)
{
- struct sysrq_key_op *op_p = NULL;
- int i;
+ const struct sysrq_key_op *op_p = NULL;
+ int i;
i = sysrq_key_table_key2index(key);
if (i != -1)
- op_p = sysrq_key_table[i];
+ op_p = sysrq_key_table[i];
- return op_p;
+ return op_p;
}
-static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p)
+static void __sysrq_put_key_op(int key, const struct sysrq_key_op *op_p)
{
- int i = sysrq_key_table_key2index(key);
+ int i = sysrq_key_table_key2index(key);
- if (i != -1)
- sysrq_key_table[i] = op_p;
+ if (i != -1)
+ sysrq_key_table[i] = op_p;
}
void __handle_sysrq(int key, bool check_mask)
{
- struct sysrq_key_op *op_p;
+ const struct sysrq_key_op *op_p;
int orig_log_level;
int orig_suppress_printk;
int i;
@@ -544,8 +590,8 @@ void __handle_sysrq(int key, bool check_mask)
orig_log_level = console_loglevel;
console_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
- op_p = __sysrq_get_key_op(key);
- if (op_p) {
+ op_p = __sysrq_get_key_op(key);
+ if (op_p) {
/*
* Should we check for enabled operations (/proc/sysrq-trigger
* should not) and is the invoked operation enabled?
@@ -594,13 +640,13 @@ static int sysrq_reset_downtime_ms;
/* Simple translation table for the SysRq keys */
static const unsigned char sysrq_xlate[KEY_CNT] =
- "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */
- "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */
- "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */
- "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */
- "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */
- "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
- "\r\000/"; /* 0x60 - 0x6f */
+ "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */
+ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */
+ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */
+ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */
+ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */
+ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
+ "\r\000/"; /* 0x60 - 0x6f */
struct sysrq_state {
struct input_handle handle;
@@ -608,6 +654,8 @@ struct sysrq_state {
unsigned long key_down[BITS_TO_LONGS(KEY_CNT)];
unsigned int alt;
unsigned int alt_use;
+ unsigned int shift;
+ unsigned int shift_use;
bool active;
bool need_reinject;
bool reinjecting;
@@ -792,10 +840,22 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
}
break;
+ case KEY_LEFTSHIFT:
+ case KEY_RIGHTSHIFT:
+ if (!value)
+ sysrq->shift = KEY_RESERVED;
+ else if (value != 2)
+ sysrq->shift = code;
+ if (sysrq->active)
+ sysrq->shift_use = sysrq->shift;
+ break;
+
case KEY_SYSRQ:
if (value == 1 && sysrq->alt != KEY_RESERVED) {
sysrq->active = true;
sysrq->alt_use = sysrq->alt;
+ /* either RESERVED (for released) or actual code */
+ sysrq->shift_use = sysrq->shift;
/*
* If nothing else will be pressed we'll need
* to re-inject Alt-SysRq keysroke.
@@ -818,8 +878,12 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
default:
if (sysrq->active && value && value != 2) {
+ unsigned char c = sysrq_xlate[code];
+
sysrq->need_reinject = false;
- __handle_sysrq(sysrq_xlate[code], true);
+ if (sysrq->shift_use != KEY_RESERVED)
+ c = toupper(c);
+ __handle_sysrq(c, true);
}
break;
}
@@ -1046,9 +1110,10 @@ int sysrq_toggle_support(int enable_mask)
return 0;
}
+EXPORT_SYMBOL_GPL(sysrq_toggle_support);
-static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p,
- struct sysrq_key_op *remove_op_p)
+static int __sysrq_swap_key_ops(int key, const struct sysrq_key_op *insert_op_p,
+ const struct sysrq_key_op *remove_op_p)
{
int retval;
@@ -1071,13 +1136,13 @@ static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p,
return retval;
}
-int register_sysrq_key(int key, struct sysrq_key_op *op_p)
+int register_sysrq_key(int 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, struct sysrq_key_op *op_p)
+int unregister_sysrq_key(int key, const struct sysrq_key_op *op_p)
{
return __sysrq_swap_key_ops(key, NULL, op_p);
}