/branches/18.06.1/target/linux/generic/pending-4.9/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 |
@@ -37,6 +37,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); |
@@ -246,13 +248,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; |
} |
|
@@ -260,6 +310,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; |
@@ -566,19 +635,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 |
@@ -55,6 +55,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 { |