mtd: rawnand: protect access to rawnand devices while in suspend
authorSean Nyekjaer <sean@geanix.com>
Tue, 8 Feb 2022 08:52:13 +0000 (09:52 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 8 Apr 2022 12:22:53 +0000 (14:22 +0200)
commit 8cba323437a49a45756d661f500b324fc2d486fe upstream.

Prevent rawnand access while in a suspended state.

Commit 013e6292aaf5 ("mtd: rawnand: Simplify the locking") allows the
rawnand layer to return errors rather than waiting in a blocking wait.

Tested on a iMX6ULL.

Fixes: 013e6292aaf5 ("mtd: rawnand: Simplify the locking")
Signed-off-by: Sean Nyekjaer <sean@geanix.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Cc: stable@vger.kernel.org
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20220208085213.1838273-1-sean@geanix.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/mtd/nand/raw/nand_base.c
include/linux/mtd/rawnand.h

index d5a2110..881e768 100644 (file)
@@ -335,16 +335,19 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
  *
  * Return: -EBUSY if the chip has been suspended, 0 otherwise
  */
-static int nand_get_device(struct nand_chip *chip)
+static void nand_get_device(struct nand_chip *chip)
 {
-       mutex_lock(&chip->lock);
-       if (chip->suspended) {
+       /* Wait until the device is resumed. */
+       while (1) {
+               mutex_lock(&chip->lock);
+               if (!chip->suspended) {
+                       mutex_lock(&chip->controller->lock);
+                       return;
+               }
                mutex_unlock(&chip->lock);
-               return -EBUSY;
-       }
-       mutex_lock(&chip->controller->lock);
 
-       return 0;
+               wait_event(chip->resume_wq, !chip->suspended);
+       }
 }
 
 /**
@@ -573,9 +576,7 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
                nand_erase_nand(chip, &einfo, 0);
 
                /* Write bad block marker to OOB */
-               ret = nand_get_device(chip);
-               if (ret)
-                       return ret;
+               nand_get_device(chip);
 
                ret = nand_markbad_bbm(chip, ofs);
                nand_release_device(chip);
@@ -3823,9 +3824,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
            ops->mode != MTD_OPS_RAW)
                return -ENOTSUPP;
 
-       ret = nand_get_device(chip);
-       if (ret)
-               return ret;
+       nand_get_device(chip);
 
        if (!ops->datbuf)
                ret = nand_do_read_oob(chip, from, ops);
@@ -4412,13 +4411,11 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
                          struct mtd_oob_ops *ops)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
-       int ret;
+       int ret = 0;
 
        ops->retlen = 0;
 
-       ret = nand_get_device(chip);
-       if (ret)
-               return ret;
+       nand_get_device(chip);
 
        switch (ops->mode) {
        case MTD_OPS_PLACE_OOB:
@@ -4478,9 +4475,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
                return -EIO;
 
        /* Grab the lock and see if the device is available */
-       ret = nand_get_device(chip);
-       if (ret)
-               return ret;
+       nand_get_device(chip);
 
        /* Shift to get first page */
        page = (int)(instr->addr >> chip->page_shift);
@@ -4567,7 +4562,7 @@ static void nand_sync(struct mtd_info *mtd)
        pr_debug("%s: called\n", __func__);
 
        /* Grab the lock and see if the device is available */
-       WARN_ON(nand_get_device(chip));
+       nand_get_device(chip);
        /* Release it and go back */
        nand_release_device(chip);
 }
@@ -4584,9 +4579,7 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
        int ret;
 
        /* Select the NAND device */
-       ret = nand_get_device(chip);
-       if (ret)
-               return ret;
+       nand_get_device(chip);
 
        nand_select_target(chip, chipnr);
 
@@ -4657,6 +4650,8 @@ static void nand_resume(struct mtd_info *mtd)
                        __func__);
        }
        mutex_unlock(&chip->lock);
+
+       wake_up_all(&chip->resume_wq);
 }
 
 /**
@@ -5434,6 +5429,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
        chip->cur_cs = -1;
 
        mutex_init(&chip->lock);
+       init_waitqueue_head(&chip->resume_wq);
 
        /* Enforce the right timings for reset/detection */
        chip->current_interface_config = nand_get_reset_interface_config();
index 5b88cd5..dcf9014 100644 (file)
@@ -1240,6 +1240,7 @@ struct nand_secure_region {
  * @lock: Lock protecting the suspended field. Also used to serialize accesses
  *        to the NAND device
  * @suspended: Set to 1 when the device is suspended, 0 when it's not
+ * @resume_wq: wait queue to sleep if rawnand is in suspended state.
  * @cur_cs: Currently selected target. -1 means no target selected, otherwise we
  *          should always have cur_cs >= 0 && cur_cs < nanddev_ntargets().
  *          NAND Controller drivers should not modify this value, but they're
@@ -1294,6 +1295,7 @@ struct nand_chip {
        /* Internals */
        struct mutex lock;
        unsigned int suspended : 1;
+       wait_queue_head_t resume_wq;
        int cur_cs;
        int read_retries;
        struct nand_secure_region *secure_regions;