背景

线上 blkio cgroup 层级关系中 io_merged 文件内容不符合预期.

4.9

初始化

我们都知道 blkio 是基于 IO 的 scheduler 的实现的, 基于线上的唯一的一块SATA盘的且调度算法是 cfq, 读一下 cfq 实现 blkio 的相关代码,首先到 cfq 的模块的入口函数 cfq_init()移除了一些错误处理相关的代码.

static int __init cfq_init(void)
{
	int ret;

#ifdef CONFIG_CFQ_GROUP_IOSCHED
	ret = blkcg_policy_register(&blkcg_policy_cfq); // 如果 CONFIG_CFQ_GROUP_IOSCHED , 也就是启动 blkio 需要的内核选项, 这个变量的位置就是
	if (ret)
		return ret;
#else
	cfq_group_idle = 0;
#endif

	ret = -ENOMEM;
	cfq_pool = KMEM_CACHE(cfq_queue, 0); 
	if (!cfq_pool)
		goto err_pol_unreg;

	ret = elv_register(&iosched_cfq); // ...实现调度算法的 callback func 即可
	if (ret)
		goto err_free_pool;
...

blkcg_policy_register(&blkcg_policy_cfq) 中的 blkcg_policy_cfq 变量的初始化如下. 其中的 cfq_blkcg_legacy_files 就是需要特别关注点的他在用户态以文件形式体现出来.

#ifdef CONFIG_CFQ_GROUP_IOSCHED
static struct blkcg_policy blkcg_policy_cfq = {
	.dfl_cftypes		= cfq_blkcg_files, // 这里变量是个只有只有一个 .name = "weight", 
	.legacy_cftypes		= cfq_blkcg_legacy_files, // 这些是主要的用户态 blkio 文件 

	.cpd_alloc_fn		= cfq_cpd_alloc, //  有 fn 是回调函数, 没有的就是用户态文件
	.cpd_init_fn		= cfq_cpd_init,
	.cpd_free_fn		= cfq_cpd_free,
	.cpd_bind_fn		= cfq_cpd_bind,

	.pd_alloc_fn		= cfq_pd_alloc,
	.pd_init_fn		= cfq_pd_init,
	.pd_offline_fn		= cfq_pd_offline,
	.pd_free_fn		= cfq_pd_free,
	.pd_reset_stats_fn	= cfq_pd_reset_stats,
};
#endif

其实我们在 Linux 中看的 blkio.io_merged 就是这里的这个变量产生的, 其中 blkio 是 sub system 名字. 在下面的代码中会拼凑出.

static struct cftype cfq_blkcg_legacy_files[] = {
	/* on root, weight is mapped to leaf_weight */
...
	{
		.name = "io_merged",
		.private = offsetof(struct cfq_group, stats.merged),
		.seq_show = cfqg_print_rwstat,
	},
...

看一下 blkiocg policy 是如何注册的这些信息中, 虽然对其一部分逻辑还没有完全搞懂,但是我知道了这个随着 CFQ 一起初始化了,在内核空间中维护一些变量.

/**
 * blkcg_policy_register - register a blkcg policy
 * @pol: blkcg policy to register
 *
 * Register @pol with blkcg core.  Might sleep and @pol may be modified on
 * successful registration.  Returns 0 on success and -errno on failure.
 */
int blkcg_policy_register(struct blkcg_policy *pol)
{
	struct blkcg *blkcg;
	int i, ret;
....
	/* register @pol */
	pol->plid = i;
	blkcg_policy[pol->plid] = pol;

	/* allocate and install cpd's */
	if (pol->cpd_alloc_fn) {
		list_for_each_entry(blkcg, &all_blkcgs, all_blkcgs_node) {
			struct blkcg_policy_data *cpd;

			cpd = pol->cpd_alloc_fn(GFP_KERNEL);
...
			blkcg->cpd[pol->plid] = cpd; // 看到现在对这些不是很明白
			cpd->blkcg = blkcg;
			cpd->plid = pol->plid;
			pol->cpd_init_fn(cpd);
		}
	}
..
	/* everything is in place, add intf files for the new policy */
	if (pol->dfl_cftypes)
// cgroup_add_dfl_cftypes 函数描述 add an array of cftypes for default hierarchy 
// Similar to cgroup_add_cftypes() but the added files are only used for the default hierarchy.
		WARN_ON(cgroup_add_dfl_cftypes(&io_cgrp_subsys, 
					       pol->dfl_cftypes));
	if (pol->legacy_cftypes)
// cgroup_add_legacy_cftypes - add an array of cftypes for legacy hierarchies
// Similar to cgroup_add_cftypes() but the added files are only used for the legacy hierarchies.
// 其实到这里我们知道这个地方就是处理文件初始化的地方, 这里说的是文件初始化不包含内容正确.
// 这里在 cgroup fs blkio 中创建文件的地方, 拼接名字也是在这里完成的.
		WARN_ON(cgroup_add_legacy_cftypes(&io_cgrp_subsys,
						  pol->legacy_cftypes));
.... // 移除一些错误处理的代码,主要是内核分配内存失败释放内存的逻辑
EXPORT_SYMBOL_GPL(blkcg_policy_register);

下面这个就是按照内核提供的 cgroup 框架实现的一个 controller groupsub system 名字叫 blkio, 其实 cgroup 框架的抽象也能猜测行为, 如果猜的不对可以看 kernel/cgroup.c 中的调用方式.

struct cgroup_subsys io_cgrp_subsys = {
	.css_alloc = blkcg_css_alloc, // css alloc = cgroup subsystem 的 内存分配
	.css_offline = blkcg_css_offline, // 就是下掉当前 sub system, 其实没有怎么在生产中见过下掉 sub system
	.css_free = blkcg_css_free, // 释放内存
	.can_attach = blkcg_can_attach, // 从 <https://elixir.bootlin.com/linux/v4.9.10/source/kernel/cgroup.c#L5179> 主要是和管理当前`层级`task 的
	.bind = blkcg_bind, // 文件系统里面的 mount bind
	.dfl_cftypes = blkcg_files, // 涉及的用户态文件
	.legacy_cftypes = blkcg_legacy_files, // 涉及的用户态文件
	.legacy_name = "blkio",
...
};
EXPORT_SYMBOL_GPL(io_cgrp_subsys);

以上基本上将 blkio 在 cfq 实现的初始化.

回头看 io_merged 存在内容为空问题

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c1ac7264-c460-4e60-8ea8-ea17a7c06079/Untitled.png

看一下 blkio.io_merged 这个文件写的过程.

https://viewer.diagrams.net/?border=0&highlight=0000ff&edit=_blank&layers=1&nav=1&title=blkio写io_merged.drawio&open=Uhttps%3A%2F%2Fdrive.google.com%2Fuc%3Fid%3D1Zl8VWOPQHTGsu5aVQykhP0hNTtsGrMU6%26export%3Ddownload