diff options
author | Don Zickus <dzickus@redhat.com> | 2011-08-12 13:54:51 -0400 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2011-08-16 14:55:58 -0400 |
commit | abd4d5587be911f63592537284dad78766d97d62 (patch) | |
tree | 50d88b64ded1189d6c816a1f90124a05354914ba /fs | |
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')
-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 ca60ebcfb15f..0472924024cc 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/timer.h> | 28 | #include <linux/timer.h> |
29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/uaccess.h> | 30 | #include <linux/uaccess.h> |
31 | #include <linux/hardirq.h> | ||
31 | #include <linux/workqueue.h> | 32 | #include <linux/workqueue.h> |
32 | 33 | ||
33 | #include "internal.h" | 34 | #include "internal.h" |
@@ -88,13 +89,20 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
88 | u64 id; | 89 | u64 id; |
89 | int hsize; | 90 | int hsize; |
90 | unsigned int part = 1; | 91 | unsigned int part = 1; |
92 | unsigned long flags = 0; | ||
93 | int is_locked = 0; | ||
91 | 94 | ||
92 | if (reason < ARRAY_SIZE(reason_str)) | 95 | if (reason < ARRAY_SIZE(reason_str)) |
93 | why = reason_str[reason]; | 96 | why = reason_str[reason]; |
94 | else | 97 | else |
95 | why = "Unknown"; | 98 | why = "Unknown"; |
96 | 99 | ||
97 | mutex_lock(&psinfo->buf_mutex); | 100 | if (in_nmi()) { |
101 | is_locked = spin_trylock(&psinfo->buf_lock); | ||
102 | if (!is_locked) | ||
103 | pr_err("pstore dump routine blocked in NMI, may corrupt error record\n"); | ||
104 | } else | ||
105 | spin_lock_irqsave(&psinfo->buf_lock, flags); | ||
98 | oopscount++; | 106 | oopscount++; |
99 | while (total < kmsg_bytes) { | 107 | while (total < kmsg_bytes) { |
100 | dst = psinfo->buf; | 108 | dst = psinfo->buf; |
@@ -123,7 +131,11 @@ static void pstore_dump(struct kmsg_dumper *dumper, | |||
123 | total += l1_cpy + l2_cpy; | 131 | total += l1_cpy + l2_cpy; |
124 | part++; | 132 | part++; |
125 | } | 133 | } |
126 | mutex_unlock(&psinfo->buf_mutex); | 134 | if (in_nmi()) { |
135 | if (is_locked) | ||
136 | spin_unlock(&psinfo->buf_lock); | ||
137 | } else | ||
138 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); | ||
127 | } | 139 | } |
128 | 140 | ||
129 | static struct kmsg_dumper pstore_dumper = { | 141 | static struct kmsg_dumper pstore_dumper = { |
@@ -188,11 +200,12 @@ void pstore_get_records(int quiet) | |||
188 | enum pstore_type_id type; | 200 | enum pstore_type_id type; |
189 | struct timespec time; | 201 | struct timespec time; |
190 | int failed = 0, rc; | 202 | int failed = 0, rc; |
203 | unsigned long flags; | ||
191 | 204 | ||
192 | if (!psi) | 205 | if (!psi) |
193 | return; | 206 | return; |
194 | 207 | ||
195 | mutex_lock(&psinfo->buf_mutex); | 208 | spin_lock_irqsave(&psinfo->buf_lock, flags); |
196 | rc = psi->open(psi); | 209 | rc = psi->open(psi); |
197 | if (rc) | 210 | if (rc) |
198 | goto out; | 211 | goto out; |
@@ -205,7 +218,7 @@ void pstore_get_records(int quiet) | |||
205 | } | 218 | } |
206 | psi->close(psi); | 219 | psi->close(psi); |
207 | out: | 220 | out: |
208 | mutex_unlock(&psinfo->buf_mutex); | 221 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); |
209 | 222 | ||
210 | if (failed) | 223 | if (failed) |
211 | printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n", | 224 | printk(KERN_WARNING "pstore: failed to load %d record(s) from '%s'\n", |
@@ -233,7 +246,8 @@ static void pstore_timefunc(unsigned long dummy) | |||
233 | */ | 246 | */ |
234 | int pstore_write(enum pstore_type_id type, char *buf, size_t size) | 247 | int pstore_write(enum pstore_type_id type, char *buf, size_t size) |
235 | { | 248 | { |
236 | u64 id; | 249 | u64 id; |
250 | unsigned long flags; | ||
237 | 251 | ||
238 | if (!psinfo) | 252 | if (!psinfo) |
239 | return -ENODEV; | 253 | return -ENODEV; |
@@ -241,13 +255,13 @@ int pstore_write(enum pstore_type_id type, char *buf, size_t size) | |||
241 | if (size > psinfo->bufsize) | 255 | if (size > psinfo->bufsize) |
242 | return -EFBIG; | 256 | return -EFBIG; |
243 | 257 | ||
244 | mutex_lock(&psinfo->buf_mutex); | 258 | spin_lock_irqsave(&psinfo->buf_lock, flags); |
245 | memcpy(psinfo->buf, buf, size); | 259 | memcpy(psinfo->buf, buf, size); |
246 | id = psinfo->write(type, 0, size, psinfo); | 260 | id = psinfo->write(type, 0, size, psinfo); |
247 | if (pstore_is_mounted()) | 261 | if (pstore_is_mounted()) |
248 | pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, | 262 | pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, |
249 | size, CURRENT_TIME, psinfo); | 263 | size, CURRENT_TIME, psinfo); |
250 | mutex_unlock(&psinfo->buf_mutex); | 264 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); |
251 | 265 | ||
252 | return 0; | 266 | return 0; |
253 | } | 267 | } |