一. 背景
-
cgroup v1目前是每个层级对应一个子系统,每个子系统之间相互独立,各系统之间很难协同工作,blkcg和memcg能分别控制某个进程的资源使用量,但是blkcg 对进程资源限制的时候无法感知 memcg中进程资源的使用量,因此对bufferio的限制一直没有实现。
-
为解决这一问题,openeuler提供了一种方案,在memcg中增加一个成员struct list_head memcg_node;用于指向与memcg关联的blkcg,在blkcg中增加一个成员struct list_head memcg_list;表示指向此blkcg中的所有memcg。系统启动后可以在用户态手动指定blkcg和memcg的映射关系来实现blkcg和memcg的绑定。
-
目前我们不希望在用户态手动实现blkcg和memcg的绑定关系,最好能在内核态就实现这种绑定关系。
二. blkcg和memcg内核态绑定设置
阿里提供了一种自动绑定方案,维护一个Radix Trie*memcg_blkcg_tree类型的数据结构来存储所有cgroup的memcg和blkcg的映射关系, 在不同的时机点对树进行增删查的操作。因此我们我们以华为方案为基,参考阿里的方案。在memcg_blkcg_tree插入节点的时机点更新memcg_node和memcg_list。以实现memcg和blkcg的绑定。
static int cgroup_migrate_execute(struct cgroup_mgctx *mgctx)
{
struct cgroup_taskset *tset = &mgctx->tset;
struct cgroup_subsys *ss;
struct task_struct *task, *tmp_task;
struct css_set *cset, *tmp_cset;
int ssid, failed_ssid, ret;
/* check that we can legitimately attach to the cgroup */
if (tset->nr_tasks) {
do_each_subsys_mask(ss, ssid, mgctx->ss_mask) {
if (ss->can_attach) {
tset->ssid = ssid;
ret = ss->can_attach(tset);
if (ret) {
failed_ssid = ssid;
goto out_cancel_attach;
}
}
} while_each_subsys_mask();
}
/*
* Now that we're guaranteed success, proceed to move all tasks to
* the new cgroup. There are no failure cases after here, so this
* is the commit point.
*/
spin_lock_irq(&css_set_lock);
list_for_each_entry(cset, &tset->src_csets, mg_node) {
list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list) {
struct css_set *from_cset = task_css_set(task);
struct css_set *to_cset = cset->mg_dst_cset;
get_css_set(to_cset);
to_cset->nr_tasks++;
css_set_move_task(task, from_cset, to_cset, true);
from_cset->nr_tasks--;
/*
* If the source or destination cgroup is frozen,
* the task might require to change its state.
*/
cgroup_freezer_migrate_task(task, from_cset->dfl_cgrp,
to_cset->dfl_cgrp);
put_css_set_locked(from_cset);
}
}
spin_unlock_irq(&css_set_lock);
/*
* Migration is committed, all target tasks are now on dst_csets.
* Nothing is sensitive to fork() after this point. Notify
* controllers that migration is complete.
*/
tset->csets = &tset->dst_csets;
if (tset->nr_tasks) {
do_each_subsys_mask(ss, ssid, mgctx->ss_mask) {
if (ss->attach) {
tset->ssid = ssid;
ss->attach(tset);
}
if (sysctl_bind_memcg_blkcg_enable) {
list_for_each_entry(cset, &tset->dst_csets, mg_node)
bind_memcg_blkcg_link(ss, cset);
}
} while_each_subsys_mask();
}
void bind_memcg_blkcg_link(struct cgroup_subsys *ss,
struct css_set *cset)
{
struct cgroup_subsys_state *blkcg_css;
struct cgroup_subsys_state *memcg_css;
if (!cgroup1_writeback_enabled())
return;
if (ss->id != io_cgrp_id && ss->id != memory_cgrp_id)
return;
memcg_css = cset->subsys[memory_cgrp_id];
blkcg_css = cset->subsys[io_cgrp_id];
if (!memcg_css || !blkcg_css)
return;
if ((memcg_css == &root_mem_cgroup->css) ||
(blkcg_css == blkcg_root_css))
return;
if (IS_ERR(blkcg_css->cgroup))
return;
wb_attach_memcg_to_blkcg(memcg_css, blkcg_css);
}
三 方案安全性
该方案只是对华为和阿里的方案进行整合,改动不大。并且提供了特性启动的开关,可以根据需要来选择是否开启。提供了bind_memcg_blkcg_enable开关来在运行时动态关闭绑定特性
四 测试
1. 起一个docker
2. docker相关的memcg信息和blkcg信息在/sys/fs/cgroup/memory/docker 和 /sys/fs/cgroup/blkio/docker
3. 对写入的设备限速
echo 253:0 209715200 > /sys/fs/cgroup/blkio/docker/{$docker_id}/blkio.throttle.write_bps_device
253:0为设备号,lsblk之后可以看到
4. 测速
docker中运行
dd if=/dev/zero of=./testfile bs=4k count=10000
本地运行
iostat -xz 1 vda
可以看到,限速成功