allocate_partition() checks a partition offset and size against the
erase size of its master. A device which cannot erase has no erase
size, so the check divides by zero and "mtd list" ends in

        ### ERROR ### Please RESET the board ###

Align on the write size where there is no erase size, the way Linux
does in allocate_partition(). The MTDPART_OFS_NXTBLK rounding divides
by the erase size too and takes the same alignment.

A driver which declares neither size gets an alignment of 1 and a
WARN_ON(), as a missing size must not take the boot down.

Signed-off-by: Heiko Schocher <[email protected]>
Reviewed-by: Simon Glass <[email protected]>
---
checkpatch reports two warnings and four checks on the two
printk(KERN_WARNING"mtd: partition ... force read-only\n") lines.
This patch only changes s/"erase block"/"erase/write block" in the
string of the print, so ignoring the warnings.

Changes in v3:
- The two alignment checks no longer rely on MTD_NO_ERASE being set
  correctly. An alignment of zero counts as 1 with a WARN_ON(), and the
  partition erase size is only taken when there is one (Simon)
- Updated the comment above the first check, which still spoke of the
  erase size alone (Simon)
- Added Reviewed-by from Simon

Changes in v2:
- New patch mtd: align a partition on the write size if the device cannot erase
  Testing on the board dropped for "mtd list" command:
  ### ERROR ### Please RESET the board ###
  This is because division by erase_size, which the SPI RAM driver
  sets to 0. Added for this case also sandbox test.

 drivers/mtd/mtdpart.c | 44 +++++++++++++++++++++++++++++++++----------
 1 file changed, 34 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index a3003cf44b0..ca13e18e353 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -563,8 +563,19 @@ static struct mtd_info *allocate_partition(struct mtd_info 
*master,
                                           const struct mtd_partition *part,
                                           int partno, uint64_t cur_offset)
 {
+       u32 wr_alignment = (master->flags & MTD_NO_ERASE) ? master->writesize :
+                                                           master->erasesize;
        struct mtd_info *slave;
+       u32 remainder;
        char *name;
+       u64 tmp;
+
+       /*
+        * A master that declares neither of the two sizes leaves nothing to
+        * align to. Take every offset as aligned instead of dividing by zero.
+        */
+       if (WARN_ON(!wr_alignment))
+               wr_alignment = 1;
 
        /* allocate the partition structure */
        slave = kzalloc(sizeof(*slave), GFP_KERNEL);
@@ -665,9 +676,11 @@ static struct mtd_info *allocate_partition(struct mtd_info 
*master,
                slave->offset = cur_offset;
        if (slave->offset == MTDPART_OFS_NXTBLK) {
                slave->offset = cur_offset;
-               if (mtd_mod_by_eb(cur_offset, master) != 0) {
-                       /* Round up to next erasesize */
-                       slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) 
* master->erasesize;
+               tmp = cur_offset;
+               remainder = do_div(tmp, wr_alignment);
+               if (remainder) {
+                       /* Round up to the next alignment boundary */
+                       slave->offset += wr_alignment - remainder;
                        debug("Moving partition %d: "
                               "0x%012llx -> 0x%012llx\n", partno,
                               (unsigned long long)cur_offset, (unsigned long 
long)slave->offset);
@@ -731,19 +744,30 @@ static struct mtd_info *allocate_partition(struct 
mtd_info *master,
                slave->erasesize = master->erasesize;
        }
 
-       if ((slave->flags & MTD_WRITEABLE) &&
-           mtd_mod_by_eb(slave->offset, slave)) {
-               /* Doesn't start on a boundary of major erase size */
+       /*
+        * The erase size of the partition may differ from the one of the
+        * master if the master exposes several erase regions. Align to the
+        * one the partition ended up with, as long as there is one.
+        */
+       if (!(slave->flags & MTD_NO_ERASE) && slave->erasesize)
+               wr_alignment = slave->erasesize;
+
+       tmp = slave->offset;
+       remainder = do_div(tmp, wr_alignment);
+       if ((slave->flags & MTD_WRITEABLE) && remainder) {
+               /* Doesn't start on a boundary of the alignment above */
                /* FIXME: Let it be writable if it is on a boundary of
                 * _minor_ erase size though */
                slave->flags &= ~MTD_WRITEABLE;
-               printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an 
erase block boundary -- force read-only\n",
+               printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an 
erase/write block boundary -- force read-only\n",
                        part->name);
        }
-       if ((slave->flags & MTD_WRITEABLE) &&
-           mtd_mod_by_eb(slave->size, slave)) {
+
+       tmp = slave->size;
+       remainder = do_div(tmp, wr_alignment);
+       if ((slave->flags & MTD_WRITEABLE) && remainder) {
                slave->flags &= ~MTD_WRITEABLE;
-               printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an 
erase block -- force read-only\n",
+               printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an 
erase/write block -- force read-only\n",
                        part->name);
        }
 
-- 
2.55.0

Reply via email to