aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/pending-4.14/411-mtd-partial_eraseblock_write.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/pending-4.14/411-mtd-partial_eraseblock_write.patch')
-rw-r--r--target/linux/generic/pending-4.14/411-mtd-partial_eraseblock_write.patch154
1 files changed, 154 insertions, 0 deletions
diff --git a/target/linux/generic/pending-4.14/411-mtd-partial_eraseblock_write.patch b/target/linux/generic/pending-4.14/411-mtd-partial_eraseblock_write.patch
new file mode 100644
index 0000000000..096d6a4e71
--- /dev/null
+++ b/target/linux/generic/pending-4.14/411-mtd-partial_eraseblock_write.patch
@@ -0,0 +1,154 @@
+From: Felix Fietkau <nbd@nbd.name>
+Subject: mtd: implement write support for partitions covering only a part of an eraseblock (buffer data that would otherwise be erased)
+
+lede-commit: 87a8e8ac1067f58ba831c4aae443f3655c31cd80
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ drivers/mtd/mtdpart.c | 90 ++++++++++++++++++++++++++++++++++++++++++++-----
+ include/linux/mtd/mtd.h | 4 +++
+ 2 files changed, 85 insertions(+), 9 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -36,6 +36,8 @@
+ #include "mtdcore.h"
+ #include "mtdsplit/mtdsplit.h"
+
++#define MTD_ERASE_PARTIAL 0x8000 /* partition only covers parts of an erase block */
++
+ /* Our partition linked list */
+ static LIST_HEAD(mtd_partitions);
+ static DEFINE_MUTEX(mtd_partitions_mutex);
+@@ -241,13 +243,61 @@ static int part_erase(struct mtd_info *m
+ struct mtd_part *part = mtd_to_part(mtd);
+ int ret;
+
++
++ instr->partial_start = false;
++ if (mtd->flags & MTD_ERASE_PARTIAL) {
++ size_t readlen = 0;
++ u64 mtd_ofs;
++
++ instr->erase_buf = kmalloc(part->parent->erasesize, GFP_ATOMIC);
++ if (!instr->erase_buf)
++ return -ENOMEM;
++
++ mtd_ofs = part->offset + instr->addr;
++ instr->erase_buf_ofs = do_div(mtd_ofs, part->parent->erasesize);
++
++ if (instr->erase_buf_ofs > 0) {
++ instr->addr -= instr->erase_buf_ofs;
++ ret = mtd_read(part->parent,
++ instr->addr + part->offset,
++ part->parent->erasesize,
++ &readlen, instr->erase_buf);
++
++ instr->len += instr->erase_buf_ofs;
++ instr->partial_start = true;
++ } else {
++ mtd_ofs = part->offset + part->mtd.size;
++ instr->erase_buf_ofs = part->parent->erasesize -
++ do_div(mtd_ofs, part->parent->erasesize);
++
++ if (instr->erase_buf_ofs > 0) {
++ instr->len += instr->erase_buf_ofs;
++ ret = mtd_read(part->parent,
++ part->offset + instr->addr +
++ instr->len - part->parent->erasesize,
++ part->parent->erasesize, &readlen,
++ instr->erase_buf);
++ } else {
++ ret = 0;
++ }
++ }
++ if (ret < 0) {
++ kfree(instr->erase_buf);
++ return ret;
++ }
++
++ }
++
+ instr->addr += part->offset;
+ ret = part->parent->_erase(part->parent, instr);
+ if (ret) {
+ if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
+ instr->fail_addr -= part->offset;
+ instr->addr -= part->offset;
++ if (mtd->flags & MTD_ERASE_PARTIAL)
++ kfree(instr->erase_buf);
+ }
++
+ return ret;
+ }
+
+@@ -255,6 +305,25 @@ void mtd_erase_callback(struct erase_inf
+ {
+ if (instr->mtd->_erase == part_erase) {
+ struct mtd_part *part = mtd_to_part(instr->mtd);
++ size_t wrlen = 0;
++
++ if (instr->mtd->flags & MTD_ERASE_PARTIAL) {
++ if (instr->partial_start) {
++ part->parent->_write(part->parent,
++ instr->addr, instr->erase_buf_ofs,
++ &wrlen, instr->erase_buf);
++ instr->addr += instr->erase_buf_ofs;
++ } else {
++ instr->len -= instr->erase_buf_ofs;
++ part->parent->_write(part->parent,
++ instr->addr + instr->len,
++ instr->erase_buf_ofs, &wrlen,
++ instr->erase_buf +
++ part->parent->erasesize -
++ instr->erase_buf_ofs);
++ }
++ kfree(instr->erase_buf);
++ }
+
+ if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
+ instr->fail_addr -= part->offset;
+@@ -598,19 +667,22 @@ static struct mtd_part *allocate_partiti
+ remainder = do_div(tmp, wr_alignment);
+ if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
+ /* Doesn't start on a boundary of major erase size */
+- /* FIXME: Let it be writable if it is on a boundary of
+- * _minor_ erase size though */
+- slave->mtd.flags &= ~MTD_WRITEABLE;
+- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+- part->name);
++ slave->mtd.flags |= MTD_ERASE_PARTIAL;
++ if (((u32)slave->mtd.size) > parent->erasesize)
++ slave->mtd.flags &= ~MTD_WRITEABLE;
++ else
++ slave->mtd.erasesize = slave->mtd.size;
+ }
+
+- tmp = slave->mtd.size;
++ tmp = slave->offset + slave->mtd.size;
+ remainder = do_div(tmp, wr_alignment);
+ if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
+- slave->mtd.flags &= ~MTD_WRITEABLE;
+- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
+- part->name);
++ slave->mtd.flags |= MTD_ERASE_PARTIAL;
++
++ if ((u32)slave->mtd.size > parent->erasesize)
++ slave->mtd.flags &= ~MTD_WRITEABLE;
++ else
++ slave->mtd.erasesize = slave->mtd.size;
+ }
+
+ mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -56,6 +56,10 @@ struct erase_info {
+ u_long priv;
+ u_char state;
+ struct erase_info *next;
++
++ u8 *erase_buf;
++ u32 erase_buf_ofs;
++ bool partial_start;
+ };
+
+ struct mtd_erase_region_info {