読者です 読者をやめる 読者になる 読者になる

TenForward

技術ブログ。はてなダイアリーから移転しました

cgroup 初期化時の v1 の処理だけ抜き出して追ってみる

cgroup Linux Kernel

cgroup 初期化のメモ。間違っている可能性大なので信用しないでください。何度も同じところを繰り返し見てるので忘れないようにメモです。

サブシステム (cgroup_subsys) と各グループのサブシステムの状態 (cgroup_subsys_state) とそれらのセット (css_set) の関係とかわかってないと以下を見てもわけわからんかも。自分用のメモで、間違いあるかもしれません。

start_kernel からまず cgroup_init_early が呼び出された後、cgroup_init が呼び出される。

   492 asmlinkage __visible void __init start_kernel(void)
    :(snip)
   511         cgroup_init_early();
    :(snip)
   661         cgroup_init();

cgroup_init_early

  4960         RCU_INIT_POINTER(init_task.cgroups, &init_css_set);

init_taskcss_set 型のメンバ cgroupsinit_css_set で初期化します。init_css_set は cgroup.c で定義されています。

  4962         for_each_subsys(ss, i) {

これは静的に定義されているサブシステム分要素を持つ cgroup_subsys 型の cgroup_subsys[] 変数 (配列) 分ループをまわすものでした (cgroup の SUBSYS マクロ 参照)。ss には i 番目のサブシステムが入ります。

  4963                 WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id,
  4964                      "invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p name:id=%d:%s\n",
  4965                      i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free,
  4966                      ss->id, ss->name);

まずは

  • サブシステムに必須のメソッドである css_alloc/free が存在しているかのチェック
  • サブシステム名とIDがまだ構造体のメンバに設定されていないチェック

を行います。

  4967                 WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN,
  4968                      "cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]);

次にサブシステム名 (cgroup_subsys_name[i] に入ってます) の名前が長すぎないかチェックしています。

  4969 
  4970                 ss->id = i;
  4971                 ss->name = cgroup_subsys_name[i];

サブシステム (変数の各メンバ) に、ID と名前を設定します。(各サブシステムで cgroup_subsys を定義する際には定義されていないメンバ)

  4973                 if (ss->early_init)
  4974                         cgroup_init_subsys(ss, true);

そしてサブシステムの early_init を行うように 1 が設定されている時は cgroup_init_subsys を呼び出します。

cgroup_init_subsys を見てみると、

  4895 static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)
  4896 {
   :(snip)
  4906         /* Create the root cgroup state for this subsystem */
  4907         ss->root = &cgrp_dfl_root;
  4908         css = ss->css_alloc(cgroup_css(&cgrp_dfl_root.cgrp, ss));

ss->root には v2 の root が放り込まれます。つまり cgroup_init_subsys は v2 の処理を行うだけです。

cgroup_init

cgroup_init はちょっと長いのですが、これも v2 の処理を無視するとやっていることはシンプルです。

  4992         BUG_ON(cgroup_init_cftypes(NULL, cgroup_legacy_base_files));

まずはこれ。cgroup を操作するために各グループに現れるファイルがあります。このファイルを cftype という構造体で定義します。

このファイルのうち、サブシステムの処理とは直接関係ない、cgroup 自体の操作に関連する cgroup コアで扱うファイル群があり、その定義が cftype の配列として静的に定義されています。それが cgroup_legacy_base_files 変数です。これを cgroup_init_cftypes 関数に渡して、ファイル操作の定義を行います (多分)。

  4996         /* Add init_css_set to the hash table */
  4997         key = css_set_hash(init_css_set.subsys);
  4998         hash_add(css_set_table, &init_css_set.hlist, key);

システム上の css_set はすべて css_set_table というハッシュテーブルで管理されますので、それに init_css_set を登録します。css_set 内のサブシステムから計算される値を使ってハッシュのキー (key) を生成し、使用します。とは言っても init_css_set って v2 用じゃ…

そしてまたサブシステム分ループ。ループの最初に以下のような処理がありますが、

  5005                 if (ss->early_init) {
  5006                         struct cgroup_subsys_state *css =
  5007                                 init_css_set.subsys[ss->id];
  5008 
  5009                         css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2,
  5010                                                    GFP_KERNEL);
  5011                         BUG_ON(css->id < 0);
  5012                 } else {
  5013                         cgroup_init_subsys(ss, false);
  5014                 }

ここですが、cgroup_init_early で初期化したサブシステムは ID を確保、されなかったサブシステムは cgroup_init_subsys で初期化します。とは言っても、いずれも v2 処理。

