patch-2.4.10 linux/drivers/char/sysrq.c

Next file: linux/drivers/char/toshiba.c
Previous file: linux/drivers/char/synclink.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.9/linux/drivers/char/sysrq.c linux/drivers/char/sysrq.c
@@ -6,6 +6,10 @@
  *
  *	(c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
  *	based on ideas by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>
+ *
+ *	(c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
+ *	overhauled to use key registration
+ *	based upon discusions in irc://irc.openprojects.net/#kernelnewbies
  */
 
 #include <linux/config.h>
@@ -24,6 +28,8 @@
 #include <linux/smp_lock.h>
 #include <linux/module.h>
 
+#include <linux/spinlock.h>
+
 #include <asm/ptrace.h>
 
 extern void reset_vc(unsigned int);
@@ -35,136 +41,83 @@
 /* Machine specific power off function */
 void (*sysrq_power_off)(void);
 
-EXPORT_SYMBOL(sysrq_power_off);
-
-/* Send a signal to all user processes */
+/* Loglevel sysrq handler */
+static void sysrq_handle_loglevel(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	int i;
+	i = key - '0';
+	console_loglevel = 7;
+	printk("%d\n", i);
+	console_loglevel = i;
+}	
+static struct sysrq_key_op sysrq_loglevel_op = {
+	handler:	sysrq_handle_loglevel,
+	help_msg:	"loglevel0-8",
+	action_msg:	"Loglevel set to ",
+};
 
-static void send_sig_all(int sig, int even_init)
-{
-	struct task_struct *p;
 
-	for_each_task(p) {
-		if (p->mm) {				    /* Not swapper nor kernel thread */
-			if (p->pid == 1 && even_init)	    /* Ugly hack to kill init */
-				p->pid = 0x8000;
-			force_sig(sig, p);
-		}
-	}
+/* SAK sysrq handler */
+#ifdef CONFIG_VT
+static void sysrq_handle_SAK(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	if (tty)
+		do_SAK(tty);
+	reset_vc(fg_console);
 }
+static struct sysrq_key_op sysrq_SAK_op = {
+	handler:	sysrq_handle_SAK,
+	help_msg:	"saK",
+	action_msg:	"SAK\n",
+};
+#endif
 
-/*
- * This function is called by the keyboard handler when SysRq is pressed
- * and any other keycode arrives.
- */
 
-void handle_sysrq(int key, struct pt_regs *pt_regs,
-		  struct kbd_struct *kbd, struct tty_struct *tty)
-{
-	int orig_log_level = console_loglevel;
+/* unraw sysrq handler */
+static void sysrq_handle_unraw(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	if (kbd)
+		kbd->kbdmode = VC_XLATE;
+}
+static struct sysrq_key_op sysrq_unraw_op = {
+	handler:	sysrq_handle_unraw,
+	help_msg:	"unRaw",
+	action_msg:	"Keyboard mode set to XLATE\n",
+};
 
-	if (!sysrq_enabled)
-		return;
 
-	if (!key)
-		return;
+/* reboot sysrq handler */
+static void sysrq_handle_reboot(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	machine_restart(NULL);
+}
+static struct sysrq_key_op sysrq_reboot_op = {
+	handler:	sysrq_handle_reboot,
+	help_msg:	"reBoot",
+	action_msg:	"Resetting\n",
+};
 
-	console_loglevel = 7;
-	printk(KERN_INFO "SysRq: ");
-	switch (key) {
-	case 'r':					    /* R -- Reset raw mode */
-		if (kbd) {
-			kbd->kbdmode = VC_XLATE;
-			printk("Keyboard mode set to XLATE\n");
-		}
-		break;
-#ifdef CONFIG_VT
-	case 'k':					    /* K -- SAK */
-		printk("SAK\n");
-		if (tty)
-			do_SAK(tty);
-		reset_vc(fg_console);
-		break;
-#endif
-	case 'b':					    /* B -- boot immediately */
-		printk("Resetting\n");
-		machine_restart(NULL);
-		break;
-	case 'o':					    /* O -- power off */
-		if (sysrq_power_off) {
-			printk("Power off\n");
-			sysrq_power_off();
-		}
-		break;
-	case 's':					    /* S -- emergency sync */
-		printk("Emergency Sync\n");
-		emergency_sync_scheduled = EMERG_SYNC;
-		wakeup_bdflush();
-		break;
-	case 'u':					    /* U -- emergency remount R/O */
-		printk("Emergency Remount R/O\n");
-		emergency_sync_scheduled = EMERG_REMOUNT;
-		wakeup_bdflush();
-		break;
-	case 'p':					    /* P -- show PC */
-		printk("Show Regs\n");
-		if (pt_regs)
-			show_regs(pt_regs);
-		break;
-	case 't':					    /* T -- show task info */
-		printk("Show State\n");
-		show_state();
-		break;
-	case 'm':					    /* M -- show memory info */
-		printk("Show Memory\n");
-		show_mem();
-		break;
-	case '0' ... '9':				    /* 0-9 -- set console logging level */
-		orig_log_level = key - '0';
-		printk("Log level set to %d\n", orig_log_level);
-		break;
-	case 'e':					    /* E -- terminate all user processes */
-		printk("Terminate All Tasks\n");
-		send_sig_all(SIGTERM, 0);
-		orig_log_level = 8;			    /* We probably have killed syslogd */
-		break;
-	case 'i':					    /* I -- kill all user processes */
-		printk("Kill All Tasks\n");
-		send_sig_all(SIGKILL, 0);
-		orig_log_level = 8;
-		break;
-	case 'l':					    /* L -- kill all processes including init */
-		printk("Kill ALL Tasks (even init)\n");
-		send_sig_all(SIGKILL, 1);
-		orig_log_level = 8;
-		break;
-	default:					    /* Unknown: help */
-		if (kbd)
-			printk("unRaw ");
-#ifdef CONFIG_VT
-		if (tty)
-			printk("saK ");
-#endif
-		printk("Boot ");
-		if (sysrq_power_off)
-			printk("Off ");
-		printk("Sync Unmount showPc showTasks showMem loglevel0-8 tErm kIll killalL\n");
-		/* Don't use 'A' as it's handled specially on the Sparc */
-	}
 
-	console_loglevel = orig_log_level;
-}
 
-/* Aux routines for the syncer */
+/* SYNC SYSRQ HANDLERS BLOCK */
 
-static int is_local_disk(kdev_t dev)	    /* Guess if the device is a local hard drive */
-{
-	unsigned int major = MAJOR(dev);
+/* do_emergency_sync helper function */
+/* Guesses if the device is a local hard drive */
+static int is_local_disk(kdev_t dev) {
+	unsigned int major;
+	major = MAJOR(dev);
 
 	switch (major) {
 	case IDE0_MAJOR:
 	case IDE1_MAJOR:
 	case IDE2_MAJOR:
 	case IDE3_MAJOR:
+	case IDE4_MAJOR:
+	case IDE5_MAJOR:
+	case IDE6_MAJOR:
+	case IDE7_MAJOR:
+	case IDE8_MAJOR:
+	case IDE9_MAJOR:
 	case SCSI_DISK0_MAJOR:
 	case SCSI_DISK1_MAJOR:
 	case SCSI_DISK2_MAJOR:
@@ -173,19 +126,24 @@
 	case SCSI_DISK5_MAJOR:
 	case SCSI_DISK6_MAJOR:
 	case SCSI_DISK7_MAJOR:
+	case XT_DISK_MAJOR:
 		return 1;
 	default:
 		return 0;
 	}
 }
 
+/* do_emergency_sync helper function */
 static void go_sync(struct super_block *sb, int remount_flag)
 {
+	int orig_loglevel;
+	orig_loglevel = console_loglevel;
+	console_loglevel = 7;
 	printk(KERN_INFO "%sing device %s ... ",
 	       remount_flag ? "Remount" : "Sync",
 	       kdevname(sb->s_dev));
 
-	if (remount_flag) {				    /* Remount R/O */
+	if (remount_flag) { /* Remount R/O */
 		int ret, flags;
 		struct list_head *p;
 
@@ -215,12 +173,12 @@
 			}
 		} else
 			printk("nothing to do\n");
-	} else {
-		fsync_dev(sb->s_dev);			    /* Sync only */
+	} else { /* Sync only */
+		fsync_dev(sb->s_dev);
 		printk("OK\n");
 	}
+	console_loglevel = orig_loglevel;
 }
-
 /*
  * Emergency Sync or Unmount. We cannot do it directly, so we set a special
  * flag and wake up the bdflush kernel thread which immediately calls this function.
@@ -230,10 +188,10 @@
 
 int emergency_sync_scheduled;
 
-void do_emergency_sync(void)
-{
+void do_emergency_sync(void) {
 	struct super_block *sb;
 	int remount_flag;
+	int orig_loglevel;
 
 	lock_kernel();
 	remount_flag = (emergency_sync_scheduled == EMERG_REMOUNT);
@@ -252,5 +210,277 @@
 			go_sync(sb, remount_flag);
 
 	unlock_kernel();
+
+	orig_loglevel = console_loglevel;
+	console_loglevel = 7;
 	printk(KERN_INFO "Done.\n");
+	console_loglevel = orig_loglevel;
+}
+
+static void sysrq_handle_sync(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	emergency_sync_scheduled = EMERG_SYNC;
+	wakeup_bdflush();
+}
+static struct sysrq_key_op sysrq_sync_op = {
+	handler:	sysrq_handle_sync,
+	help_msg:	"Sync",
+	action_msg:	"Emergency Sync\n",
+};
+
+static void sysrq_handle_mountro(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	emergency_sync_scheduled = EMERG_REMOUNT;
+	wakeup_bdflush();
+}
+static struct sysrq_key_op sysrq_mountro_op = {
+	handler:	sysrq_handle_mountro,
+	help_msg:	"Unmount",
+	action_msg:	"Emergency Remount R/0\n",
+};
+
+/* END SYNC SYSRQ HANDLERS BLOCK */
+
+
+/* SHOW SYSRQ HANDLERS BLOCK */
+
+static void sysrq_handle_showregs(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	if (pt_regs)
+		show_regs(pt_regs);
+}
+static struct sysrq_key_op sysrq_showregs_op = {
+	handler:	sysrq_handle_showregs,
+	help_msg:	"showPc",
+	action_msg:	"Show Regs\n",
+};
+
+
+static void sysrq_handle_showstate(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	show_state();
+}
+static struct sysrq_key_op sysrq_showstate_op = {
+	handler:	sysrq_handle_showstate,
+	help_msg:	"showTasks",
+	action_msg:	"Show State\n",
+};
+
+
+static void sysrq_handle_showmem(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	show_mem();
+}
+static struct sysrq_key_op sysrq_showmem_op = {
+	handler:	sysrq_handle_showmem,
+	help_msg:	"showMem",
+	action_msg:	"Show Memory\n",
+};
+
+/* SHOW SYSRQ HANDLERS BLOCK */
+
+
+/* SIGNAL SYSRQ HANDLERS BLOCK */
+
+/* signal sysrq helper function
+ * Sends a signal to all user processes */
+static void send_sig_all(int sig, int even_init)
+{
+	struct task_struct *p;
+
+	for_each_task(p) {
+		if (p->mm) { /* Not swapper nor kernel thread */
+			if (p->pid == 1 && even_init)
+				/* Ugly hack to kill init */
+				p->pid = 0x8000;
+			if (p->pid != 1)
+				force_sig(sig, p);
+		}
+	}
+}
+
+static void sysrq_handle_term(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	send_sig_all(SIGTERM, 0);
+	console_loglevel = 8;
+}
+static struct sysrq_key_op sysrq_term_op = {
+	handler:	sysrq_handle_term,
+	help_msg:	"tErm",
+	action_msg:	"Terminate All Tasks\n",
+};
+
+static void sysrq_handle_kill(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	send_sig_all(SIGKILL, 0);
+	console_loglevel = 8;
+}
+static struct sysrq_key_op sysrq_kill_op = {
+	handler:	sysrq_handle_kill,
+	help_msg:	"kIll",
+	action_msg:	"Kill All Tasks\n",
+};
+
+static void sysrq_handle_killall(int key, struct pt_regs *pt_regs,
+		struct kbd_struct *kbd, struct tty_struct *tty) {
+	send_sig_all(SIGKILL, 1);
+	console_loglevel = 8;
+}
+static struct sysrq_key_op sysrq_killall_op = {
+	handler:	sysrq_handle_killall,
+	help_msg:	"killalL",
+	action_msg:	"Kill All Tasks (even init)\n",
+};
+
+/* END SIGNAL SYSRQ HANDLERS BLOCK */
+
+
+/* Key Operations table and lock */
+spinlock_t sysrq_key_table_lock = SPIN_LOCK_UNLOCKED;
+#define SYSRQ_KEY_TABLE_LENGTH 36
+static struct sysrq_key_op *sysrq_key_table[SYSRQ_KEY_TABLE_LENGTH] = {
+/* 0 */	&sysrq_loglevel_op,
+/* 1 */	&sysrq_loglevel_op,
+/* 2 */	&sysrq_loglevel_op,
+/* 3 */	&sysrq_loglevel_op,
+/* 4 */	&sysrq_loglevel_op,
+/* 5 */	&sysrq_loglevel_op,
+/* 6 */	&sysrq_loglevel_op,
+/* 7 */	&sysrq_loglevel_op,
+/* 8 */	&sysrq_loglevel_op,
+/* 9 */	&sysrq_loglevel_op,
+/* a */	NULL, /* Don't use for system provided sysrqs,
+		 it is handled specially on the spark
+		 and will never arive */
+/* b */	&sysrq_reboot_op,
+/* c */	NULL,
+/* d */	NULL,
+/* e */	&sysrq_term_op,
+/* f */	NULL,
+/* g */	NULL,
+/* h */	NULL,
+/* i */	&sysrq_kill_op,
+/* j */	NULL,
+#ifdef CONFIG_VT
+/* k */	&sysrq_SAK_op,
+#else
+/* k */	NULL,
+#endif
+/* l */	&sysrq_killall_op,
+/* m */	&sysrq_showmem_op,
+/* n */	NULL,
+/* o */	NULL, /* This will often be registered
+		 as 'Off' at init time */
+/* p */	&sysrq_showregs_op,
+/* q */	NULL,
+/* r */	&sysrq_unraw_op,
+/* s */	&sysrq_sync_op,
+/* t */	&sysrq_showstate_op,
+/* u */	&sysrq_mountro_op,
+/* v */	NULL,
+/* w */	NULL,
+/* x */	NULL,
+/* w */	NULL,
+/* z */	NULL
+};
+
+/* key2index calculation, -1 on invalid index */
+static __inline__ int sysrq_key_table_key2index(int key) {
+	int retval;
+	if ((key >= '0') & (key <= '9')) {
+		retval = key - '0';
+	} else if ((key >= 'a') & (key <= 'z')) {
+		retval = key + 10 - 'a';
+	} else {
+		retval = -1;
+	}
+	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.
+ */
+
+struct sysrq_key_op *__sysrq_get_key_op (int key) {
+        struct sysrq_key_op *op_p;
+        int i;
+	
+	i = sysrq_key_table_key2index(key);
+        op_p = (i == -1) ? NULL : sysrq_key_table[i];
+        return op_p;
 }
+
+void __sysrq_put_key_op (int key, struct sysrq_key_op *op_p) {
+        int i;
+
+	i = sysrq_key_table_key2index(key);
+        if (i != -1)
+                sysrq_key_table[i] = op_p;
+}
+
+/*
+ * This function is called by the keyboard handler when SysRq is pressed
+ * and any other keycode arrives.
+ */
+
+void handle_sysrq(int key, struct pt_regs *pt_regs,
+		  struct kbd_struct *kbd, struct tty_struct *tty) {
+	if (!sysrq_enabled)
+		return;
+
+	__sysrq_lock_table();
+	__handle_sysrq_nolock(key, pt_regs, kbd, tty);
+	__sysrq_unlock_table();
+}
+
+/*
+ * This is the non-locking version of handle_sysrq
+ * It must/can only be called by sysrq key handlers,
+ * as they are inside of the lock
+ */
+
+void __handle_sysrq_nolock(int key, struct pt_regs *pt_regs,
+		  struct kbd_struct *kbd, struct tty_struct *tty) {
+	struct sysrq_key_op *op_p;
+	int orig_log_level;
+	int i, j;
+	
+	if (!sysrq_enabled)
+		return;
+
+	orig_log_level = console_loglevel;
+	console_loglevel = 7;
+	printk(KERN_INFO "SysRq : ");
+
+        op_p = __sysrq_get_key_op(key);
+        if (op_p) {
+                printk ("%s", op_p->action_msg);
+                op_p->handler(key, pt_regs, kbd, tty);
+	} else {
+		printk("HELP : ");
+		/* Only print the help msg once per handler */
+		for (i=0; i<SYSRQ_KEY_TABLE_LENGTH; i++) 
+		if (sysrq_key_table[i]) {
+			for (j=0; sysrq_key_table[i] != sysrq_key_table[j]; j++);
+			if (j == i)
+				printk ("%s ", sysrq_key_table[i]->help_msg);
+		}
+		printk ("\n");
+	}
+	console_loglevel = orig_log_level;
+}
+
+EXPORT_SYMBOL(handle_sysrq);
+EXPORT_SYMBOL(__handle_sysrq_nolock);
+EXPORT_SYMBOL(__sysrq_lock_table);
+EXPORT_SYMBOL(__sysrq_unlock_table);
+EXPORT_SYMBOL(__sysrq_get_key_op);
+EXPORT_SYMBOL(__sysrq_put_key_op);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)