我们想要使用ext4文件系统,首先要把ext4文件系统注册到系统中,然后使用mount挂载文件系统,这样子就可以随便的操作里面的内容了,比如创建目录,创建文件,读写文件,删除文件等等,最后可以解挂文件系统。

ext4文件系统注册

首先看看如何注册ext4文件系统,在fs/ext4/super.c的ext4_init_fs函数:

static struct file_system_type ext4_fs_type = {.owner       = THIS_MODULE,//文件系统名字.name        = "ext4",//文件系统挂载调用函数.mount      = ext4_mount,//文件系统解挂调用函数.kill_sb  = kill_block_super,//使用标志,表示文件系统在物理设备上.fs_flags    = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext4");/* Shared across all ext4 file systems */
wait_queue_head_t ext4__ioend_wq[EXT4_WQ_HASH_SZ];static int __init ext4_init_fs(void)
{int i, err;ratelimit_state_init(&ext4_mount_msg_ratelimit, 30 * HZ, 64);//初始化挂载信息ext4_li_info = NULL;mutex_init(&ext4_li_mtx);//初始化互斥锁/* Build-time check for flags consistency */ext4_check_flag_values();//检查标志的一致性for (i = 0; i < EXT4_WQ_HASH_SZ; i++)init_waitqueue_head(&ext4__ioend_wq[i]);//初始化多个等待队列err = ext4_init_es();//分配extent状态内存if (err)return err;err = ext4_init_pageio();//分配io页缓存空间if (err)goto out5;err = ext4_init_system_zone();//分配ext4需要的系统空间,存放entry入口if (err)goto out4;err = ext4_init_sysfs();//在/sys下创建kobjectif (err)goto out3;err = ext4_init_mballoc();//分配预配置内存,申请和释放用到的内存if (err)goto out2;err = init_inodecache();//分配存放inode的内存if (err)goto out1;register_as_ext3();//把ext3注册到系统中,因为ext4兼容ext3register_as_ext2();//把ext2注册到系统中,因为ext4兼容ext2err = register_filesystem(&ext4_fs_type);//把ext4注册到系统中if (err)goto out;return 0;
out:unregister_as_ext2();unregister_as_ext3();destroy_inodecache();
out1:ext4_exit_mballoc();
out2:ext4_exit_sysfs();
out3:ext4_exit_system_zone();
out4:ext4_exit_pageio();
out5:ext4_exit_es();return err;
}

系统做了大量的初始化工作,主要是通过 register_filesystem(&ext4_fs_type) 把ext4注册到系统中,ext4_fs_type中包含了系统挂载和解挂函数,我们真正挂载和解挂的时候会回调到这两个函数的,感兴趣的可以自己看看这两个函数,其实也就是调用了系统的默认挂载函数解挂函数而已,后面我会讲一下挂载函数,现在我们看看register_filesystem,在fs/filesystems.c文件中:

int register_filesystem(struct file_system_type * fs)
{int res = 0;struct file_system_type ** p;BUG_ON(strchr(fs->name, '.'));if (fs->next)return -EBUSY;write_lock(&file_systems_lock);p = find_filesystem(fs->name, strlen(fs->name));if (*p)res = -EBUSY;else*p = fs;write_unlock(&file_systems_lock);return res;
}static struct file_system_type **find_filesystem(const char *name, unsigned len)
{struct file_system_type **p;for (p = &file_systems; *p; p = &(*p)->next)if (strncmp((*p)->name, name, len) == 0 &&!(*p)->name[len])break;return p;
}

register_filesystem主要是通过find_filesystem函数在全局变量file_systems中查找指定name的文件系统类型是否存在,find_filesystem函数会在查找到文件系统类型后返回指针,查找不到则返回file_systems末尾的next指针,最后register_filesystem根据find_filesystem的返回值不为空表示已经文件系统类型存在;如果返回值是一个空表示文件系统类型不存在,需要把新的类型添加到全局变量file_systems中。
我们回头看看,kill_block_super函数是通用块设备卸载函数,这里就不讲了,我们主要看看ext4_mount函数做了什么,这个函数是所有ext4文件系统挂载的时候都会回调到的函数,在fs/ext4/super.c文件中:

static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,const char *dev_name, void *data)
{return mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super);
}

ext4_mount仅仅是调用了mount_bdev函数而已,mount_bdev函数在fs/super.c文件中:

struct dentry *mount_bdev(struct file_system_type *fs_type,int flags, const char *dev_name, void *data,int (*fill_super)(struct super_block *, void *, int))
{struct block_device *bdev;struct super_block *s;fmode_t mode = FMODE_READ | FMODE_EXCL;int error = 0;if (!(flags & SB_RDONLY))mode |= FMODE_WRITE;//通过dev_name设备名分配对应的block_device结构bdev = blkdev_get_by_path(dev_name, mode, fs_type);if (IS_ERR(bdev))return ERR_CAST(bdev);/** once the super is inserted into the list by sget, s_umount* will protect the lockfs code from trying to start a snapshot* while we are mounting*/mutex_lock(&bdev->bd_fsfreeze_mutex);if (bdev->bd_fsfreeze_count > 0) {mutex_unlock(&bdev->bd_fsfreeze_mutex);error = -EBUSY;goto error_bdev;}//得到已经存在或者新分配的super_block结构s = sget(fs_type, test_bdev_super, set_bdev_super, flags | SB_NOSEC,bdev);mutex_unlock(&bdev->bd_fsfreeze_mutex);if (IS_ERR(s))goto error_s;if (s->s_root) {//s->s_root不为空,说明得到的是已经存在的超级快//判断此次的挂载flag是否和之前的挂载有读/写冲突,如果有冲突则返回错误if ((flags ^ s->s_flags) & SB_RDONLY) {deactivate_locked_super(s);error = -EBUSY;goto error_bdev;}/** s_umount nests inside bd_mutex during* __invalidate_device().  blkdev_put() acquires* bd_mutex and can't be called under s_umount.  Drop* s_umount temporarily.  This is safe as we're* holding an active reference.*/up_write(&s->s_umount);// 因为已经有了之前存在的超级快,也就是block_dev之前也分配过了,所以这个新的bdev就可以释放了。blkdev_put(bdev, mode);down_write(&s->s_umount);} else {//s->s_root为空,说明得到的是新的超级快//设置新的超级快的mode, id, blocksizes->s_mode = mode;snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);sb_set_blocksize(s, block_size(bdev));//fill_super是一个传入参数,它由具体文件系统自己实现,ext4就实现了ext4_fill_supererror = fill_super(s, data, flags & SB_SILENT ? 1 : 0);if (error) {deactivate_locked_super(s);goto error;}//最后设置s_flags和bd_supers->s_flags |= SB_ACTIVE;bdev->bd_super = s;}return dget(s->s_root);error_s:error = PTR_ERR(s);
error_bdev:blkdev_put(bdev, mode);
error:return ERR_PTR(error);
}

