summaryrefslogtreecommitdiff
path: root/drivers/mtd/mtdblock.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/mtdblock.c')
-rw-r--r--drivers/mtd/mtdblock.c64
1 files changed, 25 insertions, 39 deletions
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index b2d5ed1cbc94..9751416c2a91 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Direct MTD block device access
*
* Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
* Copyright © 2000-2003 Nicolas Pitre <nico@fluxnic.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/fs.h>
@@ -103,8 +89,6 @@ static int write_cached_data (struct mtdblk_dev *mtdblk)
ret = erase_write (mtd, mtdblk->cache_offset,
mtdblk->cache_size, mtdblk->cache_data);
- if (ret)
- return ret;
/*
* Here we could arguably set the cache state to STATE_CLEAN.
@@ -112,9 +96,14 @@ static int write_cached_data (struct mtdblk_dev *mtdblk)
* be notified if this content is altered on the flash by other
* means. Let's declare it empty and leave buffering tasks to
* the buffer cache instead.
+ *
+ * If this cache_offset points to a bad block, data cannot be
+ * written to the device. Clear cache_state to avoid writing to
+ * bad blocks repeatedly.
*/
- mtdblk->cache_state = STATE_EMPTY;
- return 0;
+ if (ret == 0 || ret == -EIO)
+ mtdblk->cache_state = STATE_EMPTY;
+ return ret;
}
@@ -164,7 +153,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
mtdblk->cache_state = STATE_EMPTY;
ret = mtd_read(mtd, sect_start, sect_size,
&retlen, mtdblk->cache_data);
- if (ret)
+ if (ret && !mtd_is_bitflip(ret))
return ret;
if (retlen != sect_size)
return -EIO;
@@ -199,8 +188,12 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
pr_debug("mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",
mtd->name, pos, len);
- if (!sect_size)
- return mtd_read(mtd, pos, len, &retlen, buf);
+ if (!sect_size) {
+ ret = mtd_read(mtd, pos, len, &retlen, buf);
+ if (ret && !mtd_is_bitflip(ret))
+ return ret;
+ return 0;
+ }
while (len > 0) {
unsigned long sect_start = (pos/sect_size)*sect_size;
@@ -220,7 +213,7 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
memcpy (buf, mtdblk->cache_data + offset, size);
} else {
ret = mtd_read(mtd, pos, size, &retlen, buf);
- if (ret)
+ if (ret && !mtd_is_bitflip(ret))
return ret;
if (retlen != size)
return -EIO;
@@ -268,6 +261,10 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
return 0;
}
+ if (mtd_type_is_nand(mbd->mtd))
+ pr_warn_ratelimited("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n",
+ mbd->tr->name, mbd->mtd->name);
+
/* OK, it's not open. Create cache info for it */
mtdblk->count = 1;
mutex_init(&mtdblk->cache_mutex);
@@ -297,7 +294,7 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd)
* It was the last usage. Free the cache, but only sync if
* opened for writing.
*/
- if (mbd->file_mode & FMODE_WRITE)
+ if (mbd->writable)
mtd_sync(mbd->mtd);
vfree(mtdblk->cache_data);
}
@@ -308,12 +305,13 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd)
static int mtdblock_flush(struct mtd_blktrans_dev *dev)
{
struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
+ int ret;
mutex_lock(&mtdblk->cache_mutex);
- write_cached_data(mtdblk);
+ ret = write_cached_data(mtdblk);
mutex_unlock(&mtdblk->cache_mutex);
mtd_sync(dev->mtd);
- return 0;
+ return ret;
}
static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
@@ -356,19 +354,7 @@ static struct mtd_blktrans_ops mtdblock_tr = {
.owner = THIS_MODULE,
};
-static int __init init_mtdblock(void)
-{
- return register_mtd_blktrans(&mtdblock_tr);
-}
-
-static void __exit cleanup_mtdblock(void)
-{
- deregister_mtd_blktrans(&mtdblock_tr);
-}
-
-module_init(init_mtdblock);
-module_exit(cleanup_mtdblock);
-
+module_mtd_blktrans(mtdblock_tr);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net> et al.");