从这篇博文开始,从代码角度梳理一下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;
}