Linuxカーネルもくもく会#6に参加してきました
Linuxカーネルもくもく会#6に参加してきました。この会は各自Linuxソースコードの気になる箇所を読み進めたり、Linuxに関する何かをもくもくと進める会です。
cgroup周りのソースコードを読み進めてみているので、読んでみた内容のメモをまとめてみます。linux-3.17-rc3のソースコードを対象にしています。
start_kernel()からcgroup_init_early()を読んでみる
まずはLinuxカーネルのエントリポイントであるinit/main.c:start_kernel()から読み進めてみます。
init/main.c: 500 asmlinkage __visible void __init start_kernel(void) 501 { ... 519 cgroup_init_early(); ...
cgroup_*()な関数群で最初に呼ばれているのはkernel/cgroup.c:cgroup_init_early()です。関数コメントによると、OS起動時にcgroupの初期化とearly initが必要なサブシステムの初期化を行うようです。
kernel/cgroup.c: 4895 /** 4896 * cgroup_init_early - cgroup initialization at system boot 4897 * 4898 * Initialize cgroups at system boot, and initialize any 4899 * subsystems that request early init. 4900 */ 4901 int __init cgroup_init_early(void) 4902 { 4903 static struct cgroup_sb_opts __initdata opts; 4904 struct cgroup_subsys *ss; 4905 int i; 4906 4907 init_cgroup_root(&cgrp_dfl_root, &opts); 4908 cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF; 4909 4910 RCU_INIT_POINTER(init_task.cgroups, &init_css_set); 4911 4912 for_each_subsys(ss, i) { 4913 WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id, 4914 "invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p name:id=%d:%s\n", 4915 i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free, 4916 ss->id, ss->name); 4917 WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN, 4918 "cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]); 4919 4920 ss->id = i; 4921 ss->name = cgroup_subsys_name[i]; 4922 4923 if (ss->early_init) 4924 cgroup_init_subsys(ss, true); 4925 } 4926 return 0; 4927 }
引数cgrp_dfl_rootはグローバル変数で同じソースファイル内で宣言されています。
kernel/cgroup.c: 139 /* 140 * The default hierarchy, reserved for the subsystems that are otherwise 141 * unattached - it never has more than a single cgroup, and all tasks are 142 * part of that cgroup. 143 */ 144 struct cgroup_root cgrp_dfl_root;
変数cgrp_dfl_rootのデータ型である、struct cgroup_rootを見てみます。
include/linux/cgroup.h: 264 /* 265 * A cgroup_root represents the root of a cgroup hierarchy, and may be 266 * associated with a kernfs_root to form an active hierarchy. This is 267 * internal to cgroup core. Don't access directly from controllers. 268 */ 269 struct cgroup_root { 270 struct kernfs_root *kf_root; 271 272 /* The bitmask of subsystems attached to this hierarchy */ 273 unsigned int subsys_mask; 274 275 /* Unique id for this hierarchy. */ 276 int hierarchy_id; 277 278 /* The root cgroup. Root is destroyed on its release. */ 279 struct cgroup cgrp; 280 281 /* Number of cgroups in the hierarchy, used only for /proc/cgroups */ 282 atomic_t nr_cgrps; 283 284 /* A list running through the active hierarchies */ 285 struct list_head root_list; 286 287 /* Hierarchy-specific flags */ 288 unsigned int flags; 289 290 /* IDs for cgroups in this hierarchy */ 291 struct idr cgroup_idr; 292 293 /* The path to use for release notifications. */ 294 char release_agent_path[PATH_MAX]; 295 296 /* The name for this hierarchy - may be empty */ 297 char name[MAX_CGROUP_ROOT_NAMELEN]; 298 };
kernel/cgroup.c:init_cgroup_root()は必要なデータ構造の初期化を行っているだけのようなので、ざっくりと眺める感じで次の処理を読み進めてみます。
kernel/cgroup.c: 1596 static void init_cgroup_root(struct cgroup_root *root, 1597 struct cgroup_sb_opts *opts) 1598 { 1599 struct cgroup *cgrp = &root->cgrp; 1600 1601 INIT_LIST_HEAD(&root->root_list); 1602 atomic_set(&root->nr_cgrps, 1); 1603 cgrp->root = root; 1604 init_cgroup_housekeeping(cgrp); 1605 idr_init(&root->cgroup_idr); 1606 1607 root->flags = opts->flags; 1608 if (opts->release_agent) 1609 strcpy(root->release_agent_path, opts->release_agent); 1610 if (opts->name) 1611 strcpy(root->name, opts->name); 1612 if (opts->cpuset_clone_children) 1613 set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags); 1614 }
init_cgroup_root()の直後で何やらフラグを設定しています。
kernel/cgroup.c: 4901 int __init cgroup_init_early(void) 4902 { ... 4907 init_cgroup_root(&cgrp_dfl_root, &opts); 4908 cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;
CSS_NO_REFはinclude/linux/cgroup.hでenum定義されており、このCSS(Cgroup Subsys State)には参照カウンタが無いことを示しているみたいです(「参照カウンタを持たない」という意味かも)。
include/linux/cgroup.h: 96 /* bits in struct cgroup_subsys_state flags field */ 97 enum { 98 CSS_NO_REF = (1 << 0), /* no reference counting for this css */ 99 CSS_ONLINE = (1 << 1), /* between ->css_online() and ->css_offline() */ 100 CSS_RELEASED = (1 << 2), /* refcnt reached zero, released */ 101 };
cgroup_init_early()を読み進めて行きます。関数の後半にはfor_each_subsys()のループがあり、何やらsubsys的なものを順次処理しているようです。
kernel/cgroup.c: 4901 int __init cgroup_init_early(void) 4902 { ... 4912 for_each_subsys(ss, i) { ... 4920 ss->id = i; 4921 ss->name = cgroup_subsys_name[i]; 4922 4923 if (ss->early_init) 4924 cgroup_init_subsys(ss, true); 4925 } ...
for_each_subsus()はfor文に展開されるマクロだと予想できるのですが、実際どうなっているか見てみます。
kernel/cgroup.c: 376 /** 377 * for_each_subsys - iterate all enabled cgroup subsystems 378 * @ss: the iteration cursor 379 * @ssid: the index of @ss, CGROUP_SUBSYS_COUNT after reaching the end 380 */ 381 #define for_each_subsys(ss, ssid) \ 382 for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \ 383 (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)
上記で参照しているCGROUP_SUBSYS_COUNT、cgroup_subsys_idはenum値なので、最後にCGROUP_SUBSYS_COUNTが付いています。
43 /* define the enumeration of all cgroup subsystems */ 44 #define SUBSYS(_x) _x ## _cgrp_id, 45 enum cgroup_subsys_id { 46 #include <linux/cgroup_subsys.h> 47 CGROUP_SUBSYS_COUNT, 48 }; 49 #undef SUBSYS
cgroup_init_early()の4925行目のcgroup_init_subsys()、こちらはcgroup_subsys_name[]で配列になっています。
kernel/cgroup.c: 125 /* generate an array of cgroup subsystem pointers */ 126 #define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys, 127 static struct cgroup_subsys *cgroup_subsys[] = { 128 #include <linux/cgroup_subsys.h> 129 }; 130 #undef SUBSYS 131 132 /* array of cgroup subsystem names */ 133 #define SUBSYS(_x) [_x ## _cgrp_id] = #_x, 134 static const char *cgroup_subsys_name[] = { 135 #include <linux/cgroup_subsys.h> 136 }; 137 #undef SUBSYS
linux/cgroup_subsys.hの中身を見てみます。
linux/cgroup_subsys.h: 1 /* 2 * List of cgroup subsystems. 3 * 4 * DO NOT ADD ANY SUBSYSTEM WITHOUT EXPLICIT ACKS FROM CGROUP MAINTAINERS. 5 */ 6 #if IS_ENABLED(CONFIG_CPUSETS) 7 SUBSYS(cpuset) 8 #endif 9 10 #if IS_ENABLED(CONFIG_CGROUP_SCHED) 11 SUBSYS(cpu) 12 #endif ...
kernel/cgroup.c:SUBSYS()マクロで値が列挙されています。簡単なサンプルコードで展開結果を確認してみます。
$ cat tmp.c #define SUBSYS(_x) [_x ## _cgrp_id] = #_x, SUBSYS(cpu) $ gcc -E tmp.c ... [cpu_cgrp_id] = "cpu",
微妙にややこしいのですが、上記のcpu_cgrp_idはlinux/cgroup_sybsys.h(こっちはヘッダファイル)でenum宣言された値です。
linux/cgroup_subsys.hをテンプレートのように利用し、*cgroup_sybsys[]とenum cgroup_syubsys_idを再定義したSUBSYS()マクロで作るという方法になっています。cgroupサブシステムの列挙を一つのファイルで完結させたいという気持ちは分かるのですが、知らずに読むと混乱してしまいます...。
気を取り直してcgroup_init_early()の続きを読み進めてみます。
kernel/cgroup.c: 4901 int __init cgroup_init_early(void) 4902 { ... 4912 for_each_subsys(ss, i) { ... 4920 ss->id = i; 4921 ss->name = cgroup_subsys_name[i]; 4922 4923 if (ss->early_init) 4924 cgroup_init_subsys(ss, true); 4925 } 4926 return 0; 4927 }
再度for_each_subsys()、変数ssに入るのはcgroup_sybsys[]ですね。
381 #define for_each_subsys(ss, ssid) \ 382 for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \ 383 (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)
cgroup_subsys[]の中身を見てみます。サブシステムごとに関数ポインタを渡せるみたいですが、この段階ではidとnameの設定とearly_initが真の場合にcgroup_init_subsys()を呼ぶだけですね。
include/cgroup/cgroup.h: 623 struct cgroup_subsys { 624 struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css); 625 int (*css_online)(struct cgroup_subsys_state *css); 626 void (*css_offline)(struct cgroup_subsys_state *css); 627 void (*css_free)(struct cgroup_subsys_state *css); 628 void (*css_reset)(struct cgroup_subsys_state *css); 629 630 int (*can_attach)(struct cgroup_subsys_state *css, 631 struct cgroup_taskset *tset); 632 void (*cancel_attach)(struct cgroup_subsys_state *css, 633 struct cgroup_taskset *tset); 634 void (*attach)(struct cgroup_subsys_state *css, 635 struct cgroup_taskset *tset); 636 void (*fork)(struct task_struct *task); 637 void (*exit)(struct cgroup_subsys_state *css, 638 struct cgroup_subsys_state *old_css, 639 struct task_struct *task); 640 void (*bind)(struct cgroup_subsys_state *root_css); 641 642 int disabled; 643 int early_init; ... 660 /* the following two fields are initialized automtically during boot */ 661 int id; 662 #define MAX_CGROUP_TYPE_NAMELEN 32 663 const char *name;
続けてcgroup_init_subsys()も読み進めようと思ったのですが、このあたりでLinuxもくもく会の終了時刻に。続きは次回ですね。
kernel/cgroup.c: 4845 static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) 4846 { 4847 struct cgroup_subsys_state *css; 4848 4849 printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name); ...
まとめ
Linuxカーネルもくもく会に参加し、cgroupソースコードを読み進めてみました。次回のもくもく会でもcgroup周りを読んでみようと思います。