Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
[cascardo/linux.git] / drivers / tty / sysrq.c
index 3687f0c..b51c154 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/input.h>
 #include <linux/uaccess.h>
 #include <linux/moduleparam.h>
+#include <linux/jiffies.h>
 
 #include <asm/ptrace.h>
 #include <asm/irq_regs.h>
@@ -51,6 +52,9 @@
 static int __read_mostly sysrq_enabled = 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;
@@ -101,7 +105,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 +121,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 +139,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 +152,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 +163,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 +175,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 +185,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 +198,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 +249,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 +264,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 +275,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 +286,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 +300,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 +314,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,
 };
@@ -341,7 +345,7 @@ static void sysrq_handle_term(int key)
 }
 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 +364,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 +376,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,
 };
@@ -385,7 +389,7 @@ static void sysrq_handle_kill(int key)
 }
 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 +400,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,
 };
@@ -586,6 +590,7 @@ struct sysrq_state {
        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,29 +624,51 @@ 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 dummy)
+{
+       __handle_sysrq(sysrq_xlate[KEY_B], false);
+}
+
+static void sysrq_handle_reset_request(struct sysrq_state *state)
+{
+       if (sysrq_reset_downtime_ms)
+               mod_timer(&state->keyreset_timer,
+                       jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms));
+       else
+               sysrq_do_reset(0);
+}
+
+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);
                }
        }
-
-       return false;
 }
 
 static void sysrq_reinject_alt_sysrq(struct work_struct *work)
@@ -748,10 +775,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 +837,7 @@ 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, 0);
 
        error = input_register_handle(&sysrq->handle);
        if (error) {
@@ -841,6 +867,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,8 +897,6 @@ 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;
@@ -931,6 +956,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)