 4ed347aaad
			
		
	
	4ed347aaad
	
	
	
		
			
			Refresh pending patches with make target/linux/refresh. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
		
			
				
	
	
		
			329 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001
 | |
| From: OpenWrt community <openwrt-devel@lists.openwrt.org>
 | |
| Date: Wed, 13 Jul 2022 11:47:35 +0200
 | |
| Subject: [PATCH] mtd: mtdsplit support
 | |
| 
 | |
| ---
 | |
|  drivers/mtd/Kconfig            |  19 ++++
 | |
|  drivers/mtd/Makefile           |   2 +
 | |
|  drivers/mtd/mtdpart.c          | 169 ++++++++++++++++++++++++++++-----
 | |
|  include/linux/mtd/mtd.h        |  25 +++++
 | |
|  include/linux/mtd/partitions.h |   7 ++
 | |
|  5 files changed, 197 insertions(+), 25 deletions(-)
 | |
| 
 | |
| --- a/drivers/mtd/Kconfig
 | |
| +++ b/drivers/mtd/Kconfig
 | |
| @@ -12,6 +12,25 @@ menuconfig MTD
 | |
|  
 | |
|  if MTD
 | |
|  
 | |
| +menu "OpenWrt specific MTD options"
 | |
| +
 | |
| +config MTD_ROOTFS_ROOT_DEV
 | |
| +	bool "Automatically set 'rootfs' partition to be root filesystem"
 | |
| +	default y
 | |
| +
 | |
| +config MTD_SPLIT_FIRMWARE
 | |
| +	bool "Automatically split firmware partition for kernel+rootfs"
 | |
| +	default y
 | |
| +
 | |
| +config MTD_SPLIT_FIRMWARE_NAME
 | |
| +	string "Firmware partition name"
 | |
| +	depends on MTD_SPLIT_FIRMWARE
 | |
| +	default "firmware"
 | |
| +
 | |
| +source "drivers/mtd/mtdsplit/Kconfig"
 | |
| +
 | |
| +endmenu
 | |
| +
 | |
|  config MTD_TESTS
 | |
|  	tristate "MTD tests support (DANGEROUS)"
 | |
|  	depends on m
 | |
| --- a/drivers/mtd/Makefile
 | |
| +++ b/drivers/mtd/Makefile
 | |
| @@ -9,6 +9,8 @@ mtd-y				:= mtdcore.o mtdsuper.o mtdconc
 | |
|  
 | |
|  obj-y				+= parsers/
 | |
|  
 | |
| +obj-$(CONFIG_MTD_SPLIT)		+= mtdsplit/
 | |
| +
 | |
|  # 'Users' - code which presents functionality to userspace.
 | |
|  obj-$(CONFIG_MTD_BLKDEVS)	+= mtd_blkdevs.o
 | |
|  obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o
 | |
| --- a/drivers/mtd/mtdpart.c
 | |
| +++ b/drivers/mtd/mtdpart.c
 | |
| @@ -15,11 +15,13 @@
 | |
|  #include <linux/kmod.h>
 | |
|  #include <linux/mtd/mtd.h>
 | |
|  #include <linux/mtd/partitions.h>
 | |
| +#include <linux/magic.h>
 | |
|  #include <linux/err.h>
 | |
|  #include <linux/of.h>
 | |
|  #include <linux/of_platform.h>
 | |
|  
 | |
|  #include "mtdcore.h"
 | |
| +#include "mtdsplit/mtdsplit.h"
 | |
|  
 | |
