Magic SysRq Patch

Martin Mares (mj@atrey.karlin.mff.cuni.cz)
Sat, 5 Apr 1997 12:12:05 +0200


Hello, world!

I've looked at the keyboard patch posted to the list yesterday by Pavel
Machek and I've digged out some of my earlier patches and merged in some
of Pavel's ideas. The result is a much better "Magic SysRq Key" patch
offering a bit more and working in a much cleaner way.

Main features:

- Everything must be turned on by a compile-time option

- The Magic key is SysRq (Alt+PrintScreen). While holding this key, the
normal keys switch their meaning to Kernel Hacker's Functions:

- controlling the console logging level

- killing raw keyboard mode

- aliases for SAK, showpc, showtasks and showmem working even
if the keymap is broken (handy if you are playing with the
keyboard driver so much as I do)

- direct system reboot, halt and power off

- terminating and killing of all processes (maybe incl. init)

- syncing of all filesystems (hard drives first -- very handy if
you're debugging a block device) [the syncing technique is a
bit strange -- I must ask kflushd to call my own function,
the original Pavel's strategy of changing kflushd parameters
did't work well as it didn't flush inodes and superblocks]

- remounting all filesystems read only (hard drives first)

Share and Enjoy
Martin

diff -ruN linux-orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-orig/Documentation/Configure.help Fri Apr 4 22:39:38 1997
+++ linux/Documentation/Configure.help Sat Apr 5 11:38:11 1997
@@ -4425,6 +4425,17 @@
said Y to "Kernel profiling support", you must be a kernel hacker and
hence you know what this is about :-)