我们可以看到通过blkdev_get_by_path函数分配对应的块设备,然后根据sget来得到已经存在或者新分配的super_block结构体,再根据sget的返回值判断得到的是新的超级快还是已经存在的超级快。
1.如果是新的超级块,则判断此次的挂载flag是否和之前的挂载有读/写冲突,如果有冲突则返回错误,没有冲突则继续,然后把刚刚blkdev_get_by_path分配的块设备释放掉,因为这个超级快已经存在,所以块设备也是存在的。
2.如果是已经存在的超级块,说明之前已经挂载过了。需要设置新的超级快的mode, id, blocksize,然后使用传入的函数参数fill_super,也就是ext4_fill_super函数,最后设置s_flags和bd_super。
最后调用dget返回。
blkdev_get_by_path函数首先是一个路径查找的过程,调用kern_path()的路径查找得到struct path,然后以path.dentry->d_inode为参数调用bd_acquire得到block_device结构。因为这个比较简单,但是代码量有很大,我就不展开了。
接下来我会说说sget函数和fill_super函数。
先说sget函数:

struct super_block *sget(struct file_system_type *type,int (*test)(struct super_block *,void *),int (*set)(struct super_block *,void *),int flags,void *data)
{struct user_namespace *user_ns = current_user_ns();/* We don't yet pass the user namespace of the parent* mount through to here so always use &init_user_ns* until that changes.*/if (flags & SB_SUBMOUNT)user_ns = &init_user_ns;/* Ensure the requestor has permissions over the target filesystem */if (!(flags & (SB_KERNMOUNT|SB_SUBMOUNT)) && !ns_capable(user_ns, CAP_SYS_ADMIN))return ERR_PTR(-EPERM);return sget_userns(type, test, set, flags, user_ns, data);
}

sget函数组要通过sget_userns找到或者创建一个超级快:

