diff options
author | Huang Ying <ying.huang@intel.com> | 2011-01-12 19:59:43 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-13 11:03:09 -0500 |
commit | fb842b00c5eab66ec361b31550aa8a922745ce9e (patch) | |
tree | cef7e6a0bd01849fcfc911f36293304f9ba7b3c2 /kernel/printk.c | |
parent | f0f2c2b5b40b5e621a47a6a274117cce77841f1e (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>
Diffstat (limited to 'kernel/printk.c')
-rw-r--r-- | kernel/printk.c | 34 |
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 | } |
1536 | EXPORT_SYMBOL_GPL(kmsg_dump_unregister); | 1538 | EXPORT_SYMBOL_GPL(kmsg_dump_unregister); |
1537 | 1539 | ||
1538 | static 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 | |||
1548 | static 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 |