linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / char / sysrq.c
index c9688f1..d58f823 100644 (file)
 #include <linux/suspend.h>
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>         /* for fsync_bdev() */
-
+#include <linux/swap.h>
 #include <linux/spinlock.h>
+#include <linux/vt_kern.h>
+#include <linux/workqueue.h>
+#include <linux/kexec.h>
 
 #include <asm/ptrace.h>
 
-extern void reset_vc(unsigned int);
-
 /* Whether we react on sysrq keys or just ignore them */
 int sysrq_enabled = 1;
 
-/* Machine specific power off function */
-void (*sysrq_power_off)(void);
-
 /* Loglevel sysrq handler */
 static void sysrq_handle_loglevel(int key, struct pt_regs *pt_regs,
                                  struct tty_struct *tty) 
@@ -58,6 +56,7 @@ static struct sysrq_key_op sysrq_loglevel_op = {
        .handler        = sysrq_handle_loglevel,
        .help_msg       = "loglevel0-8",
        .action_msg     = "Changing Loglevel",
+       .enable_mask    = SYSRQ_ENABLE_LOG,
 };
 
 
@@ -68,12 +67,13 @@ static void sysrq_handle_SAK(int key, struct pt_regs *pt_regs,
 {
        if (tty)
                do_SAK(tty);
-       reset_vc(fg_console);
+       reset_vc(vc_cons[fg_console].d);
 }
 static struct sysrq_key_op sysrq_SAK_op = {
        .handler        = sysrq_handle_SAK,
        .help_msg       = "saK",
        .action_msg     = "SAK",
+       .enable_mask    = SYSRQ_ENABLE_KEYBOARD,
 };
 #endif
 
@@ -91,31 +91,38 @@ static struct sysrq_key_op sysrq_unraw_op = {
        .handler        = sysrq_handle_unraw,
        .help_msg       = "unRaw",
        .action_msg     = "Keyboard mode set to XLATE",
+       .enable_mask    = SYSRQ_ENABLE_KEYBOARD,
 };
 #endif /* CONFIG_VT */
 
+#ifdef CONFIG_KEXEC
+/* crashdump sysrq handler */
+static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs,
+                               struct tty_struct *tty)
+{
+       crash_kexec(pt_regs);
+}
+static struct sysrq_key_op sysrq_crashdump_op = {
+       .handler        = sysrq_handle_crashdump,
+       .help_msg       = "Crashdump",
+       .action_msg     = "Trigger a crashdump",
+       .enable_mask    = SYSRQ_ENABLE_DUMP,
+};
+#endif
+
 /* reboot sysrq handler */
 static void sysrq_handle_reboot(int key, struct pt_regs *pt_regs,
                                struct tty_struct *tty) 
 {
-       machine_restart(NULL);
+       local_irq_enable();
+       emergency_restart();
 }
 
 static struct sysrq_key_op sysrq_reboot_op = {
        .handler        = sysrq_handle_reboot,
        .help_msg       = "reBoot",
        .action_msg     = "Resetting",
-};
-
-/* crash sysrq handler */
-static void sysrq_handle_crash(int key, struct pt_regs *pt_regs,
-                              struct tty_struct *tty) {
-       *( (char *) 0) = 0;
-}
-static struct sysrq_key_op sysrq_crash_op = {
-       handler:        sysrq_handle_crash,
-       help_msg:       "Crash",
-       action_msg:     "Crashing the kernel by request",
+       .enable_mask    = SYSRQ_ENABLE_BOOT,
 };
 
 static void sysrq_handle_sync(int key, struct pt_regs *pt_regs,
@@ -128,6 +135,7 @@ static struct sysrq_key_op sysrq_sync_op = {
        .handler        = sysrq_handle_sync,
        .help_msg       = "Sync",
        .action_msg     = "Emergency Sync",
+       .enable_mask    = SYSRQ_ENABLE_SYNC,
 };
 
 static void sysrq_handle_mountro(int key, struct pt_regs *pt_regs,
@@ -140,10 +148,26 @@ static struct sysrq_key_op sysrq_mountro_op = {
        .handler        = sysrq_handle_mountro,
        .help_msg       = "Unmount",
        .action_msg     = "Emergency Remount R/O",
+       .enable_mask    = SYSRQ_ENABLE_REMOUNT,
 };
 
 /* END SYNC SYSRQ HANDLERS BLOCK */
 
+#ifdef CONFIG_DEBUG_MUTEXES
+
+static void
+sysrq_handle_showlocks(int key, struct pt_regs *pt_regs, struct tty_struct *tty)
+{
+       mutex_debug_show_all_locks();
+}
+
+static struct sysrq_key_op sysrq_showlocks_op = {
+       .handler        = sysrq_handle_showlocks,
+       .help_msg       = "show-all-locks(D)",
+       .action_msg     = "Show Locks Held",
+};
+
+#endif
 
 /* SHOW SYSRQ HANDLERS BLOCK */
 
@@ -157,6 +181,7 @@ static struct sysrq_key_op sysrq_showregs_op = {
        .handler        = sysrq_handle_showregs,
        .help_msg       = "showPc",
        .action_msg     = "Show Regs",
+       .enable_mask    = SYSRQ_ENABLE_DUMP,
 };
 
 
@@ -169,6 +194,7 @@ static struct sysrq_key_op sysrq_showstate_op = {
        .handler        = sysrq_handle_showstate,
        .help_msg       = "showTasks",
        .action_msg     = "Show State",
+       .enable_mask    = SYSRQ_ENABLE_DUMP,
 };
 
 
@@ -181,6 +207,7 @@ static struct sysrq_key_op sysrq_showmem_op = {
        .handler        = sysrq_handle_showmem,
        .help_msg       = "showMem",
        .action_msg     = "Show Memory",
+       .enable_mask    = SYSRQ_ENABLE_DUMP,
 };
 
 /* SHOW SYSRQ HANDLERS BLOCK */
@@ -211,6 +238,25 @@ static struct sysrq_key_op sysrq_term_op = {
        .handler        = sysrq_handle_term,
        .help_msg       = "tErm",
        .action_msg     = "Terminate All Tasks",
+       .enable_mask    = SYSRQ_ENABLE_SIGNAL,
+};
+
+static void moom_callback(void *ignored)
+{
+       out_of_memory(&NODE_DATA(0)->node_zonelists[ZONE_NORMAL], GFP_KERNEL, 0);
+}
+
+static DECLARE_WORK(moom_work, moom_callback, NULL);
+
+static void sysrq_handle_moom(int key, struct pt_regs *pt_regs,
+                             struct tty_struct *tty)
+{
+       schedule_work(&moom_work);
+}
+static struct sysrq_key_op sysrq_moom_op = {
+       .handler        = sysrq_handle_moom,
+       .help_msg       = "Full",
+       .action_msg     = "Manual OOM execution",
 };
 
 static void sysrq_handle_kill(int key, struct pt_regs *pt_regs,
@@ -223,13 +269,25 @@ static struct sysrq_key_op sysrq_kill_op = {
        .handler        = sysrq_handle_kill,
        .help_msg       = "kIll",
        .action_msg     = "Kill All Tasks",
+       .enable_mask    = SYSRQ_ENABLE_SIGNAL,
 };
 
 /* END SIGNAL SYSRQ HANDLERS BLOCK */
 
+static void sysrq_handle_unrt(int key, struct pt_regs *pt_regs,
+                               struct tty_struct *tty)
+{
+       normalize_rt_tasks();
+}
+static struct sysrq_key_op sysrq_unrt_op = {
+       .handler        = sysrq_handle_unrt,
+       .help_msg       = "Nice",
+       .action_msg     = "Nice All RT Tasks",
+       .enable_mask    = SYSRQ_ENABLE_RTNICE,
+};
 
 /* Key Operations table and lock */
-static spinlock_t sysrq_key_table_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(sysrq_key_table_lock);
 #define SYSRQ_KEY_TABLE_LENGTH 36
 static struct sysrq_key_op *sysrq_key_table[SYSRQ_KEY_TABLE_LENGTH] = {
 /* 0 */        &sysrq_loglevel_op,
@@ -246,10 +304,18 @@ static struct sysrq_key_op *sysrq_key_table[SYSRQ_KEY_TABLE_LENGTH] = {
                 it is handled specially on the sparc
                 and will never arrive */
 /* b */        &sysrq_reboot_op,
-/* c */ &sysrq_crash_op,
-/* d */        NULL,
+#ifdef CONFIG_KEXEC
+/* c */ &sysrq_crashdump_op,
+#else
+/* c */        NULL,
+#endif
+#ifdef CONFIG_DEBUG_MUTEXES
+/* d */ &sysrq_showlocks_op,
+#else
+/* d */ NULL,
+#endif
 /* e */        &sysrq_term_op,
-/* f */        NULL,
+/* f */        &sysrq_moom_op,
 /* g */        NULL,
 /* h */        NULL,
 /* i */        &sysrq_kill_op,
@@ -261,7 +327,7 @@ static struct sysrq_key_op *sysrq_key_table[SYSRQ_KEY_TABLE_LENGTH] = {
 #endif
 /* l */        NULL,
 /* m */        &sysrq_showmem_op,
-/* n */        NULL,
+/* n */        &sysrq_unrt_op,
 /* o */        NULL, /* This will often be registered
                 as 'Off' at init time */
 /* p */        &sysrq_showregs_op,
@@ -294,14 +360,6 @@ static int sysrq_key_table_key2index(int key) {
        return retval;
 }
 
-/*
- * table lock and unlocking functions, exposed to modules
- */
-
-void __sysrq_lock_table (void) { spin_lock(&sysrq_key_table_lock); }
-
-void __sysrq_unlock_table (void) { spin_unlock(&sysrq_key_table_lock); }
-
 /*
  * get and put functions for the table, exposed to modules.
  */
@@ -315,7 +373,7 @@ struct sysrq_key_op *__sysrq_get_key_op (int key) {
         return op_p;
 }
 
-void __sysrq_put_key_op (int key, struct sysrq_key_op *op_p) {
+static void __sysrq_put_key_op (int key, struct sysrq_key_op *op_p) {
         int i;
 
        i = sysrq_key_table_key2index(key);
@@ -329,22 +387,30 @@ void __sysrq_put_key_op (int key, struct sysrq_key_op *op_p) {
  * as they are inside of the lock
  */
 
-void __handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty)
+void __handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty, int check_mask)
 {
        struct sysrq_key_op *op_p;
        int orig_log_level;
        int i, j;
+       unsigned long flags;
 
-       __sysrq_lock_table();
+       spin_lock_irqsave(&sysrq_key_table_lock, flags);
        orig_log_level = console_loglevel;
        console_loglevel = 7;
        printk(KERN_INFO "SysRq : ");
 
         op_p = __sysrq_get_key_op(key);
         if (op_p) {
-               printk ("%s\n", op_p->action_msg);
-               console_loglevel = orig_log_level;
-               op_p->handler(key, pt_regs, tty);
+               /* Should we check for enabled operations (/proc/sysrq-trigger should not)
+                * and is the invoked operation enabled? */
+               if (!check_mask || sysrq_enabled == 1 ||
+                   (sysrq_enabled & op_p->enable_mask)) {
+                       printk ("%s\n", op_p->action_msg);
+                       console_loglevel = orig_log_level;
+                       op_p->handler(key, pt_regs, tty);
+               }
+               else
+                       printk("This sysrq operation is disabled.\n");
        } else {
                printk("HELP : ");
                /* Only print the help msg once per handler */
@@ -357,7 +423,7 @@ void __handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty)
                printk ("\n");
                console_loglevel = orig_log_level;
        }
-       __sysrq_unlock_table();
+       spin_unlock_irqrestore(&sysrq_key_table_lock, flags);
 }
 
 /*
@@ -369,11 +435,37 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty)
 {
        if (!sysrq_enabled)
                return;
-       __handle_sysrq(key, pt_regs, tty);
+       __handle_sysrq(key, pt_regs, tty, 1);
+}
+
+static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p,
+                                struct sysrq_key_op *remove_op_p) {
+
+       int retval;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sysrq_key_table_lock, flags);
+       if (__sysrq_get_key_op(key) == remove_op_p) {
+               __sysrq_put_key_op(key, insert_op_p);
+               retval = 0;
+       } else {
+               retval = -1;
+       }
+       spin_unlock_irqrestore(&sysrq_key_table_lock, flags);
+
+       return retval;
+}
+
+int register_sysrq_key(int key, struct sysrq_key_op *op_p)
+{
+       return __sysrq_swap_key_ops(key, op_p, NULL);
+}
+
+int unregister_sysrq_key(int key, struct sysrq_key_op *op_p)
+{
+       return __sysrq_swap_key_ops(key, NULL, op_p);
 }
 
 EXPORT_SYMBOL(handle_sysrq);
-EXPORT_SYMBOL(__sysrq_lock_table);
-EXPORT_SYMBOL(__sysrq_unlock_table);
-EXPORT_SYMBOL(__sysrq_get_key_op);
-EXPORT_SYMBOL(__sysrq_put_key_op);
+EXPORT_SYMBOL(register_sysrq_key);
+EXPORT_SYMBOL(unregister_sysrq_key);