+Magic System Request Key support
+CONFIG_MAGIC_SYSRQ
+ This is for kernel hackers who want to have some control over the
+ system even if the system crashes during kernel debugging (e.g., to
+ flush the disks, reboot the system immediately or dump some status
+ information). This is accomplished by pressing various keys while
+ holding SysRq (Alt+PrintScreen). As you are expected to be a kernel
+ hacker to use this, the simple rule about learning what do the keys
+ mean is "Use the source, Luke!" -- read drivers/char/kbd_sysrq.c.
+ Don't say Y unless you really know what this hack does.
+
ISDN subsystem
CONFIG_ISDN
ISDN ("Integrated Services Digital Networks", called RNIS in France)
diff -ruN linux-orig/arch/i386/config.in linux/arch/i386/config.in
--- linux-orig/arch/i386/config.in Fri Apr 4 22:37:50 1997
+++ linux/arch/i386/config.in Fri Apr 4 22:44:14 1997
@@ -111,4 +111,5 @@
if [ "$CONFIG_PROFILE" = "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
fi
+bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
endmenu
diff -ruN linux-orig/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-orig/arch/i386/defconfig Fri Apr 4 22:38:06 1997
+++ linux/arch/i386/defconfig Fri Apr 4 22:44:28 1997
@@ -229,3 +229,4 @@
# Kernel hacking
#
# CONFIG_PROFILE is not set
+# CONFIG_MAGIC_SYSRQ is not set
diff -ruN linux-orig/drivers/char/Makefile linux/drivers/char/Makefile
--- linux-orig/drivers/char/Makefile Fri Apr 4 22:35:38 1997
+++ linux/drivers/char/Makefile Fri Apr 4 22:45:39 1997
@@ -36,6 +36,9 @@

ifndef CONFIG_SUN_KEYBOARD
L_OBJS += keyboard.o defkeymap.o
+ifdef CONFIG_MAGIC_SYSRQ
+L_OBJS += kbd_sysrq.o
+endif
endif

ifeq ($(CONFIG_DIGI),y)
diff -ruN linux-orig/drivers/char/kbd_sysrq.c linux/drivers/char/kbd_sysrq.c
--- linux-orig/drivers/char/kbd_sysrq.c Thu Jan 1 01:00:00 1970
+++ linux/drivers/char/kbd_sysrq.c Sat Apr 5 11:33:38 1997
@@ -0,0 +1,237 @@
+/* -*- linux-c -*-
+ *
+ * Linux Magic System Request Key Hacks
+ *
+ * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ * based on ideas by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <asm/ptrace.h>
+#include <asm/smp_lock.h>
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
+
+#include "kbd_kern.h"
+
+extern void hard_reset_now(void);
+extern void wakeup_bdflush(int);
+extern void reset_vc(unsigned int);
+extern int emergency_sync_scheduled; /* 1=sync, 2=remount */
+extern int console_loglevel;
+extern struct vfsmount *vfsmntlist;
+
+#ifdef __sparc__
+extern void halt_now(void);
+#endif
+
+/* Send 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->pid && p->mm != &init_mm) { /* Not swapper nor kernel thread */
+ if (p->pid == 1 && even_init) /* Ugly hack to kill init */
+ p->pid = 0x8000;
+ force_sig(sig, p);
+ }
+ }
+
+ if (console_loglevel < 7) /* We might have killed syslogd */
+ console_loglevel = 7;
+}
+
+/*
+ * 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)
+{
+ printk(KERN_INFO "SysRq: ");
+ switch (key) {
+ case 19: /* R -- Reset raw mode */
+ kbd->kbdmode = VC_XLATE;
+ printk("Keyboard mode set to XLATE\n");
+ break;
+ case 30: /* A -- SAK */
+ printk("SAK\n");
+ do_SAK(tty);
+ reset_vc(fg_console);
+ break;
+ case 48: /* B -- boot immediately */
+ printk("Resetting\n");
+ hard_reset_now();
+ break;
+#ifdef __sparc__
+ case 35: /* H -- halt immediately */
+ printk("Halting\n");
+ halt_now();
+ break;
+#endif
+#ifdef CONFIG_APM
+ case 24: /* O -- power off */
+ printk("Power off\n");
+ apm_set_power_state(APM_STATE_OFF);
+ break;
+#endif
+ case 31: /* S -- emergency sync */
+ printk("Emergency Sync\n");
+ emergency_sync_scheduled = 1;
+ wakeup_bdflush(0);
+ break;
+ case 22: /* U -- emergency remount R/O */
+ printk("Emergency Remount R/O\n");
+ emergency_sync_scheduled = 2;
+ wakeup_bdflush(0);
+ break;
+ case 25: /* P -- show PC */
+ printk("Show Regs\n");
+ if (pt_regs)
+ show_regs(pt_regs);
+ break;
+ case 20: /* T -- show task info */
+ printk("Show State\n");
+ show_state();
+ break;
+ case 50: /* M -- show memory info */
+ printk("Show Memory\n");
+ show_mem();
+ break;
+ case 2: /* 1 -- set console logging to max */
+ printk("Maximum Logging\n");
+ console_loglevel = 8;
+ break;
+ case 11: /* 0 -- set console logging to normal */
+ printk("Normal Logging\n");
+ console_loglevel = 7;
+ break;
+ case 18: /* E -- terminate all user processes */
+ printk("Terminate All Tasks\n");
+ send_sig_all(SIGTERM, 0);
+ break;
+ case 37: /* K -- kill all user processes */
+ printk("Kill All Tasks\n");
+ send_sig_all(SIGKILL, 0);
+ break;
+ case 38: /* L -- kill all processes including init */
+ printk("Kill ALL Tasks (even init)\n");
+ send_sig_all(SIGKILL, 1);
+ break;
+ default: /* Unknown: help */
+ printk("unRaw sAk Boot "
+#ifdef __sparc__
+ "Halt "
+#endif
+#ifdef CONFIG_APM
+ "Off "
+#endif
+ "Sync Unmount showPc showTasks showMem debuglog1/0 tErm Kill killalL\n");
+ }
+}
+
+/* Aux routines for the syncer */
+
+static void all_files_read_only(void) /* Kill write permissions of all files */
+{
+ struct file *file;
+ int i;
+
+ for (file = first_file, i = 0; i < nr_files; i++, file = file->f_next)
+ if (file->f_inode && file->f_count && S_ISREG(file->f_inode->i_mode))
+ file->f_mode &= ~2;
+}
+
+static int is_local_disk(kdev_t dev) /* Guess if the device is a local hard drive */
+{
+ unsigned int major = MAJOR(dev);
+
+ switch (major) {
+ case IDE0_MAJOR:
+ case IDE1_MAJOR:
+ case IDE2_MAJOR:
+ case IDE3_MAJOR:
+ case SCSI_DISK_MAJOR:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void go_sync(kdev_t dev, int remount_flag)
+{
+ printk(KERN_INFO "%sing device %04x ... ",
+ remount_flag ? "Remount" : "Sync",
+ dev);
+
+ if (remount_flag) { /* Remount R/O */
+ struct super_block *sb = get_super(dev);
+ struct vfsmount *vfsmnt;
+ int ret, flags;
+
+ if (!sb) {
+ printk("Superblock not found\n");
+ return;
+ }
+ if (sb->s_flags & MS_RDONLY)
+ return;
+ quota_off(dev, -1);
+ fsync_dev(dev);
+ flags = MS_RDONLY;
+ if (sb->s_op && sb->s_op->remount_fs) {
+ ret = sb->s_op->remount_fs(sb, &flags, NULL);
+ if (ret)
+ printk("error %d\n", ret);
+ else {
+ sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
+ if ((vfsmnt = lookup_vfsmnt(sb->s_dev)))
+ vfsmnt->mnt_flags = sb->s_flags;
+ printk("OK\n");
+ }
+ } else
+ printk("nothing to do\n");
+ } else {
+ fsync_dev(dev); /* Sync only */
+ printk("OK\n");
+ }
+}
+
+/*
+ * 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.
+ * We process all mounted hard drives first to recover from crashed experimental
+ * block devices and malfunctional network filesystems.
+ */
+
+void do_emergency_sync(void)
+{
+ struct vfsmount *mnt;
+ int remount_flag = (emergency_sync_scheduled == 2);
+
+ lock_kernel();
+
+ if (remount_flag)
+ all_files_read_only();
+
+ for (mnt = vfsmntlist; mnt; mnt = mnt->mnt_next)
+ if (is_local_disk(mnt->mnt_dev))
+ go_sync(mnt->mnt_dev, remount_flag);
+
+ for (mnt = vfsmntlist; mnt; mnt = mnt->mnt_next)
+ if (!is_local_disk(mnt->mnt_dev) && MAJOR(mnt->mnt_dev))
+ go_sync(mnt->mnt_dev, remount_flag);
+
+ unlock_kernel();
+ printk(KERN_INFO "Done.\n");
+}
diff -ruN linux-orig/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- linux-orig/drivers/char/keyboard.c Fri Apr 4 22:35:38 1997
+++ linux/drivers/char/keyboard.c Sat Apr 5 10:32:57 1997
@@ -13,6 +13,7 @@
* dynamic function/string keys, led setting, Sept 1994
* `Sticky' modifier keys, 951006.
* 11-11-96: SAK should now work in the raw mode (Martin Mares)
+ * 04-04-97: Added support for the magic SysRq key (Martin Mares)
*/

#define KEYBOARD_IRQ 1
@@ -163,6 +164,12 @@
/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
static struct pt_regs * pt_regs;

+#ifdef CONFIG_MAGIC_SYSRQ
+#define SYSRQ_KEY 0x54
+extern void handle_sysrq(int, struct pt_regs *, struct kbd_struct *, struct tty_struct *);
+static int sysrq_pressed;
+#endif
+
static inline void kb_wait(void)
{
int i;
@@ -510,6 +517,18 @@
put_queue(keycode + up_flag);
raw_mode = 1; /* Most key classes will be ignored */
}
+
+#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq hacks */
+
+ if (keycode == SYSRQ_KEY) { /* SysRq */
+ sysrq_pressed = !up_flag;
+ return;
+ } else if (sysrq_pressed) { /* SysRq + anything */
+ if (!up_flag)
+ handle_sysrq(keycode, pt_regs, kbd, tty);
+ return;
+ }
+#endif

/*
* Small change in philosophy: earlier we defined repetition by
diff -ruN linux-orig/fs/buffer.c linux/fs/buffer.c
--- linux-orig/fs/buffer.c Fri Apr 4 22:30:48 1997
+++ linux/fs/buffer.c Sat Apr 5 00:32:24 1997
@@ -71,8 +71,6 @@
* remove any of the parameters, make sure to update kernel/sysctl.c.
*/

-static void wakeup_bdflush(int);
-
#define N_PARAM 9

/* the dummy values in this structure are left in there for compatibility
@@ -102,6 +100,15 @@
int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 100, 100, 1, 1};
int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};

+void wakeup_bdflush(int);
+
+/* Hacks for Emergency Sync */
+
+#ifdef CONFIG_MAGIC_SYSRQ
+extern void do_emergency_sync(void);
+int emergency_sync_scheduled = 0;
+#endif
+
/*
* Rewrote the wait-routines to use the "new" wait-queue functionality,
* and getting rid of the cli-sti pairs. The wait-queue routines still
@@ -1469,7 +1476,7 @@
struct wait_queue * bdflush_done = NULL;
struct task_struct *bdflush_tsk = 0;

-static void wakeup_bdflush(int wait)
+void wakeup_bdflush(int wait)
{
if (current == bdflush_tsk)
return;
@@ -1640,7 +1647,14 @@
#ifdef DEBUG
printk("bdflush() activated...");
#endif
-
+
+#ifdef CONFIG_MAGIC_SYSRQ
+ if (emergency_sync_scheduled) {
+ do_emergency_sync();
+ emergency_sync_scheduled = 0;
+ }
+#endif
+
ncount = 0;
#ifdef DEBUG
for(nlist = 0; nlist < NR_LIST; nlist++)
diff -ruN linux-orig/fs/super.c linux/fs/super.c
--- linux-orig/fs/super.c Fri Apr 4 22:30:49 1997
+++ linux/fs/super.c Sat Apr 5 00:18:25 1997
@@ -59,8 +59,8 @@

struct super_block super_blocks[NR_SUPER];
static struct file_system_type *file_systems = (struct file_system_type *) NULL;
-static struct vfsmount *vfsmntlist = (struct vfsmount *) NULL,
- *vfsmnttail = (struct vfsmount *) NULL,
+struct vfsmount *vfsmntlist = (struct vfsmount *) NULL;
+static struct vfsmount *vfsmnttail = (struct vfsmount *) NULL,
*mru_vfsmnt = (struct vfsmount *) NULL;

/*
@@ -433,7 +433,7 @@
}
}

-static struct super_block * get_super(kdev_t dev)
+struct super_block * get_super(kdev_t dev)
{
struct super_block * s;

diff -ruN linux-orig/include/linux/fs.h linux/include/linux/fs.h
--- linux-orig/include/linux/fs.h Fri Apr 4 22:31:52 1997
+++ linux/include/linux/fs.h Sat Apr 5 00:19:17 1997
@@ -659,6 +659,7 @@
extern int generic_file_mmap(struct inode *, struct file *, struct vm_area_struct *);
extern long generic_file_read(struct inode *, struct file *, char *, unsigned long);

+extern struct super_block *get_super(kdev_t dev);
extern void put_super(kdev_t dev);
unsigned long generate_cluster(kdev_t dev, int b[], int size);
unsigned long generate_cluster_swab32(kdev_t dev, int b[], int size);