diff options
author | Joakim Tjernlund <Joakim.Tjernlund@transmode.se> | 2011-02-07 11:07:11 -0500 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2011-03-11 07:09:07 -0500 |
commit | ecf3fde07c8dcb92a1bf3fbdfe70905d85cd00e1 (patch) | |
tree | b45b4729fdd5e49f588629a3da968a4a90aa401f /drivers/mtd/chips | |
parent | ceabebb2bd2672f709e4454e16bc6042732e2dfe (diff) |
mtd: fix race in cfi_cmdset_0001 driver
As inval_cache_and_wait_for_operation() drop and reclaim the lock
to invalidate the cache, some other thread may suspend the operation
before reaching the for(;;) loop. Therefore the loop must start with
checking the chip->state before reading status from the chip.
Signed-off-by: Joakim Tjernlund <Joakim.Tjernlund@transmode.se>
Acked-by: Michael Cashwell <mboards@prograde.net>
Acked-by: Stefan Bigler <stefan.bigler@keymile.com>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Cc: stable@kernel.org
Diffstat (limited to 'drivers/mtd/chips')
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0001.c | 43 |
1 files changed, 22 insertions, 21 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index a8c3e1c9b02a..4aaa88f8ab5f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c | |||
@@ -1230,10 +1230,32 @@ static int inval_cache_and_wait_for_operation( | |||
1230 | sleep_time = chip_op_time / 2; | 1230 | sleep_time = chip_op_time / 2; |
1231 | 1231 | ||
1232 | for (;;) { | 1232 | for (;;) { |
1233 | if (chip->state != chip_state) { | ||
1234 | /* Someone's suspended the operation: sleep */ | ||
1235 | DECLARE_WAITQUEUE(wait, current); | ||
1236 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
1237 | add_wait_queue(&chip->wq, &wait); | ||
1238 | mutex_unlock(&chip->mutex); | ||
1239 | schedule(); | ||
1240 | remove_wait_queue(&chip->wq, &wait); | ||
1241 | mutex_lock(&chip->mutex); | ||
1242 | continue; | ||
1243 | } | ||
1244 | |||
1233 | status = map_read(map, cmd_adr); | 1245 | status = map_read(map, cmd_adr); |
1234 | if (map_word_andequal(map, status, status_OK, status_OK)) | 1246 | if (map_word_andequal(map, status, status_OK, status_OK)) |
1235 | break; | 1247 | break; |
1236 | 1248 | ||
1249 | if (chip->erase_suspended && chip_state == FL_ERASING) { | ||
1250 | /* Erase suspend occured while sleep: reset timeout */ | ||
1251 | timeo = reset_timeo; | ||
1252 | chip->erase_suspended = 0; | ||
1253 | } | ||
1254 | if (chip->write_suspended && chip_state == FL_WRITING) { | ||
1255 | /* Write suspend occured while sleep: reset timeout */ | ||
1256 | timeo = reset_timeo; | ||
1257 | chip->write_suspended = 0; | ||
1258 | } | ||
1237 | if (!timeo) { | 1259 | if (!timeo) { |
1238 | map_write(map, CMD(0x70), cmd_adr); | 1260 | map_write(map, CMD(0x70), cmd_adr); |
1239 | chip->state = FL_STATUS; | 1261 | chip->state = FL_STATUS; |
@@ -1257,27 +1279,6 @@ static int inval_cache_and_wait_for_operation( | |||
1257 | timeo--; | 1279 | timeo--; |
1258 | } | 1280 | } |
1259 | mutex_lock(&chip->mutex); | 1281 | mutex_lock(&chip->mutex); |
1260 | |||
1261 | while (chip->state != chip_state) { | ||
1262 | /* Someone's suspended the operation: sleep */ | ||
1263 | DECLARE_WAITQUEUE(wait, current); | ||
1264 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
1265 | add_wait_queue(&chip->wq, &wait); | ||
1266 | mutex_unlock(&chip->mutex); | ||
1267 | schedule(); | ||
1268 | remove_wait_queue(&chip->wq, &wait); | ||
1269 | mutex_lock(&chip->mutex); | ||
1270 | } | ||
1271 | if (chip->erase_suspended && chip_state == FL_ERASING) { | ||
1272 | /* Erase suspend occured while sleep: reset timeout */ | ||
1273 | timeo = reset_timeo; | ||
1274 | chip->erase_suspended = 0; | ||
1275 | } | ||
1276 | if (chip->write_suspended && chip_state == FL_WRITING) { | ||
1277 | /* Write suspend occured while sleep: reset timeout */ | ||
1278 | timeo = reset_timeo; | ||
1279 | chip->write_suspended = 0; | ||
1280 | } | ||
1281 | } | 1282 | } |
1282 | 1283 | ||
1283 | /* Done and happy. */ | 1284 | /* Done and happy. */ |