您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

SigmaStar SSD202 openwrt 系统下ubi根文件系统挂载过程

关于UBI介绍可以参考官方文档

http://www.linux-mtd.infradead.org/doc/ubifs.html

下面是一张简介图,大概的介绍就是UBIFS依赖kernel UBI子系统,运行在MTD设备之上,应用上UBI可做自己的分区管理

SSD202的简介可以参考我另一篇博文

目前国内有几个厂家在推广使用且出了相关的核心板,我目前使用的是启明云端的开发板(启明云端已移植好openwrt系统,我在此之上进行相关分析)

首先看系统分区:

root@wireless-tag:/# cat /proc/mtd 
dev:    size   erasesize  name
mtd0: 00060000 00020000 "IPL0"
mtd1: 00060000 00020000 "IPL1"
mtd2: 00060000 00020000 "IPL_CUST0"
mtd3: 00060000 00020000 "IPL_CUST1"
mtd4: 000c0000 00020000 "UBOOT0"
mtd5: 000c0000 00020000 "UBOOT1"
mtd6: 00060000 00020000 "ENV0"
mtd7: 00020000 00020000 "KEY_CUST"
mtd8: 00060000 00020000 "LOGO"
mtd9: 00060000 00020000 "wtinfo"
mtd10: 03000000 00020000 "ubi"
mtd11: 03000000 00020000 "ubi2"
mtd12: 09a80000 00020000 "opt"

Openwrt系统有个特点,Kernel和ROOTFS是一起打包的,且支持overlayfs,对应的分区为UBI,UBI2是备份分区,

其使用地方有两个:在uboot启动失败时使用和sysupgrade升级系统时使用,具体详细的分析在此不展开,后面有空再单独介绍

分析一个系统或者一个模块的执行过程,运行日志是很好的切入点,首先我们看下UBI相关的运行日志

[    1.971721] UBI: auto-attach mtd10
[    1.974967] ubi0: attaching mtd10
[    2.021832] UBI: EOF marker found, PEBs from 65 will be erased
[    2.097352] ubi0: scanning is finished
[    2.125345] ubi0: volume 2 ("rootfs_data") re-sized from 9 to 277 LEBs
[    2.132520] ubi0: attached mtd10 (name "ubi", size 48 MiB)
[    2.137870] ubi0: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes
[    2.144726] ubi0: min./max. I/O unit sizes: 2048/2048, sub-page size 2048
[    2.151529] ubi0: VID header offset: 2048 (aligned 2048), data offset: 4096
[    2.158487] ubi0: good PEBs: 384, bad PEBs: 0, corrupted PEBs: 0
[    2.164483] ubi0: user volume: 3, internal volumes: 1, max. volumes count: 128
[    2.171724] ubi0: max/mean erase counter: 1/0, WL threshold: 4096, image sequence number: 263505868
[    2.180769] ubi0: available PEBs: 0, total reserved PEBs: 384, PEBs reserved for bad PEB handling: 40
[    2.190013] ubi0: background thread "ubi_bgt0d" started, PID 504
[    2.205930] block ubiblock0_1: created from ubi0:1(rootfs)
[    2.211260] ubiblock: device ubiblock0_1 (rootfs) set to be root filesystem

其核心地方有3个

第一,UBI: auto-attach mtd10, auto-attach 名为自动挂载,理解这个之前我们得先看一下cmdline关于rootfs相关的部分“rootfstype=squashfs,ubifs rootwait=1”,启动参数中只指定了类型,没有指定rootdev,所以此处的auto-attach尤其重要

第二,ubi0: user volume: 3, internal volumes: 1, max. volumes count: 128,说明ubi0有3个卷,此处为ubifs的一个特点,也跟openwrt ubi.img制作的方式有关,3个卷分别为kernel,rootfs和rootfs_data(也就是overlay)

第三,ubiblock: device ubiblock0_1 (rootfs) set to be root filesystem, 即ubi0的rootfs卷被挂载为系统的rootfs

从这3处初略可以看出系统是如何挂载哪里的rootfs,大致就是自动找到有roofs的ubi分区,然后自动attach,接着将其挂载为系统rootfs

下面针对这3个地方进行详细分析:

一、自动挂载ubi分区UBI: auto-attach mtd10

其主要机制从一个patch文件即可看出 490-ubi-auto-attach-mtd-device-named-ubi-or-data-on-boot.patch

From: Daniel Golle <daniel@makrotopia.org>
Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1226,6 +1226,73 @@ static struct mtd_info * __init open_mtd
 	return mtd;
 }
 
