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>
This commit is contained in:
parent
ea6d833a3f
commit
9a78bc83b4
@ -542,14 +542,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
|
|||||||
struct dataflash *priv = mtd->priv;
|
struct dataflash *priv = mtd->priv;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
if (len > 64)
|
if (from >= 64) {
|
||||||
return -EINVAL;
|
/*
|
||||||
|
* 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
|
/* Truncate the write to fit into OTP memory. */
|
||||||
* let's not do that for the only write that's ever possible.
|
|
||||||
*/
|
|
||||||
if ((from + len) > 64)
|
if ((from + len) > 64)
|
||||||
return -EINVAL;
|
len = 64 - from;
|
||||||
|
|
||||||
/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
|
/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
|
||||||
* IN: ignore all
|
* IN: ignore all
|
||||||
|
@ -324,6 +324,15 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
|
|||||||
default:
|
default:
|
||||||
ret = mtd_write(mtd, *ppos, len, &retlen, kbuf);
|
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) {
|
if (!ret) {
|
||||||
*ppos += retlen;
|
*ppos += retlen;
|
||||||
total_retlen += retlen;
|
total_retlen += retlen;
|
||||||
|
@ -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,
|
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
size_t *retlen, u_char *buf)
|
size_t *retlen, u_char *buf)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
*retlen = 0;
|
*retlen = 0;
|
||||||
if (!mtd->_write_user_prot_reg)
|
if (!mtd->_write_user_prot_reg)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
if (!len)
|
if (!len)
|
||||||
return 0;
|
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);
|
EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user