|  /*
 | |
|   * MTD methods which simply translate the effective address and pass through
 | |
| @@ -236,6 +238,147 @@ static int mtd_add_partition_attrs(struc
 | |
|  	return ret;
 | |
|  }
 | |
|  
 | |
| +static DEFINE_SPINLOCK(part_parser_lock);
 | |
| +static LIST_HEAD(part_parsers);
 | |
| +
 | |
| +static struct mtd_part_parser *mtd_part_parser_get(const char *name)
 | |
| +{
 | |
| +	struct mtd_part_parser *p, *ret = NULL;
 | |
| +
 | |
| +	spin_lock(&part_parser_lock);
 | |
| +
 | |
| +	list_for_each_entry(p, &part_parsers, list)
 | |
| +		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
 | |
| +			ret = p;
 | |
| +			break;
 | |
| +		}
 | |
| +
 | |
| +	spin_unlock(&part_parser_lock);
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
 | |
| +{
 | |
| +	module_put(p->owner);
 | |
| +}
 | |
| +
 | |
| +static struct mtd_part_parser *
 | |
| +get_partition_parser_by_type(enum mtd_parser_type type,
 | |
| +			     struct mtd_part_parser *start)
 | |
| +{
 | |
| +	struct mtd_part_parser *p, *ret = NULL;
 | |
| +
 | |
| +	spin_lock(&part_parser_lock);
 | |
| +
 | |
| +	p = list_prepare_entry(start, &part_parsers, list);
 | |
| +	if (start)
 | |
| +		mtd_part_parser_put(start);
 | |
| +
 | |
| +	list_for_each_entry_continue(p, &part_parsers, list) {
 | |
| +		if (p->type == type && try_module_get(p->owner)) {
 | |
| +			ret = p;
 | |
| +			break;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	spin_unlock(&part_parser_lock);
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static int parse_mtd_partitions_by_type(struct mtd_info *master,
 | |
| +					enum mtd_parser_type type,
 | |
| +					const struct mtd_partition **pparts,
 | |
| +					struct mtd_part_parser_data *data)
 | |
| +{
 | |
| +	struct mtd_part_parser *prev = NULL;
 | |
| +	int ret = 0;
 | |
| +
 | |
| +	while (1) {
 | |
| +		struct mtd_part_parser *parser;
 | |
| +
 | |
| +		parser = get_partition_parser_by_type(type, prev);
 | |
| +		if (!parser)
 | |
| +			break;
 | |
| +
 | |
| +		ret = (*parser->parse_fn)(master, pparts, data);
 | |
| +
 | |
| +		if (ret > 0) {
 | |
| +			mtd_part_parser_put(parser);
 | |
| +			printk(KERN_NOTICE
 | |
| +			       "%d %s partitions found on MTD device %s\n",
 | |
| +			       ret, parser->name, master->name);
 | |
| +			break;
 | |
| +		}
 | |
| +
 | |
| +		prev = parser;
 | |
| +	}
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static int
 | |
| +run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
 | |
| +{
 | |
| +	struct mtd_partition *parts;
 | |
| +	int nr_parts;
 | |
| +	int i;
 | |
| +
 | |
| +	nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
 | |
| +						NULL);
 | |
| +	if (nr_parts <= 0)
 | |
| +		return nr_parts;
 | |
| +
 | |
| +	if (WARN_ON(!parts))
 | |
| +		return 0;
 | |
| +
 | |
| +	for (i = 0; i < nr_parts; i++) {
 | |
| +		/* adjust partition offsets */
 | |
| +		parts[i].offset += child->part.offset;
 | |
| +
 | |
| +		mtd_add_partition(child->parent,
 | |
| +				  parts[i].name,
 | |
| +				  parts[i].offset,
 | |
| +				  parts[i].size);
 | |
| +	}
 | |
| +
 | |
| +	kfree(parts);
 | |
| +
 | |
| +	return nr_parts;
 | |
| +}
 | |
| +
 | |
| +#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
 | |
| +#define SPLIT_FIRMWARE_NAME	CONFIG_MTD_SPLIT_FIRMWARE_NAME
 | |
| +#else
 | |
| +#define SPLIT_FIRMWARE_NAME	"unused"
 | |
| +#endif
 | |
| +
 | |
| +static void split_firmware(struct mtd_info *master, struct mtd_info *part)
 | |
| +{
 | |
| +	run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
 | |
| +}
 | |
| +
 | |
| +static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
 | |
| +{
 | |
| +	static int rootfs_found = 0;
 | |
| +
 | |
| +	if (rootfs_found)
 | |
| +		return;
 | |
| +
 | |
| +	if (of_find_property(mtd_get_of_node(part), "linux,rootfs", NULL) ||
 | |
| +	    !strcmp(part->name, "rootfs")) {
 | |
| +		run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
 | |
| +
 | |
| +		rootfs_found = 1;
 | |
| +	}
 | |
| +
 | |
| +	if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
 | |
| +	    !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
 | |
| +	    !of_find_property(mtd_get_of_node(part), "compatible", NULL))
 | |
| +		split_firmware(master, part);
 | |
| +}
 | |
| +
 | |
|  int mtd_add_partition(struct mtd_info *parent, const char *name,
 | |
|  		      long long offset, long long length)
 | |
