f600976065a72dcb14edd828aa0c6b903d69d59b
[openwrt/openwrt.git] / target / linux / generic / pending-5.15 / 402-mtd-spi-nor-write-support-for-minor-aligned-partitions.patch
1 From acacdac272927ae1d96e0bca51eb82899671eaea Mon Sep 17 00:00:00 2001
2 From: John Thomson <git@johnthomson.fastmail.com.au>
3 Date: Fri, 25 Dec 2020 18:50:08 +1000
4 Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
8
9 Do not prevent writing to mtd partitions where a partition boundary sits
10 on a minor erasesize boundary.
11 This addresses a FIXME that has been present since the start of the
12 linux git history:
13 /* Doesn't start on a boundary of major erase size */
14 /* FIXME: Let it be writable if it is on a boundary of
15 * _minor_ erase size though */
16
17 Allow a uniform erase region spi-nor device to be configured
18 to use the non-uniform erase regions code path for an erase with:
19 CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
20
21 On supporting hardware (SECT_4K: majority of current SPI-NOR device)
22 provide the facility for an erase to use the least number
23 of SPI-NOR operations, as well as access to 4K erase without
24 requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
25
26 Introduce erasesize_minor to the mtd struct,
27 the smallest erasesize supported by the device
28
29 On existing devices, this is useful where write support is wanted
30 for data on a 4K partition, such as some u-boot-env partitions,
31 or RouterBoot soft_config, while still netting the performance
32 benefits of using 64K sectors
33
34 Performance:
35 time mtd erase firmware
36 OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length
37
38 Without this patch
39 MTD_SPI_NOR_USE_4K_SECTORS=y |n
40 real 2m 11.66s |0m 50.86s
41 user 0m 0.00s |0m 0.00s
42 sys 1m 56.20s |0m 50.80s
43
44 With this patch
45 MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y
46 real 0m 51.68s |0m 50.85s |2m 12.89s
47 user 0m 0.00s |0m 0.00s |0m 0.01s
48 sys 0m 46.94s |0m 50.38s |2m 12.46s
49
50 Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au>
51 Signed-off-by: Thibaut VARĂˆNE <hacks+kernel@slashdirt.org>
52
53 ---
54
55 checkpatch does not like the printk(KERN_WARNING
56 these should be changed separately beforehand?
57
58 Changes v1 -> v2:
59 Added mtdcore sysfs for erasesize_minor
60 Removed finding minor erasesize for variable erase regions device,
61 as untested and no responses regarding it.
62 Moved IF_ENABLED for SPINOR variable erase to guard setting
63 erasesize_minor in spi-nor/core.c
64 Removed setting erasesize to minor where partition boundaries require
65 minor erase to be writable
66 Simplified minor boundary check by relying on minor being a factor of
67 major
68
69 Changes RFC -> v1:
70 Fix uninitialized variable smatch warning
71 Reported-by: kernel test robot <lkp@intel.com>
72 Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
73 ---
74 drivers/mtd/mtdcore.c | 10 ++++++++++
75 drivers/mtd/mtdpart.c | 35 +++++++++++++++++++++++++----------
76 drivers/mtd/spi-nor/Kconfig | 10 ++++++++++
77 drivers/mtd/spi-nor/core.c | 11 +++++++++--
78 include/linux/mtd/mtd.h | 2 ++
79 5 files changed, 56 insertions(+), 12 deletions(-)
80
81 --- a/drivers/mtd/mtdcore.c
82 +++ b/drivers/mtd/mtdcore.c
83 @@ -169,6 +169,15 @@ static ssize_t mtd_erasesize_show(struct
84 }
85 MTD_DEVICE_ATTR_RO(erasesize);
86
87 +static ssize_t mtd_erasesize_minor_show(struct device *dev,
88 + struct device_attribute *attr, char *buf)
89 +{
90 + struct mtd_info *mtd = dev_get_drvdata(dev);
91 +
92 + return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor);
93 +}
94 +MTD_DEVICE_ATTR_RO(erasesize_minor);
95 +
96 static ssize_t mtd_writesize_show(struct device *dev,
97 struct device_attribute *attr, char *buf)
98 {
99 @@ -314,6 +323,7 @@ static struct attribute *mtd_attrs[] = {
100 &dev_attr_flags.attr,
101 &dev_attr_size.attr,
102 &dev_attr_erasesize.attr,
103 + &dev_attr_erasesize_minor.attr,
104 &dev_attr_writesize.attr,
105 &dev_attr_subpagesize.attr,
106 &dev_attr_oobsize.attr,
107 --- a/drivers/mtd/mtdpart.c
108 +++ b/drivers/mtd/mtdpart.c
109 @@ -41,6 +41,7 @@ static struct mtd_info *allocate_partiti
110 struct mtd_info *master = mtd_get_master(parent);
111 int wr_alignment = (parent->flags & MTD_NO_ERASE) ?
112 master->writesize : master->erasesize;
113 + int wr_alignment_minor = 0;
114 u64 parent_size = mtd_is_partition(parent) ?
115 parent->part.size : parent->size;
116 struct mtd_info *child;
117 @@ -165,6 +166,7 @@ static struct mtd_info *allocate_partiti
118 } else {
119 /* Single erase size */
120 child->erasesize = master->erasesize;
121 + child->erasesize_minor = master->erasesize_minor;
122 }
123
124 /*
125 @@ -172,26 +174,39 @@ static struct mtd_info *allocate_partiti
126 * exposes several regions with different erasesize. Adjust
127 * wr_alignment accordingly.
128 */
129 - if (!(child->flags & MTD_NO_ERASE))
130 + if (!(child->flags & MTD_NO_ERASE)) {
131 wr_alignment = child->erasesize;
132 + wr_alignment_minor = child->erasesize_minor;
133 + }
134
135 tmp = mtd_get_master_ofs(child, 0);
136 remainder = do_div(tmp, wr_alignment);
137 if ((child->flags & MTD_WRITEABLE) && remainder) {
138 - /* Doesn't start on a boundary of major erase size */
139 - /* FIXME: Let it be writable if it is on a boundary of
140 - * _minor_ erase size though */
141 - child->flags &= ~MTD_WRITEABLE;
142 - printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
143 - part->name);
144 + if (wr_alignment_minor) {
145 + /* rely on minor being a factor of major erasesize */
146 + tmp = remainder;
147 + remainder = do_div(tmp, wr_alignment_minor);
148 + }
149 + if (remainder) {
150 + child->flags &= ~MTD_WRITEABLE;
151 + printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
152 + part->name);
153 + }
154 }
155
156 tmp = mtd_get_master_ofs(child, 0) + child->part.size;
157 remainder = do_div(tmp, wr_alignment);
158 if ((child->flags & MTD_WRITEABLE) && remainder) {
159 - child->flags &= ~MTD_WRITEABLE;
160 - printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
161 - part->name);
162 + if (wr_alignment_minor) {
163 + tmp = remainder;
164 + remainder = do_div(tmp, wr_alignment_minor);
165 + }
166 +
167 + if (remainder) {
168 + child->flags &= ~MTD_WRITEABLE;
169 + printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
170 + part->name);
171 + }
172 }
173
174 child->size = child->part.size;
175 --- a/drivers/mtd/spi-nor/Kconfig
176 +++ b/drivers/mtd/spi-nor/Kconfig
177 @@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR
178
179 if MTD_SPI_NOR
180
181 +config MTD_SPI_NOR_USE_VARIABLE_ERASE
182 + bool "Disable uniform_erase to allow use of all hardware supported erasesizes"
183 + depends on !MTD_SPI_NOR_USE_4K_SECTORS
184 + default n
185 + help
186 + Allow mixed use of all hardware supported erasesizes,
187 + by forcing spi_nor to use the multiple eraseregions code path.
188 + For example: A 68K erase will use one 64K erase, and one 4K erase
189 + on supporting hardware.
190 +
191 config MTD_SPI_NOR_USE_4K_SECTORS
192 bool "Use small 4096 B erase sectors"
193 default y
194 --- a/drivers/mtd/spi-nor/core.c
195 +++ b/drivers/mtd/spi-nor/core.c
196 @@ -1271,6 +1271,8 @@ static u8 spi_nor_convert_3to4_erase(u8
197
198 static bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
199 {
200 + if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE))
201 + return false;
202 return !!nor->params->erase_map.uniform_erase_type;
203 }
204
205 @@ -2391,6 +2393,7 @@ static int spi_nor_select_erase(struct s
206 {
207 struct spi_nor_erase_map *map = &nor->params->erase_map;
208 const struct spi_nor_erase_type *erase = NULL;
209 + const struct spi_nor_erase_type *erase_minor = NULL;
210 struct mtd_info *mtd = &nor->mtd;
211 u32 wanted_size = nor->info->sector_size;
212 int i;
213 @@ -2423,8 +2426,9 @@ static int spi_nor_select_erase(struct s
214 */
215 for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
216 if (map->erase_type[i].size) {
217 - erase = &map->erase_type[i];
218 - break;
219 + if (!erase)
220 + erase = &map->erase_type[i];
221 + erase_minor = &map->erase_type[i];
222 }
223 }
224
225 @@ -2432,6 +2436,9 @@ static int spi_nor_select_erase(struct s
226 return -EINVAL;
227
228 mtd->erasesize = erase->size;
229 + if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) &&
230 + erase_minor && erase_minor->size < erase->size)
231 + mtd->erasesize_minor = erase_minor->size;
232 return 0;
233 }
234
235 --- a/include/linux/mtd/mtd.h
236 +++ b/include/linux/mtd/mtd.h
237 @@ -243,6 +243,8 @@ struct mtd_info {
238 * information below if they desire
239 */
240 uint32_t erasesize;
241 + /* "Minor" (smallest) erase size supported by the whole device */
242 + uint32_t erasesize_minor;
243 /* Minimal writable flash unit size. In case of NOR flash it is 1 (even
244 * though individual bits can be cleared), in case of NAND flash it is
245 * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR