mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05: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;
 |