diff options
author | Christian Riesch <christian.riesch@omicron.at> | 2014-03-06 12:42:37 +0100 |
---|---|---|
committer | Brian Norris <computersforpeace@gmail.com> | 2014-03-10 22:42:31 -0700 |
commit | 9a78bc83b4c31f67202b7b0a77fa25da732f44a3 (patch) | |
tree | 386d354b6cba9a22eca92c07ab10206c476c955a | |
parent | ea6d833a3fddcd1d60414d48f34c7f4fbe88608f (diff) | |
download | lwn-9a78bc83b4c31f67202b7b0a77fa25da732f44a3.tar.gz lwn-9a78bc83b4c31f67202b7b0a77fa25da732f44a3.zip |
mtd: Fix the behavior of OTP write if there is not enough room for data
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@omicron.at>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
-rw-r--r-- | drivers/mtd/devices/mtd_dataflash.c | 16 | ||||
-rw-r--r-- | drivers/mtd/mtdchar.c | 9 | ||||
-rw-r--r-- | 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 a6fdbe83bad6..dd22ce2cc9ad 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -542,14 +542,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 250798cf76aa..7d4e7b9da3a1 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 0a7d77e65335..d201feeb3ca6 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); |