Commit 8df3c8e0 authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Linus Torvalds

[PATCH] L18 flash corruption fix

Another fix to the put_chip() concurrency logic.

Problem was occurring when:

1) one thread was erasing a block in partition x;
2) another thread suspended the erase in order to write to
   partition y;
3) a third thread came along to read a different block from
   partition x and, when it called put_chip(), chip->oldstate was
   FL_ERASING and the erase (mistakenly) resumed;
4) the write in partition y obviously failed at that point.

Incidentally, the fix for this problem also fixed the case where
suspending writes for MTD XIP usage was not working properly.
Signed-off-by: default avatarNicolas Pitre <nico@cam.org>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 68f12028
......@@ -36,10 +36,7 @@
#include <linux/mtd/cfi.h>
/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
#ifdef CONFIG_MTD_XIP
#define CMDSET0001_DISABLE_WRITE_SUSPEND
#endif
/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */
// debugging, turns off buffer write mode if set to 1
#define FORCE_WORD_WRITE 0
......@@ -152,7 +149,6 @@ static void fixup_intel_strataflash(struct mtd_info *mtd, void* param)
#endif
#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND
/* The XIP config appears to have problems using write suspend at the moment */
static void fixup_no_write_suspend(struct mtd_info *mtd, void* param)
{
struct map_info *map = mtd->priv;
......@@ -733,7 +729,7 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
if (chip->priv) {
struct flchip_shared *shared = chip->priv;
spin_lock(&shared->lock);
if (shared->writing == chip) {
if (shared->writing == chip && chip->oldstate == FL_READY) {
/* We own the ability to write, but we're done */
shared->writing = shared->erasing;
if (shared->writing && shared->writing != chip) {
......@@ -745,17 +741,24 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
put_chip(map, loaner, loaner->start);
spin_lock(chip->mutex);
spin_unlock(loaner->mutex);
} else {
if (chip->oldstate != FL_ERASING) {
shared->erasing = NULL;
if (chip->oldstate != FL_WRITING)
shared->writing = NULL;
}
spin_unlock(&shared->lock);
wake_up(&chip->wq);
return;
}
} else {
shared->erasing = NULL;
shared->writing = NULL;
} else if (shared->erasing == chip && shared->writing != chip) {
/*
* We own the ability to erase without the ability
* to write, which means the erase was suspended
* and some other partition is currently writing.
* Don't let the switch below mess things up since
* we don't have ownership to resume anything.
*/
spin_unlock(&shared->lock);
wake_up(&chip->wq);
return;
}
spin_unlock(&shared->lock);
}
switch(chip->oldstate) {
......
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