+/*
+ * This function tries attaching mtd partitions named either "ubi" or "data"
+ * during boot.
+ */
+static void __init ubi_auto_attach(void)
+{
+	int err;
+	struct mtd_info *mtd;
+	loff_t offset = 0;
+	size_t len;
+	char magic[4];
+
+	/* try attaching mtd device named "ubi" or "data" */
+	mtd = open_mtd_device("ubi");
+	if (IS_ERR(mtd))
+		mtd = open_mtd_device("data");
+
+	if (IS_ERR(mtd))
+		return;
+
+	/* get the first not bad block */
+	if (mtd_can_have_bb(mtd))
+		while (mtd_block_isbad(mtd, offset)) {
+			offset += mtd->erasesize;
+
+			if (offset > mtd->size) {
+				pr_err("UBI error: Failed to find a non-bad "
+				       "block on mtd%d\n", mtd->index);
+				goto cleanup;
+			}
+		}
+
+	/* check if the read from flash was successful */
+	err = mtd_read(mtd, offset, 4, &len, (void *) magic);
+	if ((err && !mtd_is_bitflip(err)) || len != 4) {
+		pr_err("UBI error: unable to read from mtd%d\n", mtd->index);
+		goto cleanup;
+	}
+
+	/* check for a valid ubi magic */
+	if (strncmp(magic, "UBI#", 4)) {
+		pr_err("UBI error: no valid UBI magic found inside mtd%d\n", mtd->index);
+		goto cleanup;
+	}
+
+	/* don't auto-add media types where UBI doesn't makes sense */
+	if (mtd->type != MTD_NANDFLASH &&
+	    mtd->type != MTD_NORFLASH &&
+	    mtd->type != MTD_DATAFLASH &&
+	    mtd->type != MTD_MLCNANDFLASH)
+		goto cleanup;
+
+	mutex_lock(&ubi_devices_mutex);
+	pr_notice("UBI: auto-attach mtd%d\n", mtd->index);
+	err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0);
+	mutex_unlock(&ubi_devices_mutex);
+	if (err < 0) {
+		pr_err("UBI error: cannot attach mtd%d\n", mtd->index);
+		goto cleanup;
+	}
+
+	return;
+
+cleanup:
+	put_mtd_device(mtd);
+}
+
 static int __init ubi_init(void)
 {
 	int err, i, k;
@@ -1309,6 +1376,12 @@ static int __init ubi_init(void)
 		}
 	}
 
+	/* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd
+	 * parameter was given */
+	if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
+	    !ubi_is_module() && !mtd_devs)
+		ubi_auto_attach();
+
 	err = ubiblock_init();
 	if (err) {
 		pr_err("UBI error: block: cannot initialize, error %d", err);

其执行过程在ubi_init中触发,需要内核开启CONFIG_MTD_ROOTFS_ROOT_DEV支持,自动查找名为ubi或data的分区,然后自动进行attach

二、加载rootfs卷

其过程也是一个patch文件 491-ubi-auto-create-ubiblock-device-for-rootfs.patch

From: Daniel Golle <daniel@makrotopia.org>
Subject: ubi: auto-create ubiblock device for rootfs

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -635,6 +635,44 @@ static void __init ubiblock_create_from_
 	}
 }
 
+#define UBIFS_NODE_MAGIC  0x06101831
+static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc)
+{
+	int ret;
+	uint32_t magic_of, magic;
+	ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4);
+	if (ret)
+		return 0;
+	magic = le32_to_cpu(magic_of);
+	return magic == UBIFS_NODE_MAGIC;
+}
+
+static void __init ubiblock_create_auto_rootfs(void)
+{
+	int ubi_num, ret, is_ubifs;
+	struct ubi_volume_desc *desc;
+	struct ubi_volume_info vi;
+
+	for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) {
+		desc = ubi_open_volume_nm(ubi_num, "rootfs", UBI_READONLY);
+		if (IS_ERR(desc))
+			continue;
+
+		ubi_get_volume_info(desc, &vi);
+		is_ubifs = ubi_vol_is_ubifs(desc);
+		ubi_close_volume(desc);
+		if (is_ubifs)
+			break;
+
+		ret = ubiblock_create(&vi);
+		if (ret)
+			pr_err("UBI error: block: can't add '%s' volume, err=%d\n",
+				vi.name, ret);
+		/* always break if we get here */
+		break;
+	}
+}
+
 static void ubiblock_remove_all(void)
 {
 	struct ubiblock *next;
@@ -667,6 +705,10 @@ int __init ubiblock_init(void)
 	 */
 	ubiblock_create_from_param();
 
+	/* auto-attach "rootfs" volume if existing and non-ubifs */
+	if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV))
+		ubiblock_create_auto_rootfs();
+
 	/*
 	 * Block devices are only created upon user requests, so we ignore
 	 * existing volumes.

在函数ubiblock_init中进行ubiblock_create_auto_rootfs,硬编码为rootfs,注释已非常明确 /* auto-attach "rootfs" volume if existing and non-ubifs */

三、设置根文件系统

在分析之前我们先需要了解一下linux的根文件系统加载过程,此处介绍devtmpfs过程,devtmpfs网上介绍很多,大致就是在内核启动过程中自动创建/dev/xxx文件

系统运行过程中关于devtmpfs的日志只有两条:

[    0.636686] devtmpfs: initialized
...
[    2.321714] devtmpfs: mounted

devtmpfs: initialized 执行时机为cpu启动之后,具体在driver_init初始化的过程中

driver_init -->devtmpfs_init-->register_filesystem

static struct file_system_type dev_fs_type = {
	.name = "devtmpfs",
	.mount = dev_mount,
	.kill_sb = kill_litter_super,
};