struct super_block *sget_userns(struct file_system_type *type,int (*test)(struct super_block *,void *),int (*set)(struct super_block *,void *),int flags, struct user_namespace *user_ns,void *data)
{struct super_block *s = NULL;struct super_block *old;int err;if (!(flags & (SB_KERNMOUNT|SB_SUBMOUNT)) &&!(type->fs_flags & FS_USERNS_MOUNT) &&!capable(CAP_SYS_ADMIN))return ERR_PTR(-EPERM);
retry:spin_lock(&sb_lock);if (test) {//遍历超级快,如果找到则返回超级快hlist_for_each_entry(old, &type->fs_supers, s_instances) {if (!test(old, data))continue;if (user_ns != old->s_user_ns) {spin_unlock(&sb_lock);destroy_unused_super(s);return ERR_PTR(-EBUSY);}if (!grab_super(old))goto retry;destroy_unused_super(s);return old;}}//找不到,下面创建一个超级块if (!s) {spin_unlock(&sb_lock);//分配一个超级快并且初始化好s = alloc_super(type, (flags & ~SB_SUBMOUNT), user_ns);if (!s)return ERR_PTR(-ENOMEM);goto retry;}err = set(s, data);//设置私有数据if (err) {spin_unlock(&sb_lock);destroy_unused_super(s);return ERR_PTR(err);}//设置超级快的参数s->s_type = type;strlcpy(s->s_id, type->name, sizeof(s->s_id));//超级快加入到链表中list_add_tail(&s->s_list, &super_blocks);//把同一个文件系统类型的所有超级块实例链接在一起,链表的头节点是结构体file_system_type的成员fs_supershlist_add_head(&s->s_instances, &type->fs_supers);spin_unlock(&sb_lock);get_filesystem(type);register_shrinker_prepared(&s->s_shrink);return s;
}

sget_userns首先遍历ext4下所有超级块,如果找到则返回超级块,否则就创建一个超级块。接下来要分配一个超级快并且初始化好,然后设置私有数据、超级快的参数,最后把超级快加入到链表中和把同一个文件系统类型的所有超级块实例链接在一起,返回新创建的超级快。
sget函数讲完了,现在说说fill_super函数,fill_super函数其实是ext4_fill_super函数作为参数传进来的,在fs/ext4/super.c文件中,这个函数一千行,也是最后一个函数分析了,慢慢看吧:

static int ext4_fill_super(struct super_block *sb, void *data, int silent)
{struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);char *orig_data = kstrdup(data, GFP_KERNEL);//分配挂载选项的内存并且拷贝struct buffer_head *bh;struct ext4_super_block *es = NULL;//分配内存存放文件系统私有信息struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);ext4_fsblk_t block;//根据data数值计算超级块所在的块,data中可以指定超级块的块号ext4_fsblk_t sb_block = get_sb_block(&data);ext4_fsblk_t logical_sb_block;unsigned long offset = 0;unsigned long journal_devnum = 0;unsigned long def_mount_opts;struct inode *root;const char *descr;int ret = -ENOMEM;int blocksize, clustersize;unsigned int db_count;unsigned int i;int needs_recovery, has_huge_files, has_bigalloc;__u64 blocks_count;int err = 0;unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;ext4_group_t first_not_zeroed;if ((data && !orig_data) || !sbi)goto out_free_base;sbi->s_daxdev = dax_dev;//分配块组锁sbi->s_blockgroup_lock =kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);if (!sbi->s_blockgroup_lock)goto out_free_base;//填些系统私有信息sb->s_fs_info = sbi;sbi->s_sb = sb;sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;sbi->s_sb_block = sb_block;if (sb->s_bdev->bd_part)sbi->s_sectors_written_start =part_stat_read(sb->s_bdev->bd_part, sectors[STAT_WRITE]);/* Cleanup superblock name */strreplace(sb->s_id, '/', '!');/* -EINVAL is default */ret = -EINVAL;//计算块大小,最小为EXT4_MIN_BLOCK_SIZE,即1Kblocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);if (!blocksize) {ext4_msg(sb, KERN_ERR, "unable to set blocksize");goto out_fail;}/** The ext4 superblock will not be buffer aligned for other than 1kB* block sizes.  We need to calculate the offset from buffer start.*///ext4超级快不会进行缓存区对齐,需要计算偏移量if (blocksize != EXT4_MIN_BLOCK_SIZE) {logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;offset = do_div(logical_sb_block, blocksize);} else {//1k大小的超级快不需要计算偏移量logical_sb_block = sb_block;}//从磁盘中读取逻辑超级块if (!(bh = sb_bread_unmovable(sb, logical_sb_block))) {ext4_msg(sb, KERN_ERR, "unable to read superblock");goto out_fail;}/** Note: s_es must be initialized as soon as possible because*       some ext4 macro-instructions depend on its value*///初始化extent状态值,ext4指令需要它es = (struct ext4_super_block *) (bh->b_data + offset);sbi->s_es = es;sb->s_magic = le16_to_cpu(es->s_magic);//如果不是ext4文件系统的魔数,说明文件系统格式不是ext4,返回错误if (sb->s_magic != EXT4_SUPER_MAGIC)goto cantfind_ext4;sbi->s_kbytes_written = le64_to_cpu(es->s_kbytes_written);/* Warn if metadata_csum and gdt_csum are both set. */if (ext4_has_feature_metadata_csum(sb) &&ext4_has_feature_gdt_csum(sb))ext4_warning(sb, "metadata_csum and uninit_bg are ""redundant flags; please run fsck.");/* 检查已知的校验和算法 */if (!ext4_verify_csum_type(sb, es)) {ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with ""unknown checksum algorithm.");silent = 1;goto cantfind_ext4;}/* 加载校验驱动程序 */sbi->s_chksum_driver = crypto_alloc_shash("crc32c", 0, 0);if (IS_ERR(sbi->s_chksum_driver)) {ext4_msg(sb, KERN_ERR, "Cannot load crc32c driver.");ret = PTR_ERR(sbi->s_chksum_driver);sbi->s_chksum_driver = NULL;goto failed_mount;}/* 检查超块校验 */if (!ext4_superblock_csum_verify(sb, es)) {ext4_msg(sb, KERN_ERR, "VFS: Found ext4 filesystem with ""invalid superblock checksum.  Run e2fsck?");silent = 1;ret = -EFSBADCRC;goto cantfind_ext4;}/* 预先计算所有元数据的校验和种子 */if (ext4_has_feature_csum_seed(sb))sbi->s_csum_seed = le32_to_cpu(es->s_checksum_seed);else if (ext4_has_metadata_csum(sb) || ext4_has_feature_ea_inode(sb))sbi->s_csum_seed = ext4_chksum(sbi, ~0, es->s_uuid,sizeof(es->s_uuid));/* 在解析挂载选项之前设置默认值   */def_mount_opts = le32_to_cpu(es->s_default_mount_opts);set_opt(sb, INIT_INODE_TABLE);if (def_mount_opts & EXT4_DEFM_DEBUG)set_opt(sb, DEBUG);if (def_mount_opts & EXT4_DEFM_BSDGROUPS)set_opt(sb, GRPID);if (def_mount_opts & EXT4_DEFM_UID16)set_opt(sb, NO_UID32);/* 默认打开Xattr用户名称空间和acl   */set_opt(sb, XATTR_USER);
#ifdef CONFIG_EXT4_FS_POSIX_ACLset_opt(sb, POSIX_ACL);
#endif/* 当启用metadata_csum时,需要启用journal_csum   */if (ext4_has_metadata_csum(sb))set_opt(sb, JOURNAL_CHECKSUM);if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_DATA)set_opt(sb, JOURNAL_DATA);else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_ORDERED)set_opt(sb, ORDERED_DATA);else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_WBACK)set_opt(sb, WRITEBACK_DATA);if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_PANIC)set_opt(sb, ERRORS_PANIC);else if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_CONTINUE)set_opt(sb, ERRORS_CONT);elseset_opt(sb, ERRORS_RO);/* 默认启用Block_validity; 默认noblock_validity   */set_opt(sb, BLOCK_VALIDITY);if (def_mount_opts & EXT4_DEFM_DISCARD)set_opt(sb, DISCARD);sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid));sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid));sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE * HZ;sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME;sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME;if ((def_mount_opts & EXT4_DEFM_NOBARRIER) == 0)set_opt(sb, BARRIER);/** enable delayed allocation by default* Use -o nodelalloc to turn it off*///如果使用了-o nodelalloc选项则关闭延迟分配  if (!IS_EXT3_SB(sb) && !IS_EXT2_SB(sb) &&((def_mount_opts & EXT4_DEFM_NODELALLOC) == 0))set_opt(sb, DELALLOC);/** set default s_li_wait_mult for lazyinit, for the case there is* no mount option specified.*///为lazyinit设置默认的s_li_wait_multsbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;//继续进行挂载选项的分析if (sbi->s_es->s_mount_opts[0]) {char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,sizeof(sbi->s_es->s_mount_opts),GFP_KERNEL);if (!s_mount_opts)goto failed_mount;if (!parse_options(s_mount_opts, sb, &journal_devnum,&journal_ioprio, 0)) {ext4_msg(sb, KERN_WARNING,"failed to parse options in superblock: %s",s_mount_opts);}kfree(s_mount_opts);}//记录s_mount_optsbi->s_def_mount_opt = sbi->s_mount_opt;if (!parse_options((char *) data, sb, &journal_devnum,&journal_ioprio, 0))goto failed_mount;//挂载选项分析if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {printk_once(KERN_WARNING "EXT4-fs: Warning: mounting ""with data=journal disables delayed ""allocation and O_DIRECT support!\n");if (test_opt2(sb, EXPLICIT_DELALLOC)) {ext4_msg(sb, KERN_ERR, "can't mount with ""both data=journal and delalloc");goto failed_mount;}if (test_opt(sb, DIOREAD_NOLOCK)) {ext4_msg(sb, KERN_ERR, "can't mount with ""both data=journal and dioread_nolock");goto failed_mount;}if (test_opt(sb, DAX)) {ext4_msg(sb, KERN_ERR, "can't mount with ""both data=journal and dax");goto failed_mount;}if (ext4_has_feature_encrypt(sb)) {ext4_msg(sb, KERN_WARNING,"encrypted files will use data=ordered ""instead of data journaling mode");}if (test_opt(sb, DELALLOC))clear_opt(sb, DELALLOC);} else {sb->s_iflags |= SB_I_CGROUPWB;}sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |(test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0);if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV &&(ext4_has_compat_features(sb) ||ext4_has_ro_compat_features(sb) ||ext4_has_incompat_features(sb)))ext4_msg(sb, KERN_WARNING,"feature flags set on rev 0 fs, ""running e2fsck is recommended");if (es->s_creator_os == cpu_to_le32(EXT4_OS_HURD)) {set_opt2(sb, HURD_COMPAT);if (ext4_has_feature_64bit(sb)) {ext4_msg(sb, KERN_ERR,"The Hurd can't support 64-bit file systems");goto failed_mount;}/** ea_inode feature uses l_i_version field which is not* available in HURD_COMPAT mode.*///ea_inode特性使用了HURD_COMPAT模式下不可用的l_i_version字段if (ext4_has_feature_ea_inode(sb)) {ext4_msg(sb, KERN_ERR,"ea_inode feature is not supported for Hurd");goto failed_mount;}}if (IS_EXT2_SB(sb)) {//如果是ext2格式的超级快if (ext2_feature_set_ok(sb))ext4_msg(sb, KERN_INFO, "mounting ext2 file system ""using the ext4 subsystem");else {/** If we're probing be silent, if this looks like* it's actually an ext[34] filesystem.*/if (silent && ext4_feature_set_ok(sb, sb_rdonly(sb)))goto failed_mount;ext4_msg(sb, KERN_ERR, "couldn't mount as ext2 due ""to feature incompatibilities");goto failed_mount;}}if (IS_EXT3_SB(sb)) {//如果是ext3格式的超级快if (ext3_feature_set_ok(sb))ext4_msg(sb, KERN_INFO, "mounting ext3 file system ""using the ext4 subsystem");else {/** If we're probing be silent, if this looks like* it's actually an ext4 filesystem.*/if (silent && ext4_feature_set_ok(sb, sb_rdonly(sb)))goto failed_mount;ext4_msg(sb, KERN_ERR, "couldn't mount as ext3 due ""to feature incompatibilities");goto failed_mount;}}/** Check feature flags regardless of the revision level, since we* previously didn't change the revision level when setting the flags,* so there is a chance incompat flags are set on a rev 0 filesystem.*/if (!ext4_feature_set_ok(sb, (sb_rdonly(sb))))goto failed_mount;//计算块大小blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);if (blocksize < EXT4_MIN_BLOCK_SIZE ||blocksize > EXT4_MAX_BLOCK_SIZE) {ext4_msg(sb, KERN_ERR,"Unsupported filesystem blocksize %d (%d log_block_size)",blocksize, le32_to_cpu(es->s_log_block_size));goto failed_mount;}if (le32_to_cpu(es->s_log_block_size) >(EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {ext4_msg(sb, KERN_ERR,"Invalid log block size: %u",le32_to_cpu(es->s_log_block_size));goto failed_mount;}if (le32_to_cpu(es->s_log_cluster_size) >(EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {ext4_msg(sb, KERN_ERR,"Invalid log cluster size: %u",le32_to_cpu(es->s_log_cluster_size));goto failed_mount;}if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {ext4_msg(sb, KERN_ERR,"Number of reserved GDT blocks insanely large: %d",le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks));goto failed_mount;}if (sbi->s_mount_opt & EXT4_MOUNT_DAX) {if (ext4_has_feature_inline_data(sb)) {ext4_msg(sb, KERN_ERR, "Cannot use DAX on a filesystem"" that may contain inline data");sbi->s_mount_opt &= ~EXT4_MOUNT_DAX;}if (!bdev_dax_supported(sb->s_bdev, blocksize)) {ext4_msg(sb, KERN_ERR,"DAX unsupported by block device. Turning off DAX.");sbi->s_mount_opt &= ~EXT4_MOUNT_DAX;}}if (ext4_has_feature_encrypt(sb) && es->s_encryption_level) {ext4_msg(sb, KERN_ERR, "Unsupported encryption level %d",es->s_encryption_level);goto failed_mount;}//验证文件系统块大小if (sb->s_blocksize != blocksize) {/* Validate the filesystem blocksize */if (!sb_set_blocksize(sb, blocksize)) {ext4_msg(sb, KERN_ERR, "bad block size %d",blocksize);goto failed_mount;}brelse(bh);logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;offset = do_div(logical_sb_block, blocksize);bh = sb_bread_unmovable(sb, logical_sb_block);if (!bh) {ext4_msg(sb, KERN_ERR,"Can't read superblock on 2nd try");goto failed_mount;}es = (struct ext4_super_block *)(bh->b_data + offset);sbi->s_es = es;if (es->s_magic != cpu_to_le16(EXT4_SUPER_MAGIC)) {ext4_msg(sb, KERN_ERR,"Magic mismatch, very weird!");goto failed_mount;}}//判断是否存在大文件has_huge_files = ext4_has_feature_huge_file(sb);sbi->s_bitmap_maxbytes = ext4_max_bitmap_size(sb->s_blocksize_bits,has_huge_files);sb->s_maxbytes = ext4_max_size(sb->s_blocksize_bits, has_huge_files);if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) {sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE;sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO;} else {sbi->s_inode_size = le16_to_cpu(es->s_inode_size);sbi->s_first_ino = le32_to_cpu(es->s_first_ino);if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) {ext4_msg(sb, KERN_ERR, "invalid first ino: %u",sbi->s_first_ino);goto failed_mount;}if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) ||(!is_power_of_2(sbi->s_inode_size)) ||(sbi->s_inode_size > blocksize)) {ext4_msg(sb, KERN_ERR,"unsupported inode size: %d",sbi->s_inode_size);goto failed_mount;}if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE)sb->s_time_gran = 1 << (EXT4_EPOCH_BITS - 2);}sbi->s_desc_size = le16_to_cpu(es->s_desc_size);if (ext4_has_feature_64bit(sb)) {if (sbi->s_desc_size < EXT4_MIN_DESC_SIZE_64BIT ||sbi->s_desc_size > EXT4_MAX_DESC_SIZE ||!is_power_of_2(sbi->s_desc_size)) {ext4_msg(sb, KERN_ERR,"unsupported descriptor size %lu",sbi->s_desc_size);goto failed_mount;}} elsesbi->s_desc_size = EXT4_MIN_DESC_SIZE;sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);sbi->s_inodes_per_block = blocksize / EXT4_INODE_SIZE(sb);if (sbi->s_inodes_per_block == 0)goto cantfind_ext4;if (sbi->s_inodes_per_group < sbi->s_inodes_per_block ||sbi->s_inodes_per_group > blocksize * 8) {ext4_msg(sb, KERN_ERR, "invalid inodes per group: %lu\n",sbi->s_blocks_per_group);goto failed_mount;}sbi->s_itb_per_group = sbi->s_inodes_per_group /sbi->s_inodes_per_block;sbi->s_desc_per_block = blocksize / EXT4_DESC_SIZE(sb);sbi->s_sbh = bh;sbi->s_mount_state = le16_to_cpu(es->s_state);sbi->s_addr_per_block_bits = ilog2(EXT4_ADDR_PER_BLOCK(sb));sbi->s_desc_per_block_bits = ilog2(EXT4_DESC_PER_BLOCK(sb));for (i = 0; i < 4; i++)sbi->s_hash_seed[i] = le32_to_cpu(es->s_hash_seed[i]);sbi->s_def_hash_version = es->s_def_hash_version;if (ext4_has_feature_dir_index(sb)) {i = le32_to_cpu(es->s_flags);if (i & EXT2_FLAGS_UNSIGNED_HASH)sbi->s_hash_unsigned = 3;else if ((i & EXT2_FLAGS_SIGNED_HASH) == 0) {#ifdef __CHAR_UNSIGNED__if (!sb_rdonly(sb))es->s_flags |=cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH);sbi->s_hash_unsigned = 3;
#elseif (!sb_rdonly(sb))es->s_flags |=cpu_to_le32(EXT2_FLAGS_SIGNED_HASH);
#endif}}/* 处理集群大小 */clustersize = BLOCK_SIZE << le32_to_cpu(es->s_log_cluster_size);has_bigalloc = ext4_has_feature_bigalloc(sb);if (has_bigalloc) {if (clustersize < blocksize) {ext4_msg(sb, KERN_ERR,"cluster size (%d) smaller than ""block size (%d)", clustersize, blocksize);goto failed_mount;}sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -le32_to_cpu(es->s_log_block_size);sbi->s_clusters_per_group =le32_to_cpu(es->s_clusters_per_group);if (sbi->s_clusters_per_group > blocksize * 8) {ext4_msg(sb, KERN_ERR,"#clusters per group too big: %lu",sbi->s_clusters_per_group);goto failed_mount;}if (sbi->s_blocks_per_group !=(sbi->s_clusters_per_group * (clustersize / blocksize))) {ext4_msg(sb, KERN_ERR, "blocks per group (%lu) and ""clusters per group (%lu) inconsistent",sbi->s_blocks_per_group,sbi->s_clusters_per_group);goto failed_mount;}} else {if (clustersize != blocksize) {ext4_msg(sb, KERN_ERR,"fragment/cluster size (%d) != ""block size (%d)", clustersize, blocksize);goto failed_mount;}if (sbi->s_blocks_per_group > blocksize * 8) {ext4_msg(sb, KERN_ERR,"#blocks per group too big: %lu",sbi->s_blocks_per_group);goto failed_mount;}sbi->s_clusters_per_group = sbi->s_blocks_per_group;sbi->s_cluster_bits = 0;}sbi->s_cluster_ratio = clustersize / blocksize;/* 判断簇大小是否是为 8块的标准组 的整数倍   */if (sbi->s_blocks_per_group == clustersize << 3)set_opt2(sb, STD_GROUP_SIZE);/** Test whether we have more sectors than will fit in sector_t,* and whether the max offset is addressable by the page cache.*///测试我们是否有比sector_t所能容纳的更多的扇区,以及页面缓存是否可以寻址最大偏移量。err = generic_check_addressable(sb->s_blocksize_bits,ext4_blocks_count(es));if (err) {ext4_msg(sb, KERN_ERR, "filesystem"" too large to mount safely on this system");if (sizeof(sector_t) < 8)ext4_msg(sb, KERN_WARNING, "CONFIG_LBDAF not enabled");goto failed_mount;}if (EXT4_BLOCKS_PER_GROUP(sb) == 0)goto cantfind_ext4;/* 根据设备大小检查块计数 */blocks_count = sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits;if (blocks_count && ext4_blocks_count(es) > blocks_count) {ext4_msg(sb, KERN_WARNING, "bad geometry: block count %llu ""exceeds size of device (%llu blocks)",ext4_blocks_count(es), blocks_count);goto failed_mount;}/** It makes no sense for the first data block to be beyond the end* of the filesystem.*///判断第一个数据块是否超出文件系统的末端if (le32_to_cpu(es->s_first_data_block) >= ext4_blocks_count(es)) {ext4_msg(sb, KERN_WARNING, "bad geometry: first data ""block %u is beyond end of filesystem (%llu)",le32_to_cpu(es->s_first_data_block),ext4_blocks_count(es));goto failed_mount;}if ((es->s_first_data_block == 0) && (es->s_log_block_size == 0) &&(sbi->s_cluster_ratio == 1)) {ext4_msg(sb, KERN_WARNING, "bad geometry: first data ""block is 0 with a 1k block and cluster size");goto failed_mount;}//计算块数量blocks_count = (ext4_blocks_count(es) -le32_to_cpu(es->s_first_data_block) +EXT4_BLOCKS_PER_GROUP(sb) - 1);do_div(blocks_count, EXT4_BLOCKS_PER_GROUP(sb));if (blocks_count > ((uint64_t)1<<32) - EXT4_DESC_PER_BLOCK(sb)) {ext4_msg(sb, KERN_WARNING, "groups count too large: %u ""(block count %llu, first data block %u, ""blocks per group %lu)", sbi->s_groups_count,ext4_blocks_count(es),le32_to_cpu(es->s_first_data_block),EXT4_BLOCKS_PER_GROUP(sb));goto failed_mount;}sbi->s_groups_count = blocks_count;sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count,(EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) !=le32_to_cpu(es->s_inodes_count)) {ext4_msg(sb, KERN_ERR, "inodes count not valid: %u vs %llu",le32_to_cpu(es->s_inodes_count),((u64)sbi->s_groups_count * sbi->s_inodes_per_group));ret = -EINVAL;goto failed_mount;}//计算块组数量db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /EXT4_DESC_PER_BLOCK(sb);if (ext4_has_feature_meta_bg(sb)) {if (le32_to_cpu(es->s_first_meta_bg) > db_count) {ext4_msg(sb, KERN_WARNING,"first meta block group too large: %u ""(group descriptor block count %u)",le32_to_cpu(es->s_first_meta_bg), db_count);goto failed_mount;}}//分配块组描述符sbi->s_group_desc = kvmalloc_array(db_count,sizeof(struct buffer_head *),GFP_KERNEL);if (sbi->s_group_desc == NULL) {ext4_msg(sb, KERN_ERR, "not enough memory");ret = -ENOMEM;goto failed_mount;}bgl_lock_init(sbi->s_blockgroup_lock);/* 将块组描述符预读到缓冲区缓存中   */for (i = 0; i < db_count; i++) {block = descriptor_loc(sb, logical_sb_block, i);sb_breadahead(sb, block);}for (i = 0; i < db_count; i++) {block = descriptor_loc(sb, logical_sb_block, i);sbi->s_group_desc[i] = sb_bread_unmovable(sb, block);if (!sbi->s_group_desc[i]) {ext4_msg(sb, KERN_ERR,"can't read group descriptor %d", i);db_count = i;goto failed_mount2;}}//记录块组个数sbi->s_gdb_count = db_count;if (!ext4_check_descriptors(sb, logical_sb_block, &first_not_zeroed)) {ext4_msg(sb, KERN_ERR, "group descriptors corrupted!");ret = -EFSCORRUPTED;goto failed_mount2;}//初始化定时器timer_setup(&sbi->s_err_report, print_daily_error_info, 0);/* 注册extent状态树收缩器 */if (ext4_es_register_shrinker(sbi))goto failed_mount3;sbi->s_stripe = ext4_get_stripe_size(sbi);sbi->s_extent_max_zeroout_kb = 32;/** 设置ops,以便它能够读取inode*/sb->s_op = &ext4_sops;sb->s_export_op = &ext4_export_ops;sb->s_xattr = ext4_xattr_handlers;
#ifdef CONFIG_EXT4_FS_ENCRYPTIONsb->s_cop = &ext4_cryptops;
#endif
#ifdef CONFIG_QUOTAsb->dq_op = &ext4_quota_operations;if (ext4_has_feature_quota(sb))sb->s_qcop = &dquot_quotactl_sysfile_ops;elsesb->s_qcop = &ext4_qctl_operations;sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ;
#endif//记录uuidmemcpy(&sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));//初始化链表头,用来记录打开的文件INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */mutex_init(&sbi->s_orphan_lock);//初始化互斥锁sb->s_root = NULL;needs_recovery = (es->s_last_orphan != 0 ||ext4_has_feature_journal_needs_recovery(sb));if (ext4_has_feature_mmp(sb) && !sb_rdonly(sb))if (ext4_multi_mount_protect(sb, le64_to_cpu(es->s_mmp_block)))goto failed_mount3a;/** The first inode we look at is the journal inode.  Don't try* root first: it may be modified in the journal!*///日志节点处理if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {err = ext4_load_journal(sb, es, journal_devnum);if (err)goto failed_mount3a;} else if (test_opt(sb, NOLOAD) && !sb_rdonly(sb) &&ext4_has_feature_journal_needs_recovery(sb)) {ext4_msg(sb, KERN_ERR, "required journal recovery ""suppressed and not mounted read-only");goto failed_mount_wq;} else {/* Nojournal mode, all journal mount options are illegal */if (test_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM)) {ext4_msg(sb, KERN_ERR, "can't mount with ""journal_checksum, fs mounted w/o journal");goto failed_mount_wq;}if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {ext4_msg(sb, KERN_ERR, "can't mount with ""journal_async_commit, fs mounted w/o journal");goto failed_mount_wq;}if (sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ) {ext4_msg(sb, KERN_ERR, "can't mount with ""commit=%lu, fs mounted w/o journal",sbi->s_commit_interval / HZ);goto failed_mount_wq;}if (EXT4_MOUNT_DATA_FLAGS &(sbi->s_mount_opt ^ sbi->s_def_mount_opt)) {ext4_msg(sb, KERN_ERR, "can't mount with ""data=, fs mounted w/o journal");goto failed_mount_wq;}sbi->s_def_mount_opt &= EXT4_MOUNT_JOURNAL_CHECKSUM;clear_opt(sb, JOURNAL_CHECKSUM);clear_opt(sb, DATA_FLAGS);sbi->s_journal = NULL;needs_recovery = 0;goto no_journal;}//如果是64为日志则不支持if (ext4_has_feature_64bit(sb) &&!jbd2_journal_set_features(EXT4_SB(sb)->s_journal, 0, 0,JBD2_FEATURE_INCOMPAT_64BIT)) {ext4_msg(sb, KERN_ERR, "Failed to set 64-bit journal feature");goto failed_mount_wq;}//设置日志有关校验if (!set_journal_csum_feature_set(sb)) {ext4_msg(sb, KERN_ERR, "Failed to set journal checksum ""feature set");goto failed_mount_wq;}/* 验证数据日志记录模式。   */switch (test_opt(sb, DATA_FLAGS)) {case 0:/* No mode set, assume a default based on the journal* capabilities: ORDERED_DATA if the journal can* cope, else JOURNAL_DATA*/if (jbd2_journal_check_available_features(sbi->s_journal, 0, 0, JBD2_FEATURE_INCOMPAT_REVOKE)) {set_opt(sb, ORDERED_DATA);sbi->s_def_mount_opt |= EXT4_MOUNT_ORDERED_DATA;} else {set_opt(sb, JOURNAL_DATA);sbi->s_def_mount_opt |= EXT4_MOUNT_JOURNAL_DATA;}break;case EXT4_MOUNT_ORDERED_DATA:case EXT4_MOUNT_WRITEBACK_DATA:if (!jbd2_journal_check_available_features(sbi->s_journal, 0, 0, JBD2_FEATURE_INCOMPAT_REVOKE)) {ext4_msg(sb, KERN_ERR, "Journal does not support ""requested data journaling mode");goto failed_mount_wq;}default:break;}if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA &&test_opt(sb, JOURNAL_ASYNC_COMMIT)) {ext4_msg(sb, KERN_ERR, "can't mount with ""journal_async_commit in data=ordered mode");goto failed_mount_wq;}set_task_ioprio(sbi->s_journal->j_task, journal_ioprio);//设置日志commit回调函数sbi->s_journal->j_commit_callback = ext4_journal_commit_callback;no_journal:if (!test_opt(sb, NO_MBCACHE)) {sbi->s_ea_block_cache = ext4_xattr_create_cache();if (!sbi->s_ea_block_cache) {ext4_msg(sb, KERN_ERR,"Failed to create ea_block_cache");goto failed_mount_wq;}if (ext4_has_feature_ea_inode(sb)) {sbi->s_ea_inode_cache = ext4_xattr_create_cache();if (!sbi->s_ea_inode_cache) {ext4_msg(sb, KERN_ERR,"Failed to create ea_inode_cache");goto failed_mount_wq;}}}if ((DUMMY_ENCRYPTION_ENABLED(sbi) || ext4_has_feature_encrypt(sb)) &&(blocksize != PAGE_SIZE)) {ext4_msg(sb, KERN_ERR,"Unsupported blocksize for fs encryption");goto failed_mount_wq;}if (DUMMY_ENCRYPTION_ENABLED(sbi) && !sb_rdonly(sb) &&!ext4_has_feature_encrypt(sb)) {ext4_set_feature_encrypt(sb);ext4_commit_super(sb, 1);}/** Get the # of file system overhead blocks from the* superblock if present.*/if (es->s_overhead_clusters)sbi->s_overhead = le32_to_cpu(es->s_overhead_clusters);else {err = ext4_calculate_overhead(sb);if (err)goto failed_mount_wq;}/** The maximum number of concurrent works can be high and* concurrency isn't really necessary.  Limit it to 1.*///如果有需要,可以限制并发队列数量为1EXT4_SB(sb)->rsv_conversion_wq =alloc_workqueue("ext4-rsv-conversion", WQ_MEM_RECLAIM | WQ_UNBOUND, 1);if (!EXT4_SB(sb)->rsv_conversion_wq) {printk(KERN_ERR "EXT4-fs: failed to create workqueue\n");ret = -ENOMEM;goto failed_mount4;}/** The jbd2_journal_load will have done any necessary log recovery,* so we can safely mount the rest of the filesystem now.*///挂载文件系统的其余部分,包括root  root = ext4_iget(sb, EXT4_ROOT_INO, EXT4_IGET_SPECIAL);if (IS_ERR(root)) {ext4_msg(sb, KERN_ERR, "get root inode failed");ret = PTR_ERR(root);root = NULL;goto failed_mount4;}if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {ext4_msg(sb, KERN_ERR, "corrupt root inode, run e2fsck");iput(root);goto failed_mount4;}sb->s_root = d_make_root(root);if (!sb->s_root) {ext4_msg(sb, KERN_ERR, "get root dentry failed");ret = -ENOMEM;goto failed_mount4;}//设置超级快ret = ext4_setup_super(sb, es, sb_rdonly(sb));if (ret == -EROFS) {sb->s_flags |= SB_RDONLY;ret = 0;} else if (ret)goto failed_mount4a;/* 确定新的大型inode的最小大小 */if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE &&sbi->s_want_extra_isize == 0) {sbi->s_want_extra_isize = sizeof(struct ext4_inode) -EXT4_GOOD_OLD_INODE_SIZE;if (ext4_has_feature_extra_isize(sb)) {if (sbi->s_want_extra_isize <le16_to_cpu(es->s_want_extra_isize))sbi->s_want_extra_isize =le16_to_cpu(es->s_want_extra_isize);if (sbi->s_want_extra_isize <le16_to_cpu(es->s_min_extra_isize))sbi->s_want_extra_isize =le16_to_cpu(es->s_min_extra_isize);}}/* 检查是否有足够的inode空间可用 */if (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize >sbi->s_inode_size) {sbi->s_want_extra_isize = sizeof(struct ext4_inode) -EXT4_GOOD_OLD_INODE_SIZE;ext4_msg(sb, KERN_INFO, "required extra inode space not""available");}ext4_set_resv_clusters(sb);//设置ext4系统内存,存放entry入口err = ext4_setup_system_zone(sb);if (err) {ext4_msg(sb, KERN_ERR, "failed to initialize system ""zone (%d)", err);goto failed_mount4a;}ext4_ext_init(sb);//初始化ext4一些feature extentserr = ext4_mb_init(sb);//初始化存放文件内容的内存块if (err) {ext4_msg(sb, KERN_ERR, "failed to initialize mballoc (%d)",err);goto failed_mount5;}//计算空闲块block = ext4_count_free_clusters(sb);ext4_free_blocks_count_set(sbi->s_es, EXT4_C2B(sbi, block));ext4_superblock_csum_set(sb);//设置校验err = percpu_counter_init(&sbi->s_freeclusters_counter, block,GFP_KERNEL);if (!err) {unsigned long freei = ext4_count_free_inodes(sb);sbi->s_es->s_free_inodes_count = cpu_to_le32(freei);ext4_superblock_csum_set(sb);err = percpu_counter_init(&sbi->s_freeinodes_counter, freei,GFP_KERNEL);}if (!err)err = percpu_counter_init(&sbi->s_dirs_counter,ext4_count_dirs(sb), GFP_KERNEL);if (!err)err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0,GFP_KERNEL);if (!err)err = percpu_init_rwsem(&sbi->s_journal_flag_rwsem);if (err) {ext4_msg(sb, KERN_ERR, "insufficient memory");goto failed_mount6;}//如果支持flex_bg则填充flex_infoif (ext4_has_feature_flex_bg(sb))if (!ext4_fill_flex_info(sb)) {ext4_msg(sb, KERN_ERR,"unable to initialize ""flex_bg meta info!");goto failed_mount6;}//注册li_requesterr = ext4_register_li_request(sb, first_not_zeroed);if (err)goto failed_mount6;//注册sysfserr = ext4_register_sysfs(sb);if (err)goto failed_mount7;#ifdef CONFIG_QUOTA/* Enable quota usage during mount. */if (ext4_has_feature_quota(sb) && !sb_rdonly(sb)) {err = ext4_enable_quotas(sb);if (err)goto failed_mount8;}
#endif  /* CONFIG_QUOTA *///初始化超级快参数EXT4_SB(sb)->s_mount_state |= EXT4_ORPHAN_FS;ext4_orphan_cleanup(sb, es);EXT4_SB(sb)->s_mount_state &= ~EXT4_ORPHAN_FS;if (needs_recovery) {ext4_msg(sb, KERN_INFO, "recovery complete");ext4_mark_recovery_complete(sb, es);}if (EXT4_SB(sb)->s_journal) {if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)descr = " journalled data mode";else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)descr = " ordered data mode";elsedescr = " writeback data mode";} elsedescr = "out journal";if (test_opt(sb, DISCARD)) {struct request_queue *q = bdev_get_queue(sb->s_bdev);if (!blk_queue_discard(q))ext4_msg(sb, KERN_WARNING,"mounting with \"discard\" option, but ""the device does not support discard");}if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. ""Opts: %.*s%s%s", descr,(int) sizeof(sbi->s_es->s_mount_opts),sbi->s_es->s_mount_opts,*sbi->s_es->s_mount_opts ? "; " : "", orig_data);if (es->s_error_count)mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes *//* ratelimiting启用消息。 默认值是每5秒10条消息   */ratelimit_state_init(&sbi->s_err_ratelimit_state, 5 * HZ, 10);ratelimit_state_init(&sbi->s_warning_ratelimit_state, 5 * HZ, 10);ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);kfree(orig_data);//释放挂载选项的内存//成功返回return 0;//下面是失败的处理cantfind_ext4:if (!silent)ext4_msg(sb, KERN_ERR, "VFS: Can't find ext4 filesystem");goto failed_mount;#ifdef CONFIG_QUOTA
failed_mount8:ext4_unregister_sysfs(sb);
#endif
failed_mount7:ext4_unregister_li_request(sb);
failed_mount6:ext4_mb_release(sb);if (sbi->s_flex_groups)kvfree(sbi->s_flex_groups);percpu_counter_destroy(&sbi->s_freeclusters_counter);percpu_counter_destroy(&sbi->s_freeinodes_counter);percpu_counter_destroy(&sbi->s_dirs_counter);percpu_counter_destroy(&sbi->s_dirtyclusters_counter);percpu_free_rwsem(&sbi->s_journal_flag_rwsem);
failed_mount5:ext4_ext_release(sb);ext4_release_system_zone(sb);
failed_mount4a:dput(sb->s_root);sb->s_root = NULL;
failed_mount4:ext4_msg(sb, KERN_ERR, "mount failed");if (EXT4_SB(sb)->rsv_conversion_wq)destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq);
failed_mount_wq:if (sbi->s_ea_inode_cache) {ext4_xattr_destroy_cache(sbi->s_ea_inode_cache);sbi->s_ea_inode_cache = NULL;}if (sbi->s_ea_block_cache) {ext4_xattr_destroy_cache(sbi->s_ea_block_cache);sbi->s_ea_block_cache = NULL;}if (sbi->s_journal) {jbd2_journal_destroy(sbi->s_journal);sbi->s_journal = NULL;}
failed_mount3a:ext4_es_unregister_shrinker(sbi);
failed_mount3:del_timer_sync(&sbi->s_err_report);if (sbi->s_mmp_tsk)kthread_stop(sbi->s_mmp_tsk);
failed_mount2:for (i = 0; i < db_count; i++)brelse(sbi->s_group_desc[i]);kvfree(sbi->s_group_desc);
failed_mount:if (sbi->s_chksum_driver)crypto_free_shash(sbi->s_chksum_driver);
#ifdef CONFIG_QUOTAfor (i = 0; i < EXT4_MAXQUOTAS; i++)kfree(sbi->s_qf_names[i]);
#endifext4_blkdev_remove(sbi);brelse(bh);
out_fail:sb->s_fs_info = NULL;kfree(sbi->s_blockgroup_lock);
out_free_base:kfree(sbi);kfree(orig_data);fs_put_dax(dax_dev);return err ? err : ret;
}

