]> git.hungrycats.org Git - linux/commitdiff
mtd: fix race in cfi_cmdset_0001 driver
authorJoakim Tjernlund <Joakim.Tjernlund@transmode.se>
Mon, 7 Feb 2011 16:07:11 +0000 (17:07 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Mar 2011 19:49:53 +0000 (12:49 -0700)
commit ecf3fde07c8dcb92a1bf3fbdfe70905d85cd00e1 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/mtd/chips/cfi_cmdset_0001.c

index ad9268b444163e2df218b3ff5b3bee8bdfdca0a6..56802cd3493c8923cb06022c64425d4bfe17241a 100644 (file)
@@ -1229,10 +1229,32 @@ static int inval_cache_and_wait_for_operation(
        sleep_time = chip_op_time / 2;
 
        for (;;) {
+               if (chip->state != chip_state) {
+                       /* Someone's suspended the operation: sleep */
+                       DECLARE_WAITQUEUE(wait, current);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       add_wait_queue(&chip->wq, &wait);
+                       mutex_unlock(&chip->mutex);
+                       schedule();
+                       remove_wait_queue(&chip->wq, &wait);
+                       mutex_lock(&chip->mutex);
+                       continue;
+               }
+
                status = map_read(map, cmd_adr);
                if (map_word_andequal(map, status, status_OK, status_OK))
                        break;
 
+               if (chip->erase_suspended && chip_state == FL_ERASING)  {
+                       /* Erase suspend occured while sleep: reset timeout */
+                       timeo = reset_timeo;
+                       chip->erase_suspended = 0;
+               }
+               if (chip->write_suspended && chip_state == FL_WRITING)  {
+                       /* Write suspend occured while sleep: reset timeout */
+                       timeo = reset_timeo;
+                       chip->write_suspended = 0;
+               }
                if (!timeo) {
                        map_write(map, CMD(0x70), cmd_adr);
                        chip->state = FL_STATUS;
@@ -1256,27 +1278,6 @@ static int inval_cache_and_wait_for_operation(
                        timeo--;
                }
                mutex_lock(&chip->mutex);
-
-               while (chip->state != chip_state) {
-                       /* Someone's suspended the operation: sleep */
-                       DECLARE_WAITQUEUE(wait, current);
-                       set_current_state(TASK_UNINTERRUPTIBLE);
-                       add_wait_queue(&chip->wq, &wait);
-                       mutex_unlock(&chip->mutex);
-                       schedule();
-                       remove_wait_queue(&chip->wq, &wait);
-                       mutex_lock(&chip->mutex);
-               }
-               if (chip->erase_suspended && chip_state == FL_ERASING)  {
-                       /* Erase suspend occured while sleep: reset timeout */
-                       timeo = reset_timeo;
-                       chip->erase_suspended = 0;
-               }
-               if (chip->write_suspended && chip_state == FL_WRITING)  {
-                       /* Write suspend occured while sleep: reset timeout */
-                       timeo = reset_timeo;
-                       chip->write_suspended = 0;
-               }
        }
 
        /* Done and happy. */