JFFS2文件系统挂载过程(1)

从这篇博文开始,从代码角度梳理一下jffs2文件系统挂载的过程,整个过程主要以代码注释的形式呈现。本文内容基于linux4.4.198.请结合另一篇博客所述结构体进行阅读。
重要的事情先说在前面:jffs2的挂载过程中,是不允许对flash进行任何的写操作的。原因的话,我的理解是jffs2在没有完成整个扫描操作之前,不能确认挂载的mtd分区一定就是jffs2文件系统,所以不进行任何写入操作以免破坏了flash上的镜像。也就是说,如果允许写入,那如果该mtd分区本身是一个yaffs文件系统,但是我在mount的时候参数不小心写成jffs2了,那jffs2的写入动作很可能会破坏yaffs文件系统镜像,导致我重新mount这个yaffs文件系统的时候可能导致挂载不上或者数据被破坏等问题,所以,不做写入动作是最稳妥的选择。
实践是最好的学习方式,但是,想用loop设备模拟flash来挂载jffs2文件系统的童鞋注意了,这样做是不能成功的,原因是下面很快就会讲到的jffs2_mount函数,该函数调用mount_mtd,这个函数会判断挂载的是不是一个mtd设备,不是的话就会报错退出。所以,用loop设备模拟flash来挂载jffs2文件系统是行不通的,正确的方法应该是用phram来挂载了(最好通过修改内核把phram的块大小改为64K以上哈,当然不改也能用)。

入口函数:jffs2_mount

jffs2_mount只是简单的调用了mount_mtd,并传入了一个函数指针jffs2_fill_super用于回调,mount_mtd函数会在适当的时候调用它

static struct dentry *jffs2_mount(struct file_system_type *fs_type,
			int flags, const char *dev_name,
			void *data)
{
	return mount_mtd(fs_type, flags, dev_name, data, jffs2_fill_super);
}

根据注释,jffs2_fill_super函数的功能就是填充superblock变量,然后,将mount的任务交给函数jffs2_do_fill_super

/*
 * fill in the superblock
 */
static int jffs2_fill_super(struct super_block *sb, void *data, int silent)
{
	struct jffs2_sb_info *c; //jffs2代码一般把jffs2_sb_info指针变量写为c
	int ret;

	jffs2_dbg(1, "jffs2_get_sb_mtd():"
		  " New superblock for device %d (\"%s\")\n",
		  sb->s_mtd->index, sb->s_mtd->name);

	c = kzalloc(sizeof(*c), GFP_KERNEL);//分配内存空间
	if (!c)
		return -ENOMEM;
	//做一些简单的赋值操作
	c->mtd = sb->s_mtd;
	c->os_priv = sb;
	sb->s_fs_info = c;

	ret = jffs2_parse_options(c, data);//解析mount参数
	if (ret)
		return -EINVAL;

	/* Initialize JFFS2 superblock locks, the further initialization will
	 * be done later */
	//初始化一些锁和队列
	mutex_init(&c->alloc_sem);
	mutex_init(&c->erase_free_sem);
	init_waitqueue_head(&c->erase_wait);
	init_waitqueue_head(&c->inocache_wq);
	spin_lock_init(&c->erase_completion_lock);
	spin_lock_init(&c->inocache_lock);

	//给一些钩子赋值
	sb->s_op = &jffs2_super_operations;
	sb->s_export_op = &jffs2_export_ops;
	sb->s_flags = sb->s_flags | MS_NOATIME;
	sb->s_xattr = jffs2_xattr_handlers;
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
	sb->s_flags |= MS_POSIXACL;
#endif
	//剩下的工作交给jffs2_do_fill_super
	ret = jffs2_do_fill_super(sb, data, silent);
	return ret;
}

