aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorRik van Riel <riel@redhat.com>2014-06-06 17:38:13 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-06 19:08:17 -0400
commit984d74a72076a12b400339973e8c98fd2fcd90e5 (patch)
treed36c81545c10dbfa1513b8392aa10478eac7d13e /drivers/tty
parent310bf8f74927d0b945cd3e11dc5cef961ec04fd1 (diff)
sysrq: rcu-ify __handle_sysrq
Echoing values into /proc/sysrq-trigger seems to be a popular way to get information out of the kernel. However, dumping information about thousands of processes, or hundreds of CPUs to serial console can result in IRQs being blocked for minutes, resulting in various kinds of cascade failures. The most common failure is due to interrupts being blocked for a very long time. This can lead to things like failed IO requests, and other things the system cannot easily recover from. This problem is easily fixable by making __handle_sysrq use RCU instead of spin_lock_irqsave. This leaves the warning that RCU grace periods have not elapsed for a long time, but the system will come back from that automatically. It also leaves sysrq-from-irq-context when the sysrq keys are pressed, but that is probably desired since people want that to work in situations where the system is already hosed. The callers of register_sysrq_key and unregister_sysrq_key appear to be capable of sleeping. Signed-off-by: Rik van Riel <riel@redhat.com> Reported-by: Madper Xie <cxie@redhat.com> Cc: Randy Dunlap <rdunlap@infradead.org> Cc: Richard Weinberger <richard@nod.at> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/sysrq.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index b767a64e49d9..7430e87d7352 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -510,9 +510,8 @@ void __handle_sysrq(int key, bool check_mask)
510 struct sysrq_key_op *op_p; 510 struct sysrq_key_op *op_p;
511 int orig_log_level; 511 int orig_log_level;
512 int i; 512 int i;
513 unsigned long flags;
514 513
515 spin_lock_irqsave(&sysrq_key_table_lock, flags); 514 rcu_read_lock();
516 /* 515 /*
517 * Raise the apparent loglevel to maximum so that the sysrq header 516 * Raise the apparent loglevel to maximum so that the sysrq header
518 * is shown to provide the user with positive feedback. We do not 517 * is shown to provide the user with positive feedback. We do not
@@ -554,7 +553,7 @@ void __handle_sysrq(int key, bool check_mask)
554 printk("\n"); 553 printk("\n");
555 console_loglevel = orig_log_level; 554 console_loglevel = orig_log_level;
556 } 555 }
557 spin_unlock_irqrestore(&sysrq_key_table_lock, flags); 556 rcu_read_unlock();
558} 557}
559 558
560void handle_sysrq(int key) 559void handle_sysrq(int key)
@@ -1043,16 +1042,23 @@ static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p,
1043 struct sysrq_key_op *remove_op_p) 1042 struct sysrq_key_op *remove_op_p)
1044{ 1043{
1045 int retval; 1044 int retval;
1046 unsigned long flags;
1047 1045
1048 spin_lock_irqsave(&sysrq_key_table_lock, flags); 1046 spin_lock(&sysrq_key_table_lock);
1049 if (__sysrq_get_key_op(key) == remove_op_p) { 1047 if (__sysrq_get_key_op(key) == remove_op_p) {
1050 __sysrq_put_key_op(key, insert_op_p); 1048 __sysrq_put_key_op(key, insert_op_p);
1051 retval = 0; 1049 retval = 0;
1052 } else { 1050 } else {
1053 retval = -1; 1051 retval = -1;
1054 } 1052 }
1055 spin_unlock_irqrestore(&sysrq_key_table_lock, flags); 1053 spin_unlock(&sysrq_key_table_lock);
1054
1055 /*
1056 * A concurrent __handle_sysrq either got the old op or the new op.
1057 * Wait for it to go away before returning, so the code for an old
1058 * op is not freed (eg. on module unload) while it is in use.
1059 */
1060 synchronize_rcu();
1061
1056 return retval; 1062 return retval;
1057} 1063}
1058 1064