返回顶部

内核ceph_mount流程

内核ceph_mount流程

1. 用户空间命令行发起 mount 请求

mount -t ceph $(hostname):6789:/ /mnt/ceph/ -o name=admin,secret=$(ceph auth get-key client.admin)

执行这条ceph挂载命令时会发生什么?

2. 进入内核 sys_mount 系统调用

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
		char __user *, type, unsigned long, flags, void __user *, data)

sys_mount 接受用户传入的设备名、挂载点、文件系统类型、挂载标志和挂载选项。
它将参数传递给 do_mount() 函数,进行更详细的挂载操作。

3. do_mount() 函数

long do_mount(const char *dev_name, const char __user *dir_name,
		const char *type_page, unsigned long flags, void *data_page)

do_mount 函数负责解析和检查挂载参数。

调用 do_new_mount 函数,尝试将文件系统挂载到指定路径。

4. do_new_mount() 函数

static int do_new_mount(struct path *path, const char *fstype, int flags,
			int mnt_flags, const char *name, void *data)

do_new_mount 主要负责创建 vfsmount 结构并调用 vfs_kern_mount 函数,向虚拟文件系统(VFS)提交挂载请求。

vfsmount 是一个内核结构体,用于在 VFS 中表示文件系统的挂载点,包含挂载的信息和路径。

5. vfs_kern_mount() 函数

struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)

vfs_kern_mount 是挂载路径的关键函数,它通过文件系统类型(file_system_type)找到具体的挂载函数,并创建一个 vfsmount 结构。

调用 type->mount(),即调用对应文件系统类型的 mount 函数。

对于 Ceph 文件系统,file_system_type 是 ceph_fs_type,它的 mount 函数是 ceph_mount()。

6. ceph_mount() 函数

static struct dentry *ceph_mount(struct file_system_type *fs_type,
		       int flags, const char *dev_name, void *data)

ceph_mount 是 Ceph 文件系统特有的挂载函数。

ceph_mount 主要负责初始化 Ceph 文件系统客户端,连接到 Ceph 集群,并创建文件系统的根目录(dentry)。

ceph_mount 使用 Ceph 的用户态库(libceph)进行 Ceph 集群通信,包括连接到 Ceph Monitor、OSD 等服务。

挂载成功后返回根目录 dentry 给 VFS。

详细看一下代码!!!
ceph mount过程,主要在fs/ceph/super.c文件中。

ceph_mount解析挂载选项,创建文件客户端,初始化mds客户端,然后进入到ceph_real_mountceph_real_mount 将客户端节点挂载到 Ceph 文件系统集群,并打开文件系统的根目录。其中__ceph_open_session - ceph_monc_open_session建立与mon的连接。open_root_dentry - ceph_mdsc_create_request建立与mds的连接。

static struct dentry *ceph_mount(struct file_system_type *fs_type,
		       int flags, const char *dev_name, void *data)
{
	struct super_block *sb;
	struct ceph_fs_client *fsc;
	struct dentry *res;
	int err;
	int (*compare_super)(struct super_block *, void *) = ceph_compare_super;
	struct ceph_mount_options *fsopt = NULL;
	struct ceph_options *opt = NULL;

	dout("ceph_mount\n");

#ifdef CONFIG_CEPH_FS_POSIX_ACL
	flags |= MS_POSIXACL;
#endif
	err = parse_mount_options(&fsopt, &opt, flags, data, dev_name);
	if (err < 0) {
		res = ERR_PTR(err);
		goto out_final;
	}

	/* create client (which we may/may not use) */
	fsc = create_fs_client(fsopt, opt);
	if (IS_ERR(fsc)) {
		res = ERR_CAST(fsc);
		destroy_mount_options(fsopt);
		ceph_destroy_options(opt);
		goto out_final;
	}

	err = ceph_mdsc_init(fsc);
	if (err < 0) {
		res = ERR_PTR(err);
		goto out;
	}

	if (ceph_test_opt(fsc->client, NOSHARE))
		compare_super = NULL;
	sb = sget(fs_type, compare_super, ceph_set_super, flags, fsc);
	if (IS_ERR(sb)) {
		res = ERR_CAST(sb);
		goto out;
	}

	if (ceph_sb_to_client(sb) != fsc) {
		ceph_mdsc_destroy(fsc);
		destroy_fs_client(fsc);
		fsc = ceph_sb_to_client(sb);
		dout("get_sb got existing client %p\n", fsc);
	} else {
		dout("get_sb using new client %p\n", fsc);
		err = ceph_register_bdi(sb, fsc);
		if (err < 0) {
			res = ERR_PTR(err);
			goto out_splat;
		}
	}

	res = ceph_real_mount(fsc);
	if (IS_ERR(res))
		goto out_splat;
	dout("root %p inode %p ino %llx.%llx\n", res,
	     res->d_inode, ceph_vinop(res->d_inode));
	return res;

out_splat:
	ceph_mdsc_close_sessions(fsc->mdsc);
	deactivate_locked_super(sb);
	goto out_final;

out:
	ceph_mdsc_destroy(fsc);
	destroy_fs_client(fsc);
out_final:
	dout("ceph_mount fail %ld\n", PTR_ERR(res));
	return res;
}
static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
{
	int err;
	unsigned long started = jiffies;  /* note the start time */
	struct dentry *root;

	dout("mount start %p\n", fsc);
	mutex_lock(&fsc->client->mount_mutex);

	if (!fsc->sb->s_root) {
		const char *path;
		err = __ceph_open_session(fsc->client, started);
		if (err < 0)
			goto out;

		if (!fsc->mount_options->server_path) {
			path = "";
			dout("mount opening path \\t\n");
		} else {
			path = fsc->mount_options->server_path + 1;
			dout("mount opening path %s\n", path);
		}

		err = ceph_fs_debugfs_init(fsc);
		if (err < 0)
			goto out;

		root = open_root_dentry(fsc, path, started);
		if (IS_ERR(root)) {
			err = PTR_ERR(root);
			goto out;
		}
		fsc->sb->s_root = dget(root);
	} else {
		root = dget(fsc->sb->s_root);
	}

	fsc->mount_state = CEPH_MOUNT_MOUNTED;
	dout("mount success\n");
	mutex_unlock(&fsc->client->mount_mutex);
	return root;

out:
	mutex_unlock(&fsc->client->mount_mutex);
	return ERR_PTR(err);
}