接下来的jffs2_do_fill_super函数,也是做一些简单的变量赋值初始化操作,主要从mtd获取信息给struct jffs2_sb_info赋值,然后挂载的重点工作交给jffs2_do_mount_fs函数完成。
当jffs2_do_mount_fs完成工作后,为了和linux的VFS结合,jffs2_do_fill_super函数获取了jffs2文件系统分区根节点的inode和dentry结构体实例,挂载的最后一步,是启动gc线程,该线程用于完成挂载时未完成的jffs2文件系统检验(这么做主要是为了缩短mount的时间)以及垃圾回收的工作。

int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
{
	struct jffs2_sb_info *c;
	struct inode *root_i;
	int ret;
	size_t blocks;

	c = JFFS2_SB_INFO(sb); //从struct super_block获取struct jffs2_sb_info指针

	/* Do not support the MLC nand */
	if (c->mtd->type == MTD_MLCNANDFLASH)
		return -EINVAL;

#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
	if (c->mtd->type == MTD_NANDFLASH) {
		pr_err("Cannot operate on NAND flash unless jffs2 NAND support is compiled in\n");
		return -EINVAL;
	}
	if (c->mtd->type == MTD_DATAFLASH) {
		pr_err("Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in\n");
		return -EINVAL;
	}
#endif

	c->flash_size = c->mtd->size;
	c->sector_size = c->mtd->erasesize;
	blocks = c->flash_size / c->sector_size;

	/*
	 * Size alignment check
	 */
	if ((c->sector_size * blocks) != c->flash_size) {
		c->flash_size = c->sector_size * blocks;
		pr_info("Flash size not aligned to erasesize, reducing to %dKiB\n",
			c->flash_size / 1024);
	}

	if (c->flash_size < 5*c->sector_size) {
		pr_err("Too few erase blocks (%d)\n",
		       c->flash_size / c->sector_size);
		return -EINVAL;
	}

	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);

	/* NAND (or other bizarre) flash... do setup accordingly */
	ret = jffs2_flash_setup(c);//这里主要是根据flash的类型做不同的初始化操作
	if (ret)
		return ret;

	c->inocache_hashsize = calculate_inocache_hashsize(c->flash_size);
	//为inocache散列表分配内存空间
	c->inocache_list = kcalloc(c->inocache_hashsize, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
	if (!c->inocache_list) {
		ret = -ENOMEM;
		goto out_wbuf;
	}

	jffs2_init_xattr_subsystem(c);

	if ((ret = jffs2_do_mount_fs(c)))  //mount的主要工作交给他
		goto out_inohash;

	jffs2_dbg(1, "%s(): Getting root inode\n", __func__);
	root_i = jffs2_iget(sb, 1); //获取inode号为1的inode作为文件系统跟节点inode
	if (IS_ERR(root_i)) {
		jffs2_dbg(1, "get root inode failed\n");
		ret = PTR_ERR(root_i);
		goto out_root;
	}

	ret = -ENOMEM;

	jffs2_dbg(1, "%s(): d_make_root()\n", __func__);
	sb->s_root = d_make_root(root_i); //获取dentry
	if (!sb->s_root)
		goto out_root;

	sb->s_maxbytes = 0xFFFFFFFF;
	sb->s_blocksize = PAGE_CACHE_SIZE;
	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
	sb->s_magic = JFFS2_SUPER_MAGIC;
	if (!(sb->s_flags & MS_RDONLY)) //如果不是只读,那么启动gc线程
		jffs2_start_garbage_collect_thread(c);
	return 0;

out_root:
	jffs2_free_ino_caches(c);
	jffs2_free_raw_node_refs(c);
	if (jffs2_blocks_use_vmalloc(c))
		vfree(c->blocks);
	else
		kfree(c->blocks);
 out_inohash:
	jffs2_clear_xattr_subsystem(c);
	kfree(c->inocache_list);
 out_wbuf:
	jffs2_flash_cleanup(c);

	return ret;
}

好了接下来到了jffs2_do_mount_fs函数,该函数又做了一些简单的赋值初始化操作,主要是计算一些block_nr,然后为flash上每个block分配一个block描述符的空间,最后又把重任托付给了另一个函数jffs2_build_filesystem。

