Commit fb842b00 authored by Huang Ying's avatar Huang Ying Committed by Linus Torvalds

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: default avatarHuang Ying <ying.huang@intel.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f0f2c2b5
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/syslog.h> #include <linux/syslog.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/rculist.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -1502,7 +1503,7 @@ int kmsg_dump_register(struct kmsg_dumper *dumper) ...@@ -1502,7 +1503,7 @@ int kmsg_dump_register(struct kmsg_dumper *dumper)
/* Don't allow registering multiple times */ /* Don't allow registering multiple times */
if (!dumper->registered) { if (!dumper->registered) {
dumper->registered = 1; dumper->registered = 1;
list_add_tail(&dumper->list, &dump_list); list_add_tail_rcu(&dumper->list, &dump_list);
err = 0; err = 0;
} }
spin_unlock_irqrestore(&dump_list_lock, flags); spin_unlock_irqrestore(&dump_list_lock, flags);
...@@ -1526,33 +1527,16 @@ int kmsg_dump_unregister(struct kmsg_dumper *dumper) ...@@ -1526,33 +1527,16 @@ int kmsg_dump_unregister(struct kmsg_dumper *dumper)
spin_lock_irqsave(&dump_list_lock, flags); spin_lock_irqsave(&dump_list_lock, flags);
if (dumper->registered) { if (dumper->registered) {
dumper->registered = 0; dumper->registered = 0;
list_del(&dumper->list); list_del_rcu(&dumper->list);
err = 0; err = 0;
} }
spin_unlock_irqrestore(&dump_list_lock, flags); spin_unlock_irqrestore(&dump_list_lock, flags);
synchronize_rcu();
return err; return err;
} }
EXPORT_SYMBOL_GPL(kmsg_dump_unregister); 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. * kmsg_dump - dump kernel log to kernel message dumpers.
* @reason: the reason (oops, panic etc) for dumping * @reason: the reason (oops, panic etc) for dumping
...@@ -1591,13 +1575,9 @@ void kmsg_dump(enum kmsg_dump_reason reason) ...@@ -1591,13 +1575,9 @@ void kmsg_dump(enum kmsg_dump_reason reason)
l2 = chars; l2 = chars;
} }
if (!spin_trylock_irqsave(&dump_list_lock, flags)) { rcu_read_lock();
printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", list_for_each_entry_rcu(dumper, &dump_list, list)
kmsg_to_str(reason));
return;
}
list_for_each_entry(dumper, &dump_list, list)
dumper->dump(dumper, reason, s1, l1, s2, l2); dumper->dump(dumper, reason, s1, l1, s2, l2);
spin_unlock_irqrestore(&dump_list_lock, flags); rcu_read_unlock();
} }
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment