Skip to content

Commit

Permalink
mtd: spi-nor: write support for minor aligned partitions
Browse files Browse the repository at this point in the history
Do not prevent writing to mtd partitions where a partition boundary sits
on a minor erasesize boundary.
This addresses a FIXME that has been present since the start of the
linux git history:
/* 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 */

Allow a uniform erase region spi-nor device to be configured
to use the non-uniform erase regions code path for an erase with:
CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y

On supporting hardware (SECT_4K: majority of current SPI-NOR device)
provide the facility for an erase to use the least number
of SPI-NOR operations, as well as access to 4K erase without
requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS

Introduce erasesize_minor to the mtd struct,
the smallest erasesize supported by the device

On existing devices, this is useful where write support is wanted
for data on a 4K partition, such as some u-boot-env partitions,
or RouterBoot soft_config, while still netting the performance
benefits of using 64K sectors

Performance:
time mtd erase firmware
OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length

Without this patch
MTD_SPI_NOR_USE_4K_SECTORS=y	|n
real    2m 11.66s		|0m 50.86s
user    0m 0.00s		|0m 0.00s
sys     1m 56.20s		|0m 50.80s

With this patch
MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y		|4K_SECTORS=y
real    0m 51.68s		|0m 50.85s	|2m 12.89s
user    0m 0.00s		|0m 0.00s	|0m 0.01s
sys     0m 46.94s		|0m 50.38s	|2m 12.46s

Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au>
Signed-off-by: Thibaut VARÈNE <hacks+kernel@slashdirt.org>
  • Loading branch information
john-tho authored and dangowrt committed Mar 30, 2023
1 parent 1775bbd commit 0ef4314
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 12 deletions.
10 changes: 10 additions & 0 deletions drivers/mtd/mtdcore.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
Expand Down
35 changes: 25 additions & 10 deletions drivers/mtd/mtdpart.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -163,33 +164,47 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent,
} else {
/* Single erase size */
child->erasesize = master->erasesize;
child->erasesize_minor = master->erasesize_minor;
}

/*
* Child erasesize might differ from the parent one if the 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;
printk(KERN_WARNING"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;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
part->name);
}
}

child->size = child->part.size;
Expand Down
10 changes: 10 additions & 0 deletions drivers/mtd/spi-nor/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR

if MTD_SPI_NOR

config MTD_SPI_NOR_USE_VARIABLE_ERASE
bool "Disable uniform_erase to allow use of all hardware supported erasesizes"
depends on !MTD_SPI_NOR_USE_4K_SECTORS
default n
help
Allow mixed use of all hardware supported erasesizes,
by forcing spi_nor to use the multiple eraseregions code path.
For example: A 68K erase will use one 64K erase, and one 4K erase
on supporting hardware.

config MTD_SPI_NOR_USE_4K_SECTORS
bool "Use small 4096 B erase sectors"
default y
Expand Down
11 changes: 9 additions & 2 deletions drivers/mtd/spi-nor/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,8 @@ static u8 spi_nor_convert_3to4_erase(u8 opcode)

static bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE))
return false;
return !!nor->params->erase_map.uniform_erase_type;
}

Expand Down Expand Up @@ -2548,6 +2550,7 @@ static int spi_nor_select_erase(struct spi_nor *nor)
{
struct spi_nor_erase_map *map = &nor->params->erase_map;
const struct spi_nor_erase_type *erase = NULL;
const struct spi_nor_erase_type *erase_minor = NULL;
struct mtd_info *mtd = &nor->mtd;
u32 wanted_size = nor->info->sector_size;
int i;
Expand Down Expand Up @@ -2580,15 +2583,19 @@ static int spi_nor_select_erase(struct spi_nor *nor)
*/
for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
if (map->erase_type[i].size) {
erase = &map->erase_type[i];
break;
if (!erase)
erase = &map->erase_type[i];
erase_minor = &map->erase_type[i];
}
}

if (!erase)
return -EINVAL;

mtd->erasesize = erase->size;
if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) &&
erase_minor && erase_minor->size < erase->size)
mtd->erasesize_minor = erase_minor->size;
return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions include/linux/mtd/mtd.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,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
Expand Down

0 comments on commit 0ef4314

Please sign in to comment.