mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-25 02:54:28 -04:00 
			
		
		
		
	Similar to supporting nvmem-layouts on MTD devices, also allow referencing UBI and MMC devices in DT. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
		
			
				
	
	
		
			226 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From e5cf19bd8204925f3bd2067df9e867313eac388b Mon Sep 17 00:00:00 2001
 | |
| From: Daniel Golle <daniel@makrotopia.org>
 | |
| Date: Mon, 1 May 2023 11:57:51 +0100
 | |
| Subject: [PATCH 03/15] mtd: ubi: block: use notifier to create ubiblock from
 | |
|  parameter
 | |
| 
 | |
| Use UBI_VOLUME_ADDED notification to create ubiblock device specified
 | |
| on kernel cmdline or module parameter.
 | |
| This makes thing more simple and has the advantage that ubiblock devices
 | |
| on volumes which are not present at the time the ubi module is probed
 | |
| will still be created.
 | |
| 
 | |
| Suggested-by: Zhihao Cheng <chengzhihao1@huawei.com>
 | |
| Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 | |
| ---
 | |
|  drivers/mtd/ubi/block.c | 154 ++++++++++++++++++++++------------------
 | |
|  1 file changed, 85 insertions(+), 69 deletions(-)
 | |
| 
 | |
| --- a/drivers/mtd/ubi/block.c
 | |
| +++ b/drivers/mtd/ubi/block.c
 | |
| @@ -33,6 +33,7 @@
 | |
|  #include <linux/kernel.h>
 | |
|  #include <linux/list.h>
 | |
|  #include <linux/mutex.h>
 | |
| +#include <linux/namei.h>
 | |
|  #include <linux/slab.h>
 | |
|  #include <linux/mtd/ubi.h>
 | |
|  #include <linux/workqueue.h>
 | |
| @@ -67,10 +68,10 @@ struct ubiblock_pdu {
 | |
|  };
 | |
|  
 | |
|  /* Numbers of elements set in the @ubiblock_param array */
 | |
| -static int ubiblock_devs __initdata;
 | |
| +static int ubiblock_devs;
 | |
|  
 | |
|  /* MTD devices specification parameters */
 | |
| -static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata;
 | |
| +static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES];
 | |
|  
 | |
