diff options
Diffstat (limited to 'drivers/mtd/mtdoops.c')
-rw-r--r-- | drivers/mtd/mtdoops.c | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 72c434c61b0a..1687521b4aa4 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/workqueue.h> | 28 | #include <linux/workqueue.h> |
29 | #include <linux/sched.h> | 29 | #include <linux/sched.h> |
30 | #include <linux/wait.h> | 30 | #include <linux/wait.h> |
31 | #include <linux/spinlock.h> | ||
31 | #include <linux/mtd/mtd.h> | 32 | #include <linux/mtd/mtd.h> |
32 | 33 | ||
33 | #define OOPS_PAGE_SIZE 4096 | 34 | #define OOPS_PAGE_SIZE 4096 |
@@ -42,6 +43,9 @@ struct mtdoops_context { | |||
42 | int nextcount; | 43 | int nextcount; |
43 | 44 | ||
44 | void *oops_buf; | 45 | void *oops_buf; |
46 | |||
47 | /* writecount and disabling ready are spin lock protected */ | ||
48 | spinlock_t writecount_lock; | ||
45 | int ready; | 49 | int ready; |
46 | int writecount; | 50 | int writecount; |
47 | } oops_cxt; | 51 | } oops_cxt; |
@@ -290,11 +294,22 @@ static void mtdoops_console_sync(void) | |||
290 | { | 294 | { |
291 | struct mtdoops_context *cxt = &oops_cxt; | 295 | struct mtdoops_context *cxt = &oops_cxt; |
292 | struct mtd_info *mtd = cxt->mtd; | 296 | struct mtd_info *mtd = cxt->mtd; |
297 | unsigned long flags; | ||
293 | 298 | ||
294 | if (!cxt->ready || !mtd || cxt->writecount == 0) | 299 | if (!cxt->ready || !mtd || cxt->writecount == 0) |
295 | return; | 300 | return; |
296 | 301 | ||
302 | /* | ||
303 | * Once ready is 0 and we've held the lock no further writes to the | ||
304 | * buffer will happen | ||
305 | */ | ||
306 | spin_lock_irqsave(&cxt->writecount_lock, flags); | ||
307 | if (!cxt->ready) { | ||
308 | spin_unlock_irqrestore(&cxt->writecount_lock, flags); | ||
309 | return; | ||
310 | } | ||
297 | cxt->ready = 0; | 311 | cxt->ready = 0; |
312 | spin_unlock_irqrestore(&cxt->writecount_lock, flags); | ||
298 | 313 | ||
299 | schedule_work(&cxt->work_write); | 314 | schedule_work(&cxt->work_write); |
300 | } | 315 | } |
@@ -304,6 +319,7 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) | |||
304 | { | 319 | { |
305 | struct mtdoops_context *cxt = co->data; | 320 | struct mtdoops_context *cxt = co->data; |
306 | struct mtd_info *mtd = cxt->mtd; | 321 | struct mtd_info *mtd = cxt->mtd; |
322 | unsigned long flags; | ||
307 | 323 | ||
308 | if (!oops_in_progress) { | 324 | if (!oops_in_progress) { |
309 | mtdoops_console_sync(); | 325 | mtdoops_console_sync(); |
@@ -313,6 +329,13 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) | |||
313 | if (!cxt->ready || !mtd) | 329 | if (!cxt->ready || !mtd) |
314 | return; | 330 | return; |
315 | 331 | ||
332 | /* Locking on writecount ensures sequential writes to the buffer */ | ||
333 | spin_lock_irqsave(&cxt->writecount_lock, flags); | ||
334 | |||
335 | /* Check ready status didn't change whilst waiting for the lock */ | ||
336 | if (!cxt->ready) | ||
337 | return; | ||
338 | |||
316 | if (cxt->writecount == 0) { | 339 | if (cxt->writecount == 0) { |
317 | u32 *stamp = cxt->oops_buf; | 340 | u32 *stamp = cxt->oops_buf; |
318 | *stamp = cxt->nextcount; | 341 | *stamp = cxt->nextcount; |
@@ -324,6 +347,11 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) | |||
324 | 347 | ||
325 | memcpy(cxt->oops_buf + cxt->writecount, s, count); | 348 | memcpy(cxt->oops_buf + cxt->writecount, s, count); |
326 | cxt->writecount += count; | 349 | cxt->writecount += count; |
350 | |||
351 | spin_unlock_irqrestore(&cxt->writecount_lock, flags); | ||
352 | |||
353 | if (cxt->writecount == OOPS_PAGE_SIZE) | ||
354 | mtdoops_console_sync(); | ||
327 | } | 355 | } |
328 | 356 | ||
329 | static int __init mtdoops_console_setup(struct console *co, char *options) | 357 | static int __init mtdoops_console_setup(struct console *co, char *options) |