kernel: support "linux,rootfs" DT property for splitting rootfs
[openwrt/staging/hauke.git] / target / linux / generic / pending-5.15 / 400-mtd-mtdsplit-support.patch
1 From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001
2 From: OpenWrt community <openwrt-devel@lists.openwrt.org>
3 Date: Wed, 13 Jul 2022 11:47:35 +0200
4 Subject: [PATCH] mtd: mtdsplit support
5
6 ---
7 drivers/mtd/Kconfig | 19 ++++
8 drivers/mtd/Makefile | 2 +
9 drivers/mtd/mtdpart.c | 169 ++++++++++++++++++++++++++++-----
10 include/linux/mtd/mtd.h | 25 +++++
11 include/linux/mtd/partitions.h | 7 ++
12 5 files changed, 197 insertions(+), 25 deletions(-)
13
14 --- a/drivers/mtd/Kconfig
15 +++ b/drivers/mtd/Kconfig
16 @@ -12,6 +12,25 @@ menuconfig MTD
17
18 if MTD
19
20 +menu "OpenWrt specific MTD options"
21 +
22 +config MTD_ROOTFS_ROOT_DEV
23 + bool "Automatically set 'rootfs' partition to be root filesystem"
24 + default y
25 +
26 +config MTD_SPLIT_FIRMWARE
27 + bool "Automatically split firmware partition for kernel+rootfs"
28 + default y
29 +
30 +config MTD_SPLIT_FIRMWARE_NAME
31 + string "Firmware partition name"
32 + depends on MTD_SPLIT_FIRMWARE
33 + default "firmware"
34 +
35 +source "drivers/mtd/mtdsplit/Kconfig"
36 +
37 +endmenu
38 +
39 config MTD_TESTS
40 tristate "MTD tests support (DANGEROUS)"
41 depends on m
42 --- a/drivers/mtd/Makefile
43 +++ b/drivers/mtd/Makefile
44 @@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc
45
46 obj-y += parsers/
47
48 +obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
49 +
50 # 'Users' - code which presents functionality to userspace.
51 obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
52 obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
53 --- a/drivers/mtd/mtdpart.c
54 +++ b/drivers/mtd/mtdpart.c
55 @@ -15,11 +15,13 @@
56 #include <linux/kmod.h>
57 #include <linux/mtd/mtd.h>
58 #include <linux/mtd/partitions.h>
59 +#include <linux/magic.h>
60 #include <linux/err.h>
61 #include <linux/of.h>
62 #include <linux/of_platform.h>
63
64 #include "mtdcore.h"
65 +#include "mtdsplit/mtdsplit.h"
66
67 /*
68 * MTD methods which simply translate the effective address and pass through
69 @@ -236,6 +238,147 @@ static int mtd_add_partition_attrs(struc
70 return ret;
71 }
72
73 +static DEFINE_SPINLOCK(part_parser_lock);
74 +static LIST_HEAD(part_parsers);
75 +
76 +static struct mtd_part_parser *mtd_part_parser_get(const char *name)
77 +{
78 + struct mtd_part_parser *p, *ret = NULL;
79 +
80 + spin_lock(&part_parser_lock);
81 +
82 + list_for_each_entry(p, &part_parsers, list)
83 + if (!strcmp(p->name, name) && try_module_get(p->owner)) {
84 + ret = p;
85 + break;
86 + }
87 +
88 + spin_unlock(&part_parser_lock);
89 +
90 + return ret;
91 +}
92 +
93 +static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
94 +{
95 + module_put(p->owner);
96 +}
97 +
98 +static struct mtd_part_parser *
99 +get_partition_parser_by_type(enum mtd_parser_type type,
100 + struct mtd_part_parser *start)
101 +{
102 + struct mtd_part_parser *p, *ret = NULL;
103 +
104 + spin_lock(&part_parser_lock);
105 +
106 + p = list_prepare_entry(start, &part_parsers, list);
107 + if (start)
108 + mtd_part_parser_put(start);
109 +
110 + list_for_each_entry_continue(p, &part_parsers, list) {
111 + if (p->type == type && try_module_get(p->owner)) {
112 + ret = p;
113 + break;
114 + }
115 + }
116 +
117 + spin_unlock(&part_parser_lock);
118 +
119 + return ret;
120 +}
121 +
122 +static int parse_mtd_partitions_by_type(struct mtd_info *master,
123 + enum mtd_parser_type type,
124 + const struct mtd_partition **pparts,
125 + struct mtd_part_parser_data *data)
126 +{
127 + struct mtd_part_parser *prev = NULL;
128 + int ret = 0;
129 +
130 + while (1) {
131 + struct mtd_part_parser *parser;
132 +
133 + parser = get_partition_parser_by_type(type, prev);
134 + if (!parser)
135 + break;
136 +
137 + ret = (*parser->parse_fn)(master, pparts, data);
138 +
139 + if (ret > 0) {
140 + mtd_part_parser_put(parser);
141 + printk(KERN_NOTICE
142 + "%d %s partitions found on MTD device %s\n",
143 + ret, parser->name, master->name);
144 + break;
145 + }
146 +
147 + prev = parser;
148 + }
149 +
150 + return ret;
151 +}
152 +
153 +static int
154 +run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
155 +{
156 + struct mtd_partition *parts;
157 + int nr_parts;
158 + int i;
159 +
160 + nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
161 + NULL);
162 + if (nr_parts <= 0)
163 + return nr_parts;
164 +
165 + if (WARN_ON(!parts))
166 + return 0;
167 +
168 + for (i = 0; i < nr_parts; i++) {
169 + /* adjust partition offsets */
170 + parts[i].offset += child->part.offset;
171 +
172 + mtd_add_partition(child->parent,
173 + parts[i].name,
174 + parts[i].offset,
175 + parts[i].size);
176 + }
177 +
178 + kfree(parts);
179 +
180 + return nr_parts;
181 +}
182 +
183 +#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
184 +#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME
185 +#else
186 +#define SPLIT_FIRMWARE_NAME "unused"
187 +#endif
188 +
189 +static void split_firmware(struct mtd_info *master, struct mtd_info *part)
190 +{
191 + run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
192 +}
193 +
194 +static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
195 +{
196 + static int rootfs_found = 0;
197 +
198 + if (rootfs_found)
199 + return;
200 +
201 + if (of_find_property(mtd_get_of_node(part), "linux,rootfs", NULL) ||
202 + !strcmp(part->name, "rootfs")) {
203 + run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
204 +
205 + rootfs_found = 1;
206 + }
207 +
208 + if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
209 + !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
210 + !of_find_property(mtd_get_of_node(part), "compatible", NULL))
211 + split_firmware(master, part);
212 +}
213 +
214 int mtd_add_partition(struct mtd_info *parent, const char *name,
215 long long offset, long long length)
216 {
217 @@ -274,6 +417,7 @@ int mtd_add_partition(struct mtd_info *p
218 if (ret)
219 goto err_remove_part;
220
221 + mtd_partition_split(parent, child);
222 mtd_add_partition_attrs(child);
223
224 return 0;
225 @@ -422,6 +566,7 @@ int add_mtd_partitions(struct mtd_info *
226 goto err_del_partitions;
227 }
228
229 + mtd_partition_split(master, child);
230 mtd_add_partition_attrs(child);
231
232 /* Look for subpartitions */
233 @@ -438,31 +583,6 @@ err_del_partitions:
234 return ret;
235 }
236
237 -static DEFINE_SPINLOCK(part_parser_lock);
238 -static LIST_HEAD(part_parsers);
239 -
240 -static struct mtd_part_parser *mtd_part_parser_get(const char *name)
241 -{
242 - struct mtd_part_parser *p, *ret = NULL;
243 -
244 - spin_lock(&part_parser_lock);
245 -
246 - list_for_each_entry(p, &part_parsers, list)
247 - if (!strcmp(p->name, name) && try_module_get(p->owner)) {
248 - ret = p;
249 - break;
250 - }
251 -
252 - spin_unlock(&part_parser_lock);
253 -
254 - return ret;
255 -}
256 -
257 -static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
258 -{
259 - module_put(p->owner);
260 -}
261 -
262 /*
263 * Many partition parsers just expected the core to kfree() all their data in
264 * one chunk. Do that by default.
265 --- a/include/linux/mtd/mtd.h
266 +++ b/include/linux/mtd/mtd.h
267 @@ -613,6 +613,24 @@ static inline void mtd_align_erase_req(s
268 req->len += mtd->erasesize - mod;
269 }
270
271 +static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
272 +{
273 + if (mtd_mod_by_eb(sz, mtd) == 0)
274 + return sz;
275 +
276 + /* Round up to next erase block */
277 + return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
278 +}
279 +
280 +static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
281 +{
282 + if (mtd_mod_by_eb(sz, mtd) == 0)
283 + return sz;
284 +
285 + /* Round down to the start of the current erase block */
286 + return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
287 +}
288 +
289 static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
290 {
291 if (mtd->writesize_shift)
292 @@ -686,6 +704,13 @@ extern struct mtd_info *of_get_mtd_devic
293 extern struct mtd_info *get_mtd_device_nm(const char *name);
294 extern void put_mtd_device(struct mtd_info *mtd);
295
296 +static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
297 +{
298 + if (!mtd_is_partition(mtd))
299 + return 0;
300 +
301 + return mtd->part.offset;
302 +}
303
304 struct mtd_notifier {
305 void (*add)(struct mtd_info *mtd);
306 --- a/include/linux/mtd/partitions.h
307 +++ b/include/linux/mtd/partitions.h
308 @@ -75,6 +75,12 @@ struct mtd_part_parser_data {
309 * Functions dealing with the various ways of partitioning the space
310 */
311
312 +enum mtd_parser_type {
313 + MTD_PARSER_TYPE_DEVICE = 0,
314 + MTD_PARSER_TYPE_ROOTFS,
315 + MTD_PARSER_TYPE_FIRMWARE,
316 +};
317 +
318 struct mtd_part_parser {
319 struct list_head list;
320 struct module *owner;
321 @@ -83,6 +89,7 @@ struct mtd_part_parser {
322 int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
323 struct mtd_part_parser_data *);
324 void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
325 + enum mtd_parser_type type;
326 };
327
328 /* Container for passing around a set of parsed partitions */