int jffs2_do_mount_fs(struct jffs2_sb_info *c)
{
	int ret;
	int i;
	int size;

	c->free_size = c->flash_size;
	c->nr_blocks = c->flash_size / c->sector_size;
	size = sizeof(struct jffs2_eraseblock) * c->nr_blocks;
#ifndef __ECOS
	if (jffs2_blocks_use_vmalloc(c))
		c->blocks = vzalloc(size);
	else
#endif
		c->blocks = kzalloc(size, GFP_KERNEL); //为每一个block分配一个struct jffs2_eraseblock描述符,这里一次性申请完所有的block描述符
	if (!c->blocks)
		return -ENOMEM;

	for (i=0; i<c->nr_blocks; i++) { //然后初始化这些block描述符
		INIT_LIST_HEAD(&c->blocks[i].list);
		c->blocks[i].offset = i * c->sector_size;
		c->blocks[i].free_size = c->sector_size;
	}

	//初始化各种链表
	INIT_LIST_HEAD(&c->clean_list);
	INIT_LIST_HEAD(&c->very_dirty_list);
	INIT_LIST_HEAD(&c->dirty_list);
	INIT_LIST_HEAD(&c->erasable_list);
	INIT_LIST_HEAD(&c->erasing_list);
	INIT_LIST_HEAD(&c->erase_checking_list);
	INIT_LIST_HEAD(&c->erase_pending_list);
	INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
	INIT_LIST_HEAD(&c->erase_complete_list);
	INIT_LIST_HEAD(&c->free_list);
	INIT_LIST_HEAD(&c->bad_list);
	INIT_LIST_HEAD(&c->bad_used_list);
	c->highest_ino = 1;
	c->summary = NULL;

	ret = jffs2_sum_init(c); //summary的相关初始化
	if (ret)
		goto out_free;

	if (jffs2_build_filesystem(c)) { //mount重任交给jffs2_build_filesystem
		dbg_fsbuild("build_fs failed\n");
		jffs2_free_ino_caches(c);
		jffs2_free_raw_node_refs(c);
		ret = -EIO;
		goto out_free;
	}

	jffs2_calc_trigger_levels(c); //根据物理flash情况计算一些阈值

	return 0;

 out_free:
#ifndef __ECOS
	if (jffs2_blocks_use_vmalloc(c))
		vfree(c->blocks);
	else
#endif
		kfree(c->blocks);

	return ret;
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PetaLinux是一个基于嵌入式Linux的开发工具链,用于构建嵌入式系统。它提供了许多可定制的组件和功能,其中包括对JFFS2文件系统的支持。 JFFS2(Journaling Flash File System 2)是一种针对闪存存储设备设计的文件系统。它具有很好的压缩率和高度可靠性,适用于嵌入式系统。在嵌入式系统中,闪存是一种常见的存储设备,它通常具有较小的存储容量和有限的周期寿命。因此,选择合适的文件系统对于确保系统的可靠性和性能至关重要。 使用PetaLinux时选择使用JFFS2文件系统有几个优点。首先,JFFS2具有较好的压缩率,可以节省存储空间。这在嵌入式系统中特别重要,因为闪存的容量通常有限。其次,JFFS2采用了日志技术,可以确保数据的完整性和一致性。它可以通过将修改的数据写入日志中,然后再写入闪存中来提供数据持久性。这有助于防止数据损坏和错误。 另外,PetaLinux提供了对JFFS2文件系统的内建支持和集成工具。这些工具使得在构建嵌入式系统时轻松地添加和配置JFFS2文件系统成为可能。开发人员可以使用PetaLinux提供的命令和选项设置文件系统的参数和属性,以满足特定的需求。 综上所述,使用PetaLinux来构建嵌入式系统并选择JFFS2文件系统能够提供较好的存储空间利用率和数据的可靠性。这使得嵌入式系统在具有有限存储容量和闪存设备的情况下,能够更好地运行和维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值