その後の条件分岐、

  5035                 if (ss->dfl_cftypes == ss->legacy_cftypes) {
  5036                         WARN_ON(cgroup_add_cftypes(ss, ss->dfl_cftypes));
  5037                 } else {
  5038                         WARN_ON(cgroup_add_dfl_cftypes(ss, ss->dfl_cftypes));
  5039                         WARN_ON(cgroup_add_legacy_cftypes(ss, ss->legacy_cftypes));
  5040                 }

if 文 true の条件は特別な起動オプションを付けないと満たさない開発目的の条件なので無視。通常は false になるので、v2 の設定を行った (cgroup_add_dfl_cftypes呼び出し) 後の 5039 行目だけが v1 の処理でしょう、と思えますが、実は通常はここは何もしません。この理由は後で。

  5042                 if (ss->bind)
  5043                         ss->bind(init_css_set.subsys[ssid]);
  5044         }

その後、サブシステムに bind 関数が設定されている場合はそれを実行。

  5046         err = sysfs_create_mount_point(fs_kobj, "cgroup");

sysfs にマウントポイントとなるディレクトリを作成 (/sys/fs/cgroup)。

  5050         err = register_filesystem(&cgroup_fs_type);

cgroup というファイルシステムを登録。

  5056         proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations);

proc に “cgroups” というファイルを作成。で終わりです。

cgroup_add_legacy_cftypes で何もしない理由

cgroup_add_legacy_cftypes で v1 っぽい処理を呼び出しているにも関わらず「通常は何もしません」と書いた理由を追ってみます。

cgroup_add_legacy_cftypes

cgroup_add_legacy_cftypes にサブシステムと cgroup_subsys 構造体型の各サブシステムの変数で定義されている legacy_cftypes を渡します。legacy_cftypes は各サブシステムで v1 のグループに出現させるファイルが各サブシステムごとに定義されています。

cgroup_add_legacy_cftypes 関数を見てみます。

  3313 int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
  3314 {
    :(snip)
  3322         if (!cgroup_legacy_files_on_dfl ||
  3323             ss->dfl_cftypes != ss->legacy_cftypes) {
  3324                 for (cft = cfts; cft && cft->name[0] != '\0'; cft++)
  3325                         cft->flags |= __CFTYPE_NOT_ON_DFL;
  3326         }
  3327 
  3328         return cgroup_add_cftypes(ss, cfts);
  3329 }

通常は (開発目的のフラグがオン、またはデフォルトの cftypes が v1 の場合 (これも通常はない) でなければ)、各 cftype 型の変数のメンバ flags に “v2 階層には表示させない” というフラグを設定したのち、cgroup_add_cftypes を呼び出します。

cgroup_add_cftypes

cgroup_add_cftypes 関数

  3263 static int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
  3264 {
   :(snip)
  3273         ret = cgroup_init_cftypes(ss, cfts);
   :(snip)
  3280         ret = cgroup_apply_cftypes(cfts, true);

cgroup_init_cftypescft->ss = ss という cftype 構造体の ss にサブシステムを入れている部分があります。そして cgroup_apply_cftypes 関数を呼びます。

cgroup_apply_cftypes

  3138 static int cgroup_apply_cftypes(struct cftype *cfts, bool is_add)
  3139 {
  3141         struct cgroup_subsys *ss = cfts[0].ss;
  3142         struct cgroup *root = &ss->root->cgrp;

ss には cfts[0].ss を入れてますが、これは cgroup_init_cftypes でサブシステムそのものを入れました。root 変数にはサブシステムの root が入りますが、これは cgroup_init_subsys で v2 の root が入っていたはず。

  3155                 ret = cgroup_addrm_files(cgrp, cfts, is_add);

cgroup_addrm_files

そして cgroup_addrm_files 呼び出し。

  3105 static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[],
  3106                               bool is_add)
  3107 {
   :(snip)
  3113         for (cft = cfts; cft->name[0] != '\0'; cft++) {
  3114                 /* does cft->flags tell us to skip this file on @cgrp? */
  3115                 if ((cft->flags & __CFTYPE_ONLY_ON_DFL) && !cgroup_on_dfl(cgrp))
  3116                         continue;
  3117                 if ((cft->flags & __CFTYPE_NOT_ON_DFL) && cgroup_on_dfl(cgrp))
  3118                         continue;
   :(snip)

というループがあり、

  • 現在の cgrp は v2 の root の cgroup が入っているはず (cgroup_apply_cftypes の処理参照)
  • cft->flags には __CFTYPE_NOT_ON_DFL が放り込まれていたはず (cgroup_add_legacy_cftypes の処理参照)

というわけで、ここはずっと continue で何もしないままループを抜けます。ループを抜けなければ cgroup_add_file で cgroup 用のファイルを追加するのですが、何もしないということです。

関連