aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHuang Ying <ying.huang@intel.com>2011-01-12 19:59:43 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-13 11:03:09 -0500
commitfb842b00c5eab66ec361b31550aa8a922745ce9e (patch)
treecef7e6a0bd01849fcfc911f36293304f9ba7b3c2
parentf0f2c2b5b40b5e621a47a6a274117cce77841f1e (diff)
printk: use RCU to prevent potential lock contention in kmsg_dump
dump_list_lock is used to protect dump_list in kmsg_dumper implementation, kmsg_dump() uses it to traverse dump_list too. But if there is contention on the lock, kmsg_dump() will fail, and the valuable kernel message may be lost. This patch solves this issue with RCU. Because kmsg_dump() only read the list, no lock is needed in kmsg_dump(). So that kmsg_dump() will never fail because of lock contention. Signed-off-by: Huang Ying <ying.huang@intel.com> Cc: "Paul E. McKenney" <paulmck@us.ibm.com> Cc: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--kernel/printk.c34
1 files changed, 7 insertions, 27 deletions
diff --git a/kernel/printk.c b/kernel/printk.c
index 0b0c9aa71e89..53d9a9ec88e6 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -39,6 +39,7 @@
39#include <linux/syslog.h> 39#include <linux/syslog.h>
40#include <linux/cpu.h> 40#include <linux/cpu.h>
41#include <linux/notifier.h> 41#include <linux/notifier.h>
42#include <linux/rculist.h>
42 43
43#include <asm/uaccess.h> 44#include <asm/uaccess.h>
44 45
@@ -1502,7 +1503,7 @@ int kmsg_dump_register(struct kmsg_dumper *dumper)
1502 /* Don't allow registering multiple times */ 1503 /* Don't allow registering multiple times */
1503 if (!dumper->registered) { 1504 if (!dumper->registered) {
1504 dumper->registered = 1; 1505 dumper->registered = 1;
1505 list_add_tail(&dumper->list, &dump_list); 1506 list_add_tail_rcu(&dumper->list, &dump_list);
1506 err = 0; 1507 err = 0;
1507 } 1508 }
1508 spin_unlock_irqrestore(&dump_list_lock, flags); 1509 spin_unlock_irqrestore(&dump_list_lock, flags);
@@ -1526,33 +1527,16 @@ int kmsg_dump_unregister(struct kmsg_dumper *dumper)
1526 spin_lock_irqsave(&dump_list_lock, flags); 1527 spin_lock_irqsave(&dump_list_lock, flags);
1527 if (dumper->registered) { 1528 if (dumper->registered) {
1528 dumper->registered = 0; 1529 dumper->registered = 0;
1529 list_del(&dumper->list); 1530 list_del_rcu(&dumper->list);
1530 err = 0; 1531 err = 0;
1531 } 1532 }
1532 spin_unlock_irqrestore(&dump_list_lock, flags); 1533 spin_unlock_irqrestore(&dump_list_lock, flags);
1534 synchronize_rcu();
1533 1535
1534 return err; 1536 return err;
1535} 1537}
1536EXPORT_SYMBOL_GPL(kmsg_dump_unregister); 1538EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
1537 1539
1538static const char * const kmsg_reasons[] = {
1539 [KMSG_DUMP_OOPS] = "oops",
1540 [KMSG_DUMP_PANIC] = "panic",
1541 [KMSG_DUMP_KEXEC] = "kexec",
1542 [KMSG_DUMP_RESTART] = "restart",
1543 [KMSG_DUMP_HALT] = "halt",
1544 [KMSG_DUMP_POWEROFF] = "poweroff",
1545 [KMSG_DUMP_EMERG] = "emergency_restart",
1546};
1547
1548static const char *kmsg_to_str(enum kmsg_dump_reason reason)
1549{
1550 if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0)
1551 return "unknown";
1552
1553 return kmsg_reasons[reason];
1554}
1555
1556/** 1540/**
1557 * kmsg_dump - dump kernel log to kernel message dumpers. 1541 * kmsg_dump - dump kernel log to kernel message dumpers.
1558 * @reason: the reason (oops, panic etc) for dumping 1542 * @reason: the reason (oops, panic etc) for dumping
@@ -1591,13 +1575,9 @@ void kmsg_dump(enum kmsg_dump_reason reason)
1591 l2 = chars; 1575 l2 = chars;
1592 } 1576 }
1593 1577
1594 if (!spin_trylock_irqsave(&dump_list_lock, flags)) { 1578 rcu_read_lock();
1595 printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", 1579 list_for_each_entry_rcu(dumper, &dump_list, list)
1596 kmsg_to_str(reason));
1597 return;
1598 }
1599 list_for_each_entry(dumper, &dump_list, list)
1600 dumper->dump(dumper, reason, s1, l1, s2, l2); 1580 dumper->dump(dumper, reason, s1, l1, s2, l2);
1601 spin_unlock_irqrestore(&dump_list_lock, flags); 1581 rcu_read_unlock();
1602} 1582}
1603#endif 1583#endif