diff options
author | Don Zickus <dzickus@redhat.com> | 2011-08-12 10:54:51 -0700 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2011-08-16 11:55:58 -0700 |
commit | abd4d5587be911f63592537284dad78766d97d62 (patch) | |
tree | 50d88b64ded1189d6c816a1f90124a05354914ba /fs/pstore | |
parent | 6dda9266913ad57e09afc1a10d6473f10c806a63 (diff) |
pstore: change mutex locking to spin_locks
pstore was using mutex locking to protect read/write access to the
backend plug-ins. This causes problems when pstore is executed in
an NMI context through panic() -> kmsg_dump().
This patch changes the mutex to a spin_lock_irqsave then also checks to
see if we are in an NMI context. If we are in an NMI and can't get the
lock, just print a message stating that and blow by the locking.
All this is probably a hack around the bigger locking problem but it
solves my current situation of trying to sleep in an NMI context.
Tested by loading the lkdtm module and executing a HARDLOCKUP which
will cause the machine to panic inside the nmi handler.
Signed-off-by: Don Zickus <dzickus@redhat.com>
Acked-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'fs/pstore')
-rw-r--r-- | fs/pstore/platform.c | 28 |
1 files changed, 21 insertions, 7 deletions
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index ca60ebcfb15..0472924024c 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -28,6 +28,7 @@ #include <linux/timer.h> #include <linux/slab.h> #include <linux/uaccess.h> +#include <linux/hardirq.h> #include <linux/workqueue.h> #include "internal.h" @@ -88,13 +89,20 @@ static void pstore_dump(struct kmsg_dumper *dumper, u64 id; int hsize; unsigned int part = 1; + unsigned long flags = 0; + int is_locked = 0; if (reason < ARRAY_SIZE(reason_str)) why = reason_str[reason]; else why = "Unknown"; - mutex_lock(&psinfo->buf_mutex); + if (in_nmi()) { + is_locked = spin_trylock(&psinfo->buf_lock); + if (!is_locked) + pr_err("pstore dump routine blocked in NMI, may corrupt error record\n"); + } else + spin_lock_irqsave(&psinfo->buf_lock, flags); oopscount++; while (total < kmsg_bytes) { dst = psinfo->buf; @@ -123,7 +131,11 @@ static void pstore_dump(struct kmsg_dumper *dumper, total += l1_cpy + l2_cpy; part++; } - mutex_unlock(&psinfo->buf_mutex); + if (in_nmi()) { + if (is_locked) + spin_unlock(&psinfo->buf_lock); + } else + spin_unlock_irqrestore(&psinfo->buf_lock, flags); } static struct kmsg_dumper pstore_dumper = { @@ -188,11 +200,12 @@ void pstore_get_records(int quiet) enum pstore_type_id type; struct timespec time; int failed = 0, rc; + unsigned long flags; if (!psi) return; - mutex_lock(&psinfo->buf_mutex); + spin_lock_irqsave(&psinfo->buf_lock, flags); rc = psi->open(psi); if (rc) goto out; @@ -205,7 +218,7 @@ void pstore_get_records(int quiet) } psi->close(psi); out: - mutex_unlock(&psinfo->buf_mutex); + spin_unlock_irqrestore(&psinfo->buf_lock, flags); if (failed) printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n", @@ -233,7 +246,8 @@ static void pstore_timefunc(unsigned long dummy) */ int pstore_write(enum pstore_type_id type, char *buf, size_t size) { - u64 id; + u64 id; + unsigned long flags; if (!psinfo) return -ENODEV; @@ -241,13 +255,13 @@ int pstore_write(enum pstore_type_id type, char *buf, size_t size) if (size > psinfo->bufsize) return -EFBIG; - mutex_lock(&psinfo->buf_mutex); + spin_lock_irqsave(&psinfo->buf_lock, flags); memcpy(psinfo->buf, buf, size); id = psinfo->write(type, 0, size, psinfo); if (pstore_is_mounted()) pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, size, CURRENT_TIME, psinfo); - mutex_unlock(&psinfo->buf_mutex); + spin_unlock_irqrestore(&psinfo->buf_lock, flags); return 0; } |