Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christian Riesch and committed by
Brian Norris
9a78bc83 ea6d833a

+30 -7
+10 -6
drivers/mtd/devices/mtd_dataflash.c
··· 542 542 struct dataflash *priv = mtd->priv; 543 543 int status; 544 544 545 - if (len > 64) 546 - return -EINVAL; 545 + if (from >= 64) { 546 + /* 547 + * Attempting to write beyond the end of OTP memory, 548 + * no data can be written. 549 + */ 550 + *retlen = 0; 551 + return 0; 552 + } 547 553 548 - /* Strictly speaking, we *could* truncate the write ... but 549 - * let's not do that for the only write that's ever possible. 550 - */ 554 + /* Truncate the write to fit into OTP memory. */ 551 555 if ((from + len) > 64) 552 - return -EINVAL; 556 + len = 64 - from; 553 557 554 558 /* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes 555 559 * IN: ignore all
+9
drivers/mtd/mtdchar.c
··· 324 324 default: 325 325 ret = mtd_write(mtd, *ppos, len, &retlen, kbuf); 326 326 } 327 + 328 + /* 329 + * Return -ENOSPC only if no data could be written at all. 330 + * Otherwise just return the number of bytes that actually 331 + * have been written. 332 + */ 333 + if ((ret == -ENOSPC) && (total_retlen)) 334 + break; 335 + 327 336 if (!ret) { 328 337 *ppos += retlen; 329 338 total_retlen += retlen;
+11 -1
drivers/mtd/mtdcore.c
··· 932 932 int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, 933 933 size_t *retlen, u_char *buf) 934 934 { 935 + int ret; 936 + 935 937 *retlen = 0; 936 938 if (!mtd->_write_user_prot_reg) 937 939 return -EOPNOTSUPP; 938 940 if (!len) 939 941 return 0; 940 - return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); 942 + ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); 943 + if (ret) 944 + return ret; 945 + 946 + /* 947 + * If no data could be written at all, we are out of memory and 948 + * must return -ENOSPC. 949 + */ 950 + return (*retlen) ? 0 : -ENOSPC; 941 951 } 942 952 EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg); 943 953