[PATCH v3 1/3] mtd: mtdpart: write support for minor-aligned partitions

Thibaut VARÈNE hacks+kernel at slashdirt.org
Fri Jul 29 02:16:34 PDT 2022


This patch enables writing to mtd partitions where a partition boundary
sits on a "minor" erasesize boundary.

This patch adds a uint32_t `erasesize_minor` to struct mtd_info: the
smallest erasesize supported by the device, also exposed in sysfs.

This patch is a no-op if erasesize_minor is unset (0) and has no
userspace-visible side effect (in particular the reported erasesize in
/proc/mtd is unchanged). This new feature is only enabled for single
eraseregion devices, where it makes most sense.

This patch addresses an outstanding mtdpart.c FIXME that has been
present since the start of the linux git history.

Signed-off-by: John Thomson <git at johnthomson.fastmail.com.au>
Signed-off-by: Thibaut VARÈNE <hacks+kernel at slashdirt.org>
---
 Documentation/ABI/testing/sysfs-class-mtd |  8 ++++++
 drivers/mtd/mtdcore.c                     | 10 +++++++
 drivers/mtd/mtdpart.c                     | 35 ++++++++++++++++-------
 include/linux/mtd/mtd.h                   |  2 ++
 4 files changed, 45 insertions(+), 10 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd
index 3bc7c0a95..786068f63 100644
--- a/Documentation/ABI/testing/sysfs-class-mtd
+++ b/Documentation/ABI/testing/sysfs-class-mtd
@@ -240,3 +240,11 @@ Contact:	linux-mtd at lists.infradead.org
 Description:
 		Number of bytes available for a client to place data into
 		the out of band area.
+
+What:		/sys/class/mtd/mtdX/erasesize_minor
+Date:		July 2022
+KernelVersion:	5.20
+Contact:	linux-mtd at lists.infradead.org
+Description:
+		"Minor" erase size for the device. If supported, this is
+		the smallest eraseblock size for the device.
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 9eb0680db..ccb80197d 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -168,6 +168,15 @@ static ssize_t mtd_erasesize_show(struct device *dev,
 }
 MTD_DEVICE_ATTR_RO(erasesize);
 
+static ssize_t mtd_erasesize_minor_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct mtd_info *mtd = dev_get_drvdata(dev);
+
+	return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor);
+}
+MTD_DEVICE_ATTR_RO(erasesize_minor);
+
 static ssize_t mtd_writesize_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
@@ -313,6 +322,7 @@ static struct attribute *mtd_attrs[] = {
 	&dev_attr_flags.attr,
 	&dev_attr_size.attr,
 	&dev_attr_erasesize.attr,
+	&dev_attr_erasesize_minor.attr,
 	&dev_attr_writesize.attr,
 	&dev_attr_subpagesize.attr,
 	&dev_attr_oobsize.attr,
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index d442fa94c..6bf21567f 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -39,6 +39,7 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent,
 	struct mtd_info *master = mtd_get_master(parent);
 	int wr_alignment = (parent->flags & MTD_NO_ERASE) ?
 			   master->writesize : master->erasesize;
+	int wr_alignment_minor = 0;
 	u64 parent_size = mtd_is_partition(parent) ?
 			  parent->part.size : parent->size;
 	struct mtd_info *child;
@@ -163,6 +164,7 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent,
 	} else {
 		/* Single erase size */
 		child->erasesize = master->erasesize;
+		child->erasesize_minor = master->erasesize_minor;
 	}
 
 	/*
@@ -170,26 +172,39 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent,
 	 * exposes several regions with different erasesize. Adjust
 	 * wr_alignment accordingly.
 	 */
-	if (!(child->flags & MTD_NO_ERASE))
+	if (!(child->flags & MTD_NO_ERASE)) {
 		wr_alignment = child->erasesize;
+		wr_alignment_minor = child->erasesize_minor;
+	}
 
 	tmp = mtd_get_master_ofs(child, 0);
 	remainder = do_div(tmp, wr_alignment);
 	if ((child->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 */
-		child->flags &= ~MTD_WRITEABLE;
-		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
-			part->name);
+		if (wr_alignment_minor) {
+			/* rely on minor being a factor of major erasesize */
+			tmp = remainder;
+			remainder = do_div(tmp, wr_alignment_minor);
+		}
+		if (remainder) {
+			child->flags &= ~MTD_WRITEABLE;
+			pr_warn("mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+				part->name);
+		}
 	}
 
 	tmp = mtd_get_master_ofs(child, 0) + child->part.size;
 	remainder = do_div(tmp, wr_alignment);
 	if ((child->flags & MTD_WRITEABLE) && remainder) {
-		child->flags &= ~MTD_WRITEABLE;
-		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
-			part->name);
+		if (wr_alignment_minor) {
+			tmp = remainder;
+			remainder = do_div(tmp, wr_alignment_minor);
+		}
+
+		if (remainder) {
+			child->flags &= ~MTD_WRITEABLE;
+			pr_warn("mtd: partition \"%s\" doesn't end on an erase/write block boundary -- force read-only\n",
+				part->name);
+		}
 	}
 
 	child->size = child->part.size;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 955aee14b..8efbc929e 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -238,6 +238,8 @@ struct mtd_info {
 	 * information below if they desire
 	 */
 	uint32_t erasesize;
+	/* "Minor" (smallest) erase size supported by the whole device */
+	uint32_t erasesize_minor;
 	/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
 	 * though individual bits can be cleared), in case of NAND flash it is
 	 * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
-- 
2.30.2




More information about the linux-mtd mailing list