cgroupのv1とv2の切り分け的なところ

cgroupはv1とv2という2つの実装(と言っていいのか?)があってそれらはkernel/cgroup.cで一つのコードベースにまとまっているのでコード読んでで混乱するなーというところで、誰得なめもを。

特に、4.1とかのカーネルだとv2のコードはまだ正式版となってないけどマージはされていて、DEVELsane_behaviorというオプションを使うことでv2の機能が使えるようになってます(参考:

Linux 3.16 から試せる cgroup の単一階層構造 (1) - TenForward)。今はv2がちゃんと入っているのでこのオプションは不要で、cgroupのマウント時にcgroup v2としてマウントするかどうかで切り分けできます。cgroup_mount()のis_v2がそれです。

2083 static struct dentry *cgroup_mount(struct file_system_type *fs_type,
2084                          int flags, const char *unused_dev_name,
2085                          void *data)
2086 {
2087         bool is_v2 = fs_type == &cgroup2_fs_type;
2088         struct super_block *pinned_sb = NULL;
2089         struct cgroup_namespace *ns = current->nsproxy->cgroup_ns;
2090         struct cgroup_subsys *ss;
2091         struct cgroup_root *root;

v1とv2は階層構造が違うんですが、どちらの構造を見せるかという判定には古めのカーネルならcgrp_dfl_root_visible変数、v2が正式に入ってからならcgrp_dfl_visible変数を使います。これはbool値を格納する変数で、trueならv2の構成を見せます。これらの変数の値を設定するのはcgroup_mount()です。

4.1の場合は以下のようにDEVELsane_behaviorオプションが設定されている場合にtrueをセットします。

1763         /* look for a matching existing root */
1764         if (opts.flags & CGRP_ROOT_SANE_BEHAVIOR) {
1765                 cgrp_dfl_root_visible = true;
1766                 root = &cgrp_dfl_root;
1767                 cgroup_get(&root->cgrp);
1768                 ret = 0;
1769                 goto out_unlock;
1770         }

4.10の場合はcgroup_mountの最初で設定したis_v2がtrueの場合(cgroup v2のファイルシステムをマウントする時)に設定します。

2113         if (is_v2) {
2114                 if (data) {
2115                         pr_err("cgroup2: unknown option \"%s\"\n", (char *)data);
2116                         put_cgroup_ns(ns);
2117                         return ERR_PTR(-EINVAL);
2118                 }
2119                 cgrp_dfl_visible = true;
2120                 root = &cgrp_dfl_root;
2121                 cgroup_get(&root->cgrp);
2122                 goto out_mount;
2123         }

この変数を使っているところは1箇所だけで、proc_cgroup_show()です。この関数は何をやっているかというと/proc//cgroupファイルの中身を表示します。ファイルの中身はこのような感じです。

masami@saga:~$ cat /proc/self/cgroup
11:pids:/user.slice/user-1000.slice/user@1000.service
10:cpuset:/
9:blkio:/
8:net_cls,net_prio:/
7:hugetlb:/
6:perf_event:/
5:cpu,cpuacct:/
4:devices:/user.slice
3:freezer:/
2:memory:/
1:name=systemd:/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service

4.1も4.10カーネルもproc_cgroup_show()の以下のところで使ってます。

5111                 if (root == &cgrp_dfl_root && !cgrp_dfl_root_visible)
5112                         continue;

ここで出てくるcgrp_dfl_rootはstaticな変数です。

144 struct cgroup_root cgrp_dfl_root;

4.1の場合、cgroup_init_early()init_cgroup_root()を呼び出して最初の初期化しています。

4952 int __init cgroup_init_early(void)
4953 {
4954         static struct cgroup_sb_opts __initdata opts;
4955         struct cgroup_subsys *ss;
4956         int i;
4957 
4958         init_cgroup_root(&cgrp_dfl_root, &opts);
4959         cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;

init_cgroup_root()ではcgrp_dfl_rootのメンバ変数cgrpを対象に初期化してます。

1624 static void init_cgroup_root(struct cgroup_root *root,
1625                              struct cgroup_sb_opts *opts)
1626 {
1627         struct cgroup *cgrp = &root->cgrp;
1628 
1629         INIT_LIST_HEAD(&root->root_list);
1630         atomic_set(&root->nr_cgrps, 1);
1631         cgrp->root = root;
1632         init_cgroup_housekeeping(cgrp);
1633         idr_init(&root->cgroup_idr);
1634 
1635         root->flags = opts->flags;
1636         if (opts->release_agent)
1637                 strcpy(root->release_agent_path, opts->release_agent);
1638         if (opts->name)
1639                 strcpy(root->name, opts->name);
1640         if (opts->cpuset_clone_children)
1641                 set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
1642 }

次に、cgroup_init()でさらなる設定が入ります。ここではcgroup_setup_root()でcgroup本体(という言い方で良いのか?)を行っていて、cgroup_init()ではcgroupのサブシステムに関する設定を行っています。

( ´ー`)フゥー...

Linuxシステム[実践]入門 (Software Design plus)

Linuxシステム[実践]入門 (Software Design plus)