在devtmpfs环境准备好之后,才有VFS初始化过程以及MTD分区检索

linux根文件系统挂载入口为prepare_namespace

具体过程为:

kernel_init --> kernel_init_freeable --> prepare_namespace

/*
 * Prepare the namespace - decide what/where to mount, load ramdisks, etc.
 */
void __init prepare_namespace(void)
{
	int is_floppy;

	printk(KERN_INFO "prepare_namespace root_delay=%d, cmd_root_dev [%s]\n", 
		root_delay, saved_root_name);

	if (root_delay) {
		printk(KERN_INFO "Waiting %d sec before mounting root device...\n",
		       root_delay);
		ssleep(root_delay);
	}

	/*
	 * wait for the known devices to complete their probing
	 *
	 * Note: this is a potential source of long boot delays.
	 * For example, it is not atypical to wait 5 seconds here
	 * for the touchpad of a laptop to initialize.
	 */
	wait_for_device_probe();

	md_run_setup();

	if (saved_root_name[0]) {
		root_device_name = saved_root_name;
		if (!strncmp(root_device_name, "mtd", 3) ||
		    !strncmp(root_device_name, "ubi", 3)) {
			mount_block_root(root_device_name, root_mountflags);
			goto out;
		}
		ROOT_DEV = name_to_dev_t(root_device_name);
		if (strncmp(root_device_name, "/dev/", 5) == 0)
			root_device_name += 5;
	}

	if (initrd_load())
		goto out;

	/* wait for any asynchronous scanning to complete */
	if ((ROOT_DEV == 0) && root_wait) {
		printk(KERN_INFO "Waiting for root device %s...\n",
			saved_root_name);
		while (driver_probe_done() != 0 ||
			(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
			msleep(100);
		async_synchronize_full();
	}

	is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;

	if (is_floppy && rd_doload && rd_load_disk(0))
		ROOT_DEV = Root_RAM0;

	mount_root();
out:
	devtmpfs_mount("dev");
	sys_mount(".", "/", NULL, MS_MOVE, NULL);
	sys_chroot(".");
}

其中有几个关键的参数:

cmdline中的root,rootwait,rootdelay,rootfstype

root指定挂载分区,在 auto-attach 章节里,如果cmdline中指定了root设备,那么就不用自动探测,当然在openwrt系统中,kernel和rootfs是一体的,设置自动探测与其特有的固件打包方式有关;

一般来说,当rootfs占有一个独立的mtd分区的时候,启动参数才指定root分区

rootfstype,说明挂载类型,在openwrt中传递的是“rootfstype=squashfs,ubifs“

详细日志参照:

[    2.269712] prepare_namespace root_delay=0, cmd_root_dev []
[    2.275130] VFS: under CONFIG_MTD_ROOTFS_ROOT_DEV to mount_ubi_rootfs
[    2.287905] VFS: to do_mount_root [squashfs] from cmdlines rootfstype
[    2.304145] VFS: Mounted root (squashfs filesystem) readonly on device 254:0.
[    2.321714] devtmpfs: mounted to [dev]

主要挂载过程在mount_root中

void __init mount_root(void)
{
#ifdef CONFIG_ROOT_NFS
	if (ROOT_DEV == Root_NFS) {
		if (mount_nfs_root())
			return;

		printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
		ROOT_DEV = Root_FD0;
	}
#endif
#ifdef CONFIG_BLK_DEV_FD
	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
		/* rd_doload is 2 for a dual initrd/ramload setup */
		if (rd_doload==2) {
			if (rd_load_disk(1)) {
				ROOT_DEV = Root_RAM1;
				root_device_name = NULL;
			}
		} else
			change_floppy("root floppy");
	}
#endif
#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
	printk(KERN_NOTICE "VFS: under CONFIG_MTD_ROOTFS_ROOT_DEV to mount_ubi_rootfs\n");
	if (!mount_ubi_rootfs())
		return;
#endif
#ifdef CONFIG_BLOCK
	{
		int err = create_dev("/dev/root", ROOT_DEV);

		if (err < 0)
			pr_emerg("Failed to create /dev/root: %d\n", err);
		mount_block_root("/dev/root", root_mountflags);
	}
#endif
}

由于使能了CONFIG_MTD_ROOTFS_ROOT_DEV,前面有UBI的自动attach,因此在此处直接挂载ubifs

static int __init mount_ubi_rootfs(void)
{
	int flags = MS_SILENT;
	int err, tried = 0;

	while (tried < 2) {
		err = do_mount_root("ubi0:rootfs", "ubifs", flags, \
					root_mount_data);
		switch (err) {
			case -EACCES:
				flags |= MS_RDONLY;
				tried++;
				break;
			default:
				return err;
		}
	}

	return -EINVAL;
}

至此UBI根文件系统挂载完毕

四、关于overlayfs挂载

主要在openwrt的mount_root中,日志为

[    4.376589] mount_root: overlay filesystem has not been fully initialized yet
[    4.383875] mount_root: switching to ubifs overlay

具体overlay机制,openwrt中资料很多,在此不作介绍

 

 


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进