|  {
 | |
| @@ -274,6 +417,7 @@ int mtd_add_partition(struct mtd_info *p
 | |
|  	if (ret)
 | |
|  		goto err_remove_part;
 | |
|  
 | |
| +	mtd_partition_split(parent, child);
 | |
|  	mtd_add_partition_attrs(child);
 | |
|  
 | |
|  	return 0;
 | |
| @@ -422,6 +566,7 @@ int add_mtd_partitions(struct mtd_info *
 | |
|  			goto err_del_partitions;
 | |
|  		}
 | |
|  
 | |
| +		mtd_partition_split(master, child);
 | |
|  		mtd_add_partition_attrs(child);
 | |
|  
 | |
|  		/* Look for subpartitions */
 | |
| @@ -438,31 +583,6 @@ err_del_partitions:
 | |
|  	return ret;
 | |
|  }
 | |
|  
 | |
| -static DEFINE_SPINLOCK(part_parser_lock);
 | |
| -static LIST_HEAD(part_parsers);
 | |
| -
 | |
| -static struct mtd_part_parser *mtd_part_parser_get(const char *name)
 | |
| -{
 | |
| -	struct mtd_part_parser *p, *ret = NULL;
 | |
| -
 | |
| -	spin_lock(&part_parser_lock);
 | |
| -
 | |
| -	list_for_each_entry(p, &part_parsers, list)
 | |
| -		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
 | |
| -			ret = p;
 | |
| -			break;
 | |
| -		}
 | |
| -
 | |
| -	spin_unlock(&part_parser_lock);
 | |
| -
 | |
| -	return ret;
 | |
| -}
 | |
| -
 | |
| -static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
 | |
| -{
 | |
| -	module_put(p->owner);
 | |
| -}
 | |
| -
 | |
|  /*
 | |
|   * Many partition parsers just expected the core to kfree() all their data in
 | |
|   * one chunk. Do that by default.
 | |
| --- a/include/linux/mtd/mtd.h
 | |
| +++ b/include/linux/mtd/mtd.h
 | |
| @@ -615,6 +615,24 @@ static inline void mtd_align_erase_req(s
 | |
|  		req->len += mtd->erasesize - mod;
 | |
|  }
 | |
|  
 | |
| +static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
 | |
| +{
 | |
| +	if (mtd_mod_by_eb(sz, mtd) == 0)
 | |
| +		return sz;
 | |
| +
 | |
| +	/* Round up to next erase block */
 | |
| +	return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
 | |
| +}
 | |
| +
 | |
| +static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
 | |
| +{
 | |
| +	if (mtd_mod_by_eb(sz, mtd) == 0)
 | |
| +		return sz;
 | |
| +
 | |
| +	/* Round down to the start of the current erase block */
 | |
| +	return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
 | |
| +}
 | |
| +
 | |
|  static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
 | |
|  {
 | |
|  	if (mtd->writesize_shift)
 | |
| @@ -688,6 +706,13 @@ extern struct mtd_info *of_get_mtd_devic
 | |
|  extern struct mtd_info *get_mtd_device_nm(const char *name);
 | |
|  extern void put_mtd_device(struct mtd_info *mtd);
 | |
|  
 | |
| +static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
 | |
| +{
 | |
| +	if (!mtd_is_partition(mtd))
 | |
| +		return 0;
 | |
| +
 | |
| +	return mtd->part.offset;
 | |
| +}
 | |
|  
 | |
|  struct mtd_notifier {
 | |
|  	void (*add)(struct mtd_info *mtd);
 | |
| --- a/include/linux/mtd/partitions.h
 | |
| +++ b/include/linux/mtd/partitions.h
 | |
| @@ -75,6 +75,12 @@ struct mtd_part_parser_data {
 | |
|   * Functions dealing with the various ways of partitioning the space
 | |
|   */
 | |
|  
 | |
| +enum mtd_parser_type {
 | |
| +	MTD_PARSER_TYPE_DEVICE = 0,
 | |
| +	MTD_PARSER_TYPE_ROOTFS,
 | |
| +	MTD_PARSER_TYPE_FIRMWARE,
 | |
| +};
 | |
| +
 | |
|  struct mtd_part_parser {
 | |
|  	struct list_head list;
 | |
|  	struct module *owner;
 | |
| @@ -83,6 +89,7 @@ struct mtd_part_parser {
 | |
|  	int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
 | |
|  			struct mtd_part_parser_data *);
 | |
|  	void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
 | |
| +	enum mtd_parser_type type;
 | |
|  };
 | |
|  
 | |
|  /* Container for passing around a set of parsed partitions */
 |