[PATCH v3] mtd: Fix the behavior of OTP write if there is not enough room for data

Christian Riesch christian.riesch at omicron.at
Thu Mar 6 06:42:37 EST 2014


If a write to one time programmable memory (OTP) hits the end of this
memory area, no more data can be written. The count variable in
mtdchar_write() in drivers/mtd/mtdchar.c is not decreased anymore.
We are trapped in the loop forever, mtdchar_write() will never return
in this case.

The desired behavior of a write in such a case is described in [1]:
- Try to write as much data as possible, truncate the write to fit into
  the available memory and return the number of bytes that actually
  have been written.
- If no data could be written at all, return -ENOSPC.

This patch fixes the behavior of OTP write if there is not enough space
for all data:

1) mtd_write_user_prot_reg() in drivers/mtd/mtdcore.c is modified to
   return -ENOSPC if no data could be written at all.
2) mtdchar_write() is modified to handle -ENOSPC correctly. Exit if a
   write returned -ENOSPC and yield the correct return value, either
   then number of bytes that could be written, or -ENOSPC, if no data
   could be written at all.

Furthermore the patch harmonizes the behavior of the OTP memory write
in drivers/mtd/devices/mtd_dataflash.c with the other implementations
and the requirements from [1]. Instead of returning -EINVAL if the data
does not fit into the OTP memory, we try to write as much data as
possible/truncate the write.

[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html

Signed-off-by: Christian Riesch <christian.riesch at omicron.at>
Cc: Brian Norris <computersforpeace at gmail.com>
---

Changes for v3:
- Moved logic for returning -ENOSPC from cfi_intelext_write_user_prot_reg,
  dataflash_write_user_otp, onenand_write_user_prot_reg to a common
  implementation in mtd_write_user_prot_reg, as requested by Brian Norris.
- Simplified the code in mtdchar_write as suggested by Brian.
- Added more comments, commit message.

 drivers/mtd/devices/mtd_dataflash.c |   16 ++++++++++------
 drivers/mtd/mtdchar.c               |    9 +++++++++
 drivers/mtd/mtdcore.c               |   12 +++++++++++-
 3 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index c89f1c5..10b7608 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -543,14 +543,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
 	struct dataflash	*priv = mtd->priv;
 	int			status;
 
-	if (len > 64)
-		return -EINVAL;
+	if (from >= 64) {
+		/*
+		 * Attempting to write beyond the end of OTP memory,
+		 * no data can be written.
+		 */
+		*retlen = 0;
+		return 0;
+	}
 
-	/* Strictly speaking, we *could* truncate the write ... but
-	 * let's not do that for the only write that's ever possible.
-	 */
+	/* Truncate the write to fit into OTP memory. */
 	if ((from + len) > 64)
-		return -EINVAL;
+		len = 64 - from;
 
 	/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
 	 * IN:  ignore all
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 250798c..7d4e7b9 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -324,6 +324,15 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
 		default:
 			ret = mtd_write(mtd, *ppos, len, &retlen, kbuf);
 		}
+
+		/*
+		 * Return -ENOSPC only if no data could be written at all.
+		 * Otherwise just return the number of bytes that actually
+		 * have been written.
+		 */
+		if ((ret == -ENOSPC) && (total_retlen))
+			break;
+
 		if (!ret) {
 			*ppos += retlen;
 			total_retlen += retlen;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 0a7d77e..d201fee 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -932,12 +932,22 @@ EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
 int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
 			    size_t *retlen, u_char *buf)
 {
+	int ret;
+
 	*retlen = 0;
 	if (!mtd->_write_user_prot_reg)
 		return -EOPNOTSUPP;
 	if (!len)
 		return 0;
-	return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+	ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+	if (ret)
+		return ret;
+
+	/*
+	 * If no data could be written at all, we are out of memory and
+	 * must return -ENOSPC.
+	 */
+	return (*retlen) ? 0 : -ENOSPC;
 }
 EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
 
-- 
1.7.9.5




More information about the linux-mtd mailing list