Re: [PATCH v4] drivers/tty: Folding Android's keyreset driver insysRQ

From: Dmitry Torokhov
Date: Mon Nov 19 2012 - 02:25:19 EST


Hi Mathieu,

On Sun, Nov 11, 2012 at 01:24:48PM -0700, mathieu.poirier@xxxxxxxxxx wrote:
> From: "Mathieu J. Poirier" <mathieu.poirier@xxxxxxxxxx>
>
> This patch adds keyreset functionality to the sysrq driver. It
> allows certain button/key combinations to be used in order to
> trigger device resets.
>
> The first time the key-combo is detected a work function that syncs
> the filesystems is scheduled and the kernel rebooted. If all the keys
> are released and then pressed again, it calls panic. Reboot on panic
> should be set for this to work.
>
> Redefining the '__weak sysrq_keyreset_get_params' function is required
> to trigger the feature. Alternatively keys can be passed to the
> driver via the "/sys/module/sysrq" interface.
>
> This functionality comes from the keyreset driver submitted by
> Arve Hjønnevåg in the Android kernel.

Thank you for making the changes. This still looks pretty complicated,
how about if we trim it a bit, like in the patch below.

Thanks.

--
Dmitry

Input: sysrq - allow specifying alternate reset sequence

From: Mathieu J. Poirier <mathieu.poirier@xxxxxxxxxx>

This patch adds keyreset functionality to the sysrq driver. It allows
certain button/key combinations to be used in order to trigger emergency
reboots.

Redefining the '__weak platform_sysrq_reset_seq' variable is required
to trigger the feature. Alternatively keys can be passed to the driver
via a module parameter.

This functionality comes from the keyreset driver submitted by
Arve Hjønnevåg in the Android kernel.

Signed-off-by: Mathieu Poirier <mathieu.poirier@xxxxxxxxxx>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx>
---
drivers/tty/sysrq.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)

diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 16ee6ce..9dddaf7 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -41,6 +41,7 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/uaccess.h>
+#include <linux/moduleparam.h>

#include <asm/ptrace.h>
#include <asm/irq_regs.h>
@@ -576,8 +577,73 @@ struct sysrq_state {
bool active;
bool need_reinject;
bool reinjecting;
+
+ /* reset sequence handling */
+ bool reset_canceled;
+ unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)];
+ int reset_seq_len;
+ int reset_seq_cnt;
+ int reset_seq_version;
};

+#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */
+static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX];
+static unsigned int sysrq_reset_seq_len;
+static unsigned int sysrq_reset_seq_version = 1;
+
+static void sysrq_parse_reset_sequence(struct sysrq_state *state)
+{
+ int i;
+ unsigned short key;
+
+ state->reset_seq_cnt = 0;
+
+ for (i = 0; i < sysrq_reset_seq_len; i++) {
+ key = sysrq_reset_seq[i];
+
+ if (key == KEY_RESERVED || key > KEY_MAX)
+ break;
+
+ __set_bit(key, state->reset_keybit);
+ state->reset_seq_len++;
+
+ if (test_bit(key, state->key_down))
+ state->reset_seq_cnt++;
+ }
+}
+
+static bool sysrq_detect_reset_sequence(struct sysrq_state *state,
+ unsigned int code, int value)
+{
+ if (state->reset_seq_version != sysrq_reset_seq_version) {
+ sysrq_parse_reset_sequence(state);
+ /* Disable reset until old keys are not released */
+ state->reset_canceled = state->reset_seq_cnt != 0;
+ state->reset_seq_version = sysrq_reset_seq_version;
+ }
+
+ if (!test_bit(code, state->reset_keybit)) {
+ /*
+ * Pressing any key _not_ in reset sequence cancels
+ * the reset sequence.
+ */
+ if (value && state->reset_seq_cnt)
+ state->reset_canceled = true;
+ } else if (value == 0) {
+ /* key release */
+ 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;
+ }
+ }
+
+ return false;
+}
+
static void sysrq_reinject_alt_sysrq(struct work_struct *work)
{
struct sysrq_state *sysrq =
@@ -690,6 +756,11 @@ static bool sysrq_filter(struct input_handle *handle,
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);
+ }
+
} else if (value == 0 &&
test_and_clear_bit(code, sysrq->key_down)) {
/*
@@ -785,7 +856,20 @@ static bool sysrq_handler_registered;

static inline void sysrq_register_handler(void)
{
+ extern unsigned short platform_sysrq_reset_seq[] __weak;
+ unsigned short key;
int error;
+ int i;
+
+ if (platform_sysrq_reset_seq) {
+ for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
+ key = platform_sysrq_reset_seq[i];
+ if (key == KEY_RESERVED || key > KEY_MAX)
+ break;
+
+ sysrq_reset_seq[sysrq_reset_seq_len++] = key;
+ }
+ }

error = input_register_handler(&sysrq_handler);
if (error)
@@ -802,6 +886,36 @@ static inline void sysrq_unregister_handler(void)
}
}

+static int sysrq_reset_seq_param_set(const char *buffer,
+ const struct kernel_param *kp)
+{
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buffer, 0, &val);
+ if (error < 0)
+ return error;
+
+ if (val > KEY_MAX)
+ return -EINVAL;
+
+ *((unsigned short *)kp->arg) = val;
+ sysrq_reset_seq_version++;
+
+ return 0;
+}
+
+static struct kernel_param_ops param_ops_sysrq_reset_seq = {
+ .get = param_get_ushort,
+ .set = sysrq_reset_seq_param_set,
+};
+
+#define param_check_sysrq_reset_seq(name, p) \
+ __param_check(name, p, unsigned short)
+
+module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
+ &sysrq_reset_seq_len, 0644);
+
#else

static inline void sysrq_register_handler(void)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/