Skip to content

Commit

Permalink
tty/sysrq: Add configurable handler to execute a compound action
Browse files Browse the repository at this point in the history
Userland might want to execute e.g. 'w' (show blocked tasks), followed
by 's' (sync), followed by 1000 ms delay and then followed by 'c' (crash)
upon a single magic SysRq. Or one might want to execute the famous "Raising
Elephants Is So Utterly Boring" action. This patch adds a configurable
handler, triggered with 'C', for this exact purpose. The user specifies the
composition of the compound action using syntax similar to getopt, where
each letter corresponds to an individual action and a colon followed by a
number corresponds to a delay of that many milliseconds, e.g.:

ws:1000c

or

r:100eis:1000ub

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
  • Loading branch information
andrzejtp authored and intel-lab-lkp committed Sep 29, 2021
1 parent 8daa57f commit c2e145d
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 1 deletion.
9 changes: 9 additions & 0 deletions Documentation/admin-guide/sysrq.rst
Expand Up @@ -32,6 +32,7 @@ to 1. Here is the list of possible values in /proc/sys/kernel/sysrq:
64 = 0x40 - enable signalling of processes (term, kill, oom-kill)
128 = 0x80 - allow reboot/poweroff
256 = 0x100 - allow nicing of all RT tasks
512 = 0x200 - allow compound action

You can set the value in the file by the following command::

Expand Down Expand Up @@ -148,6 +149,14 @@ Command Function

``z`` Dump the ftrace buffer

``C`` Execute a predefined, compound action. The action is defined with
sysrq.sysrq_compound_action module parameter, whose value contains known
command keys (except ``C`` to prevent recursion). The command keys can
be optionally followed by a colon and a number of milliseconds to wait
after executing the last action. For example:

sysrq.sysrq_compound_action=r:100eis:1000ub

``0``-``9`` Sets the console log level, controlling which kernel messages
will be printed to your console. (``0``, for example would make
it so that only emergency messages like PANICs or OOPSes would
Expand Down
81 changes: 80 additions & 1 deletion drivers/tty/sysrq.c
Expand Up @@ -19,6 +19,7 @@
#include <linux/sched/rt.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
Expand Down Expand Up @@ -439,6 +440,15 @@ static const struct sysrq_key_op sysrq_unrt_op = {
.enable_mask = SYSRQ_ENABLE_RTNICE,
};

static void sysrq_action_compound(int key);

static struct sysrq_key_op sysrq_action_compound_op = {
.handler = sysrq_action_compound,
.help_msg = "execute-compound-action(C)",
.action_msg = "Execute compound action",
.enable_mask = SYSRQ_ENABLE_COMPOUND,
};

/* Key Operations table and lock */
static DEFINE_SPINLOCK(sysrq_key_table_lock);

Expand Down Expand Up @@ -501,7 +511,7 @@ static const struct sysrq_key_op *sysrq_key_table[62] = {
&sysrq_ftrace_dump_op, /* z */
NULL, /* A */
NULL, /* B */
NULL, /* C */
&sysrq_action_compound_op, /* C */
NULL, /* D */
NULL, /* E */
NULL, /* F */
Expand Down Expand Up @@ -634,6 +644,7 @@ EXPORT_SYMBOL(handle_sysrq);

#ifdef CONFIG_INPUT
static int sysrq_reset_downtime_ms;
static char *sysrq_compound_action;

/* Simple translation table for the SysRq keys */
static const unsigned char sysrq_xlate[KEY_CNT] =
Expand Down Expand Up @@ -787,6 +798,61 @@ static void sysrq_of_get_keyreset_config(void)
{
}
#endif
#define SYSRQ_COMPOUND_ACTION_VALIDATE 0
#define SYSRQ_COMPOUND_ACTION_RUN 1

static int sysrq_process_compound_action(int pass)
{
const char *action = sysrq_compound_action;
const struct sysrq_key_op *op_p;
int ret, delay;

while (*action) {
op_p = __sysrq_get_key_op(*action);
if (!op_p)
return -EINVAL;

/* Don't allow calling ourselves recursively */
if (op_p == &sysrq_action_compound_op)
return -EINVAL;

if (pass == SYSRQ_COMPOUND_ACTION_RUN)
__handle_sysrq(*action, false);

if (*++action == ':') {
ret = sscanf(action++, ":%d", &delay);
if (ret < 1) /* we want at least ":[0-9]" => 1 item */
return -EINVAL;

while (*action >= '0' && *action <= '9')
++action;
if (pass == SYSRQ_COMPOUND_ACTION_RUN)
mdelay(delay);
}
}

return 0;
}

static void sysrq_action_compound(int key)
{
if (!sysrq_compound_action) {
pr_err("Unconfigured compound action for %s",
sysrq_action_compound_op.help_msg);

return;
}

if (sysrq_process_compound_action(SYSRQ_COMPOUND_ACTION_VALIDATE)) {
pr_err("Incorrect compound action %s for %s",
sysrq_compound_action,
sysrq_action_compound_op.help_msg);

return;
}

sysrq_process_compound_action(SYSRQ_COMPOUND_ACTION_RUN);
}

static void sysrq_reinject_alt_sysrq(struct work_struct *work)
{
Expand Down Expand Up @@ -1079,8 +1145,21 @@ module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,

module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644);

module_param(sysrq_compound_action, charp, 0644);
MODULE_PARM_DESC(sysrq_compound_action,
"Compound sysrq action to be executed on Alt-Shift-SysRq-C\n"
"The compound action definition consists of known SysRq action letters except 'C',\n"
"each letter can be optionally followed by a colon and a number of milliseconds to wait\n"
"after executing the last action.\n"
"Example:\n"
"To unRaw, wait 100ms, tErminate, kIll, Sync, wait 1000ms, Unmount, Boot\n"
"sysrq.sysrq_compound_action=r:100eis:1000ub");
#else

{
}

static void sysrq_action_compound(int key)
static inline void sysrq_register_handler(void)
{
}
Expand Down
1 change: 1 addition & 0 deletions include/linux/sysrq.h
Expand Up @@ -28,6 +28,7 @@
#define SYSRQ_ENABLE_SIGNAL 0x0040
#define SYSRQ_ENABLE_BOOT 0x0080
#define SYSRQ_ENABLE_RTNICE 0x0100
#define SYSRQ_ENABLE_COMPOUND 0x0200

struct sysrq_key_op {
void (* const handler)(int);
Expand Down

0 comments on commit c2e145d

Please sign in to comment.