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 | |
| 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')
| -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 |