|  struct ubiblock {
 | |
|  	struct ubi_volume_desc *desc;
 | |
| @@ -504,7 +505,7 @@ int ubiblock_remove(struct ubi_volume_in
 | |
|  	}
 | |
|  
 | |
|  	/* Found a device, let's lock it so we can check if it's busy */
 | |
| -	mutex_lock(&dev->dev_mutex);
 | |
| +	mutex_lock_nested(&dev->dev_mutex, SINGLE_DEPTH_NESTING);
 | |
|  	if (dev->refcnt > 0) {
 | |
|  		ret = -EBUSY;
 | |
|  		goto out_unlock_dev;
 | |
| @@ -567,6 +568,85 @@ static int ubiblock_resize(struct ubi_vo
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| +static bool
 | |
| +match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id)
 | |
| +{
 | |
| +	int err, len;
 | |
| +	struct path path;
 | |
| +	struct kstat stat;
 | |
| +
 | |
| +	if (ubi_num == -1) {
 | |
| +		/* No ubi num, name must be a vol device path */
 | |
| +		err = kern_path(name, LOOKUP_FOLLOW, &path);
 | |
| +		if (err)
 | |
| +			return false;
 | |
| +
 | |
| +		err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
 | |
| +		path_put(&path);
 | |
| +		if (err)
 | |
| +			return false;
 | |
| +
 | |
| +		if (!S_ISCHR(stat.mode))
 | |
| +			return false;
 | |
| +
 | |
| +		if (vi->ubi_num != ubi_major2num(MAJOR(stat.rdev)))
 | |
| +			return false;
 | |
| +
 | |
| +		if (vi->vol_id != MINOR(stat.rdev) - 1)
 | |
| +			return false;
 | |
| +
 | |
| +		return true;
 | |
| +	}
 | |
| +
 | |
| +	if (vol_id == -1) {
 | |
| +		if (vi->ubi_num != ubi_num)
 | |
| +			return false;
 | |
| +
 | |
| +		len = strnlen(name, UBI_VOL_NAME_MAX + 1);
 | |
| +		if (len < 1 || vi->name_len != len)
 | |
| +			return false;
 | |
| +
 | |
| +		if (strcmp(name, vi->name))
 | |
| +			return false;
 | |
| +
 | |
| +		return true;
 | |
| +	}
 | |
| +
 | |
| +	if (vi->ubi_num != ubi_num)
 | |
| +		return false;
 | |
| +
 | |
| +	if (vi->vol_id != vol_id)
 | |
| +		return false;
 | |
| +
 | |
| +	return true;
 | |
| +}
 | |
| +
 | |
| +static void
 | |
| +ubiblock_create_from_param(struct ubi_volume_info *vi)
 | |
| +{
 | |
| +	int i, ret = 0;
 | |
| +	struct ubiblock_param *p;
 | |
| +
 | |
| +	/*
 | |
| +	 * Iterate over ubiblock cmdline parameters. If a parameter matches the
 | |
| +	 * newly added volume create the ubiblock device for it.
 | |
| +	 */
 | |
| +	for (i = 0; i < ubiblock_devs; i++) {
 | |
| +		p = &ubiblock_param[i];
 | |
| +
 | |
| +		if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
 | |
| +			continue;
 | |
| +
 | |
| +		ret = ubiblock_create(vi);
 | |
| +		if (ret) {
 | |
| +			pr_err(
 | |
| +			       "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
 | |
| +			       vi->name, p->ubi_num, p->vol_id, ret);
 | |
| +		}
 | |
| +		break;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
|  static int ubiblock_notify(struct notifier_block *nb,
 | |
|  			 unsigned long notification_type, void *ns_ptr)
 | |
|  {
 | |
| @@ -574,10 +654,7 @@ static int ubiblock_notify(struct notifi
 | |
|  
 | |
|  	switch (notification_type) {
 | |
|  	case UBI_VOLUME_ADDED:
 | |
| -		/*
 | |
| -		 * We want to enforce explicit block device creation for
 | |
| -		 * volumes, so when a volume is added we do nothing.
 | |
| -		 */
 | |
| +		ubiblock_create_from_param(&nt->vi);
 | |
|  		break;
 | |
|  	case UBI_VOLUME_REMOVED:
 | |
|  		ubiblock_remove(&nt->vi);
 | |
| @@ -603,56 +680,6 @@ static struct notifier_block ubiblock_no
 | |
|  	.notifier_call = ubiblock_notify,
 | |
|  };
 | |
|  
 | |
| -static struct ubi_volume_desc * __init
 | |
| -open_volume_desc(const char *name, int ubi_num, int vol_id)
 | |
| -{
 | |
| -	if (ubi_num == -1)
 | |
| -		/* No ubi num, name must be a vol device path */
 | |
| -		return ubi_open_volume_path(name, UBI_READONLY);
 | |
| -	else if (vol_id == -1)
 | |
| -		/* No vol_id, must be vol_name */
 | |
| -		return ubi_open_volume_nm(ubi_num, name, UBI_READONLY);
 | |
| -	else
 | |
| -		return ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
 | |
| -}
 | |
| -
 | |
| -static void __init ubiblock_create_from_param(void)
 | |
| -{
 | |
| -	int i, ret = 0;
 | |
| -	struct ubiblock_param *p;
 | |
| -	struct ubi_volume_desc *desc;
 | |
| -	struct ubi_volume_info vi;
 | |
| -
 | |
| -	/*
 | |
| -	 * If there is an error creating one of the ubiblocks, continue on to
 | |
| -	 * create the following ubiblocks. This helps in a circumstance where
 | |
| -	 * the kernel command-line specifies multiple block devices and some
 | |
| -	 * may be broken, but we still want the working ones to come up.
 | |
| -	 */
 | |
| -	for (i = 0; i < ubiblock_devs; i++) {
 | |
| -		p = &ubiblock_param[i];
 | |
| -
 | |
| -		desc = open_volume_desc(p->name, p->ubi_num, p->vol_id);
 | |
| -		if (IS_ERR(desc)) {
 | |
| -			pr_err(
 | |
| -			       "UBI: block: can't open volume on ubi%d_%d, err=%ld\n",
 | |
| -			       p->ubi_num, p->vol_id, PTR_ERR(desc));
 | |
| -			continue;
 | |
| -		}
 | |
| -
 | |
| -		ubi_get_volume_info(desc, &vi);
 | |
| -		ubi_close_volume(desc);
 | |
| -
 | |
| -		ret = ubiblock_create(&vi);
 | |
| -		if (ret) {
 | |
| -			pr_err(
 | |
| -			       "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
 | |
| -			       vi.name, p->ubi_num, p->vol_id, ret);
 | |
| -			continue;
 | |
| -		}
 | |
| -	}
 | |
| -}
 | |
| -
 | |
|  static void ubiblock_remove_all(void)
 | |
|  {
 | |
|  	struct ubiblock *next;
 | |
| @@ -678,18 +705,7 @@ int __init ubiblock_init(void)
 | |
|  	if (ubiblock_major < 0)
 | |
|  		return ubiblock_major;
 | |
|  
 | |
| -	/*
 | |
| -	 * Attach block devices from 'block=' module param.
 | |
| -	 * Even if one block device in the param list fails to come up,
 | |
| -	 * still allow the module to load and leave any others up.
 | |
| -	 */
 | |
| -	ubiblock_create_from_param();
 | |
| -
 | |
| -	/*
 | |
| -	 * Block devices are only created upon user requests, so we ignore
 | |
| -	 * existing volumes.
 | |
| -	 */
 | |
| -	ret = ubi_register_volume_notifier(&ubiblock_notifier, 1);
 | |
| +	ret = ubi_register_volume_notifier(&ubiblock_notifier, 0);
 | |
|  	if (ret)
 | |
|  		goto err_unreg;
 | |
|  	return 0;
 |