7. 返回到用户空间

挂载成功后,sys_mount 返回到用户空间,mount 命令得到返回结果。

此时,Ceph 文件系统的根目录已经挂载在指定的挂载点,用户可以通过挂载点访问 Ceph 文件系统。

查看日志

/var/log/messages中只看到这两条相关日志。

Nov xx 11:03:56 node1 kernel: libceph: mon0 192.xxx.xxx.xxx:6789 session established
Nov xx 11:03:56 node1 kernel: libceph: client50981349 fsid ed69ef4c-63ed-436f-a733-c53c0037105d

以上日志信息远远不够,准备提高内核日志打印级别继续看看。

内核日志级别调整

内核日志级别可以通过/proc/sys/kernel/printk文件来修改

先来看看这个文件初始是什么样子

[root@node1 ~]# cat /proc/sys/kernel/printk
4	4	1	7

这四个值的定义可以在kernel/printk.c中找到

int console_printk[4] = {
	DEFAULT_CONSOLE_LOGLEVEL,	/* console_loglevel */
	DEFAULT_MESSAGE_LOGLEVEL,	/* default_message_loglevel */
	MINIMUM_CONSOLE_LOGLEVEL,	/* minimum_console_loglevel */
	DEFAULT_CONSOLE_LOGLEVEL,	/* default_console_loglevel */
};

而内核日志级别有以下七种

#define KERN_EMERG	KERN_SOH "0"	/* system is unusable */
#define KERN_ALERT	KERN_SOH "1"	/* action must be taken immediately */
#define KERN_CRIT	KERN_SOH "2"	/* critical conditions */
#define KERN_ERR	KERN_SOH "3"	/* error conditions */
#define KERN_WARNING	KERN_SOH "4"	/* warning conditions */
#define KERN_NOTICE	KERN_SOH "5"	/* normal but significant condition */
#define KERN_INFO	KERN_SOH "6"	/* informational */
#define KERN_DEBUG	KERN_SOH "7"	/* debug-level messages */

#define KERN_DEFAULT	KERN_SOH "d"	/* the default kernel loglevel */

只需要调整对应数字就可以,比如

echo 8 4 1 7 > /proc/sys/kernel/printk
注意:控制台只会显示大于所设置等级的信息,比如想要看debug(7)日志信息,需要设置为7以上

方法二:
确保/etc/rsyslog.conf文件中 #kern.* 注释被取消!
systemctl restart rsyslog
这样内核产生的所有日志信息都会被记录到相应的文件中,包括 dout 产生的调试信息。

补充:
dmesg 的信息在环缓冲区中,只包含最新的内核日志;
/var/log/messages 是磁盘上的文件,保留较长时间的日志记录。

补充 - 内核开启DEBUG

在生产环境中,出于安全和性能的考虑,可能会禁用调试信息,这时候调整日志打印级别是不生效的,这时候需要在内核代码中打开DEBUG
方法一
修改Makefile文件,比如我们要打开fs/ceph/下所有文件及子目录下的DEBUG宏,可以在fs/ceph/Makefile中添加subdir-ccflags-y := -DDEBUG选项

方法二
pr_debug/dev_dbg模块最上面加上#define DEBUG语句

开启之后,在dmesg中就可以看到pr_debug/dev_dbg的打印信息了。

posted @ 2024-11-21 14:51  十方央丶  阅读(121)  评论(0)    收藏  举报