diff options
author | Huang Ying <ying.huang@intel.com> | 2011-01-13 01:59:43 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-13 17:03:09 +0100 |
commit | fb842b00c5eab66ec361b31550aa8a922745ce9e (patch) | |
tree | cef7e6a0bd01849fcfc911f36293304f9ba7b3c2 /kernel/printk.c | |
parent | dca: remove unneeded NULL check (diff) | |
download | linux-fb842b00c5eab66ec361b31550aa8a922745ce9e.tar.xz linux-fb842b00c5eab66ec361b31550aa8a922745ce9e.zip |
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 @@ #include <linux/syslog.h> #include <linux/cpu.h> #include <linux/notifier.h> +#include <linux/rculist.h> #include <asm/uaccess.h> @@ -1502,7 +1503,7 @@ int kmsg_dump_register(struct kmsg_dumper *dumper) /* Don't allow registering multiple times */ if (!dumper->registered) { dumper->registered = 1; - list_add_tail(&dumper->list, &dump_list); + list_add_tail_rcu(&dumper->list, &dump_list); err = 0; } spin_unlock_irqrestore(&dump_list_lock, flags); @@ -1526,33 +1527,16 @@ int kmsg_dump_unregister(struct kmsg_dumper *dumper) spin_lock_irqsave(&dump_list_lock, flags); if (dumper->registered) { dumper->registered = 0; - list_del(&dumper->list); + list_del_rcu(&dumper->list); err = 0; } spin_unlock_irqrestore(&dump_list_lock, flags); + synchronize_rcu(); return err; } EXPORT_SYMBOL_GPL(kmsg_dump_unregister); -static const char * const kmsg_reasons[] = { - [KMSG_DUMP_OOPS] = "oops", - [KMSG_DUMP_PANIC] = "panic", - [KMSG_DUMP_KEXEC] = "kexec", - [KMSG_DUMP_RESTART] = "restart", - [KMSG_DUMP_HALT] = "halt", - [KMSG_DUMP_POWEROFF] = "poweroff", - [KMSG_DUMP_EMERG] = "emergency_restart", -}; - -static const char *kmsg_to_str(enum kmsg_dump_reason reason) -{ - if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0) - return "unknown"; - - return kmsg_reasons[reason]; -} - /** * kmsg_dump - dump kernel log to kernel message dumpers. * @reason: the reason (oops, panic etc) for dumping @@ -1591,13 +1575,9 @@ void kmsg_dump(enum kmsg_dump_reason reason) l2 = chars; } - if (!spin_trylock_irqsave(&dump_list_lock, flags)) { - printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", - kmsg_to_str(reason)); - return; - } - list_for_each_entry(dumper, &dump_list, list) + rcu_read_lock(); + list_for_each_entry_rcu(dumper, &dump_list, list) dumper->dump(dumper, reason, s1, l1, s2, l2); - spin_unlock_irqrestore(&dump_list_lock, flags); + rcu_read_unlock(); } #endif |