Linux内核有什么之块设备驱动有什么第七回 —— 邂逅的三个文件系统之二:实际文件系统(4)

接前一篇文章:Linux内核有什么之块设备驱动有什么第六回 —— 邂逅的三个文件系统之二:实际文件系统(3)

本文内容参考:

Linux设备驱动开发详解 —— 基于最新的Linux4.0内核》 宋宝华,机械工业出版社

34 | 块设备(上):如何建立代理商销售模式?-趣谈Linux操作系统-极客时间

特此致谢!

上回书以F2FS文件系统为例,讲解了块设备邂逅的第二个文件系统 —— 实际文件系统的挂载流程中的mount_bdev函数调用的第1个函数lookup_bdev。再来看一下调用流程:

struct file_system_type f2fs_fs_type --->

    f2fs_fs_type->mount ---> 

        f2fs_mount --->

            mount_bdev --->

                lookup_bdev

为了便于理解和回顾,再次贴出mount_bdev函数的源码,在Linux内核源码根目录/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 super_block *s;
	int error;
	dev_t dev;
 
	error = lookup_bdev(dev_name, &dev);
	if (error)
		return ERR_PTR(error);
 
	flags |= SB_NOSEC;
	s = sget(fs_type, test_bdev_super, set_bdev_super, flags, &dev);
	if (IS_ERR(s))
		return ERR_CAST(s);
 
	if (s->s_root) {
		if ((flags ^ s->s_flags) & SB_RDONLY) {
			deactivate_locked_super(s);
			return ERR_PTR(-EBUSY);
		}
	} else {
		/*
		 * We drop s_umount here because we need to open the bdev and
		 * bdev->open_mutex ranks above s_umount (blkdev_put() ->
		 * bdev_mark_dead()). It is safe because we have active sb
		 * reference and SB_BORN is not set yet.
		 */
		super_unlock_excl(s);
		error = setup_bdev_super(s, flags, NULL);
		__super_lock_excl(s);
		if (!error)
			error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
		if (error) {
			deactivate_locked_super(s);
			return ERR_PTR(error);
		}
 
		s->s_flags |= SB_ACTIVE;
	}
 
	return dget(s->s_root);
}
EXPORT_SYMBOL(mount_bdev);

接下来来看mount_bdev()调用的第2个函数sget。代码片段如下:

	flags |= SB_NOSEC;
	s = sget(fs_type, test_bdev_super, set_bdev_super, flags, &dev);
	if (IS_ERR(s))
		return ERR_CAST(s);

这里注意老版本和新版本的区别,老版本这一部分代码如下:

这两者最大的区别就是函数的最后一个参数。老版本中是前一步得到的struct block_device *bdev;而新版本中则是dev_t *bdev(bdev实际上就是一个u32类型的变量,中间类型为__kernel_dev_t)。

sget函数也在Linux内核源码根目录/fs/super.c中,代码如下:

/**
 *	sget	-	find or create a superblock
 *	@type:	  filesystem type superblock should belong to
 *	@test:	  comparison callback
 *	@set:	  setup callback
 *	@flags:	  mount flags
 *	@data:	  argument to each of them
 */
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();
	struct super_block *s = NULL;
	struct super_block *old;
	int err;

	/* 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;

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_dead(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;
	strscpy(s->s_id, type->name, sizeof(s->s_id));
	list_add_tail(&s->s_list, &super_blocks);
	hlist_add_head(&s->s_instances, &type->fs_supers);
	spin_unlock(&sb_lock);
	get_filesystem(type);
	shrinker_register(s->s_shrink);
	return s;
}
EXPORT_SYMBOL(sget);

sget这个函数也是新老内核版本不一样了。老版本代码是这样:

static int set_bdev_super(struct super_block *s, void *data)
{
    s->s_bdev = data;
    s->s_dev = s->s_bdev->bd_dev;
    s->s_bdi = bdi_get(s->s_bdev->bd_bdi);
    return 0;
}

/**
  * sget - find or create a superblock
  * @type: filesystem type superblock should belong to
  * @test: comparison callback
  * @set: setup callback
  * @flags: mount flags
  * @data: argument to each of them
  */
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)
{
    ......
    return sget_userns(type, test, set, flags, user_ns, data);
}

/**
  * sget_userns - find or create a superblock
  * @type: filesystem type superblock should belong to
  * @test: comparison callback
  * @set: setup callback
  * @flags: mount flags
  * @user_ns: User namespace for the super_block
  * @data: argument to each of them
  */
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 (!s)
    {
        s = alloc_super(type, (flags & ~MS_SUBMOUNT), user_ns);
        ......
    }
    err = set(s, data);
    ......
    s->s_type = type;
    strlcpy(s->s_id, type->name, sizeof(s->s_id)); list_add_tail(&s->s_list, &super_blocks);
    hlist_add_head(&s->s_instances, &type->fs_supers);
    spin_unlock(&sb_lock);
    get_filesystem(type);
    register_shrinker(&s->s_shrink);
    return s;
}

通过新老版本内核代码可以看到,sget这个函数确实有比较大的改动了。

老版本中的机制是:将 block_device塞进superblock里面(在set_bdev_super函数中)。而sget要做的,就是分配一个 super_block,然后调用set_bdev_super这个回调函数。

新版本中的机制和老版本差不多,大体也是这个流程:

    if (!s)
    {
        s = alloc_super(type, (flags & ~MS_SUBMOUNT), user_ns);
        ......
    }
    err = set(s, data);

只不过新版本直接放在了sget函数中,而不像老版本中还多了一层函数调用(sget_userns)。

这里实际看一下新版本内核代码中的set_bdev_super函数的源码,在同文件(Linux内核源码根目录/fs/super.c)中,如下:

static int set_bdev_super(struct super_block *s, void *data)
{
	s->s_dev = *(dev_t *)data;
	return 0;
}

对比一下老版本的代码:

static int set_bdev_super(struct super_block *s, void *data)
{
    s->s_bdev = data;
    s->s_dev = s->s_bdev->bd_dev;
    s->s_bdi = bdi_get(s->s_bdev->bd_bdi);
    return 0;
}

可见,新版本内核代码在这一块更为精简了。

这里也给出test_bdev_super函数的代码,也在同文件中,如下:

static int test_bdev_super(struct super_block *s, void *data)
{
	return !(s->s_iflags & SB_I_RETIRED) && s->s_dev == *(dev_t *)data;
}

到此为止,mount 中一个块设备的过程就结束了。设备打开了,查找得到了dev_t dev,并且塞到了super_block结构(实例)中。

至此,mount_bdev函数就解析完了。欲知后事如何,且看下回分解。

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝天居士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值