From: Jason Baron <jbaron@redhat.com>

The sysrq code is taking a spinlock from both interrupt and process context
in an unsafe and deadlocky manner.

Move all those inlined functions out of sysrq.h, into sysrq.c then withdraw
all those exported-to-modules helper functions then remove
__sysrq_trylock_table() altogether and then use spin_lock_irqsave() in the
appropriate places.

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/char/sysrq.c  |   47 +++++++++++++++++++++++++++++-------------
 25-akpm/include/linux/sysrq.h |   45 +---------------------------------------
 2 files changed, 35 insertions(+), 57 deletions(-)

diff -puN drivers/char/sysrq.c~fix-altsysrq-deadlock drivers/char/sysrq.c
--- 25/drivers/char/sysrq.c~fix-altsysrq-deadlock	Wed Oct 27 16:21:08 2004
+++ 25-akpm/drivers/char/sysrq.c	Wed Oct 27 16:21:08 2004
@@ -315,14 +315,6 @@ static int sysrq_key_table_key2index(int
 }
 
 /*
- * 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.
  */
 
@@ -354,8 +346,9 @@ void __handle_sysrq(int key, struct pt_r
 	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 : ");
@@ -377,7 +370,7 @@ void __handle_sysrq(int key, struct pt_r
 		printk ("\n");
 		console_loglevel = orig_log_level;
 	}
-	__sysrq_unlock_table();
+	spin_unlock_irqrestore(&sysrq_key_table_lock, flags);
 }
 
 /*
@@ -392,8 +385,34 @@ void handle_sysrq(int key, struct pt_reg
 	__handle_sysrq(key, pt_regs, tty);
 }
 
+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);
diff -puN include/linux/sysrq.h~fix-altsysrq-deadlock include/linux/sysrq.h
--- 25/include/linux/sysrq.h~fix-altsysrq-deadlock	Wed Oct 27 16:21:08 2004
+++ 25-akpm/include/linux/sysrq.h	Wed Oct 27 16:21:08 2004
@@ -31,49 +31,8 @@ struct sysrq_key_op {
 
 void handle_sysrq(int, struct pt_regs *, struct tty_struct *);
 void __handle_sysrq(int, struct pt_regs *, struct tty_struct *);
-
-/*
- * Sysrq registration manipulation functions
- */
-
-void __sysrq_lock_table (void);
-void __sysrq_unlock_table (void);
-struct sysrq_key_op *__sysrq_get_key_op (int key);
-void __sysrq_put_key_op (int key, struct sysrq_key_op *op_p);
-
-extern __inline__ int
-__sysrq_swap_key_ops_nolock(int key, struct sysrq_key_op *insert_op_p,
-				struct sysrq_key_op *remove_op_p)
-{
-	int retval;
-	if (__sysrq_get_key_op(key) == remove_op_p) {
-		__sysrq_put_key_op(key, insert_op_p);
-		retval = 0;
-	} else {
-                retval = -1;
-	}
-	return retval;
-}
-
-extern __inline__ int
-__sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p,
-				struct sysrq_key_op *remove_op_p) {
-	int retval;
-	__sysrq_lock_table();
-	retval = __sysrq_swap_key_ops_nolock(key, insert_op_p, remove_op_p);
-	__sysrq_unlock_table();
-	return retval;
-}
-	
-static inline int register_sysrq_key(int key, struct sysrq_key_op *op_p)
-{
-	return __sysrq_swap_key_ops(key, op_p, NULL);
-}
-
-static inline int unregister_sysrq_key(int key, struct sysrq_key_op *op_p)
-{
-	return __sysrq_swap_key_ops(key, NULL, op_p);
-}
+int register_sysrq_key(int, struct sysrq_key_op *);
+int unregister_sysrq_key(int, struct sysrq_key_op *);
 
 #else
 
_