ext4_fill_super的工作紧紧是在磁盘挂载的时候从磁盘中读取超级块来填充内存中的超级块结构体。讲完了88

linux虚拟文件系统(三)-ext4文件系统注册分析相关推荐

  1. Linux内核学习:EXT4 文件系统在 Linux 内核系统中的读写过程

    目录 1 概述 2 虚拟文件系统 与 Ext4 文件系统 2.1 sys_write( ) 代码跟踪 2.2 sys_write( ) 过程分析 2.3 sys_write( ) 的核心部分 vfs_ ...

  2. 制作ext4文件系统linux,linux下如何制作ext4文件系统镜像

    1.生成一个空的2MiB文件 dd if=/dev/zero of=rootfs.ext4 bs=1024 count=2048 (指定每一块大小为1024字节,一共又2048块,那么就是2048 * ...

  3. linux虚拟网络设备--内核网桥的实现分析(六)

    一.Linux内核网桥的实现分析 Linux 内核分别在2.2 和 2.4内核中实现了网桥.但是2.2 内核和 2.4内核的实现有很大的区别,2.4中的实现几乎是全部重写了所有的实现代码.本文以2.4 ...

  4. 【Linux】10_存储管理EXT4文件系统详解

    名词解释 EXT4:Fourth extended filesystem,第四代扩展文件系统 XFS:索引(index ) 文件系统 系统限制 Ext3: 文件系统最大16TB Ext4: 文件系统最 ...

  5. 关于Linux系统中的ext4文件系统制作命令的学习与理解,make_ext4fs 参数介绍。

    使用的工具是make_ext4fs   例子:make_ext4fs -s -l 3141572800 -a root -L linux system.img system 在网上查看了些质料对该命令 ...

  6. 如何在 Linux 虚拟机上扩展根文件系统

    https://docs.azure.cn/zh-cn/articles/azure-operations-guide/virtual-machines/linux/aog-virtual-machi ...

  7. oracle+磁盘挂载格式化,oracle asm disk格式化恢复—格式化为ext4文件系统

    昨天中午接到一位朋友紧急求救电话,大概场景如下,asm data磁盘组一共把个asm disk,但是使用4个lun实现的(也就是说每个lun使用fdisk进行分区),该主机上还有一个lun是用来存放备 ...

  8. ext4 文件系统新特性

    Ext4的名称来源于4th extended filesystem,它是广泛应用于Linux的Ext3文件系统的后继.同Ext3类似,Ext4也是日志文件系统.而与Ext3仅仅是将日志功能加入到Ext ...

  9. ext4 文件系统的特点、优缺点以及使用场景

    ext4(Fourth Extended File System)是 Linux 中最新的一种文件系统,它是 ext3 文件系统的后续版本,具有更高的性能.可靠性和扩展性.下面是 ext4 文件系统的 ...

最新文章

  1. 我有一个计划001之数据挖掘面试(更新ing)
  2. VMWare克隆出多个操作系统
  3. 如何用python画一朵玫瑰花-使用Python画一朵美丽的玫瑰花
  4. python中pickle模块的用法_Python中json模块和pickle模块的简单介绍(附示例)
  5. xxl-job 执行结果是空_xxljob dotnet core executor执行器开源
  6. (五)nodejs循序渐进-回调函数和异常处理(基础篇)
  7. 覆盖索引与联合索引_MySQL:你知道什么是覆盖索引吗?
  8. java被oracle收购,sun被oracle收购了,openoffice和virtualbox会不会死?
  9. 自由软件江湖里的码头和规矩
  10. 特定数量的商品如何在高并发下进行库存锁定 ?
  11. OSChina 周五乱弹 —— 美团外卖程序崩溃的真相
  12. springboot点餐微信小程序毕业设计源码221144
  13. Developers are hitting a problem due to tools.jar when using Checkstyle
  14. Outlook打开工享Calendar
  15. CSS3 animation-fill-mode详解
  16. 渗透测试常用在线工具汇总
  17. 什么才是高级软件工程师?
  18. 购房新政:减免住房交易税 放宽还迁房上市条件
  19. c语言编程输出1000以内能被3整除的数,【C语言】找出1000以内可以被3整除的数
  20. 错过必定后悔的干货!深入解析大数据行业的集群架构方案

热门文章

  1. 燕山大学工控软件复习资料
  2. 缓解办公疲劳的方法有很多,这里介绍几种常用的方法...
  3. linux查看eps文件格式,eps 格式图转pdf格式图
  4. Web性能优化方法总结
  5. asp.net仓储物流管理系统VS开发sqlserver数据库web结构C#编程
  6. 【Qt】条形码制作器(Code39编码)
  7. 讯飞输入法键盘计算机,讯飞输入法莫得键盘怎么使用?莫得键盘使用说明
  8. kali爆破wifi密码测试
  9. 机械工业ERP系统“数字排产”
  10. [Uva12260]Free Goodies(dp+贪心)