Cgroup学习笔记3—代码实现—相关结构和全局变量

 基于 LInux-5.10

一、相关结构

1. 通过多次的 #define 和 #undef SUBSYS 宏来展开 cgroup_subsys.h 中通过 deconfig 使能的 cgroup 子系统来填充相关结构体。 在 include/linux/cgroup-defs.h 中以 SUBSYS(_x) 定义的宏,代入 cpu cgroup 展开为:

enum cgroup_subsys_id {
    ...
    cpu_cgrp_id,
    ...
    CGROUP_SUBSYS_COUNT,
};

在 include/linux/cgroup.h 中以 SUBSYS(_x) 定义的宏,代入 cpu 这个 cgroup 子系统,展开为:

extern struct cgroup_subsys cpu_cgrp_subsys; //定义在kernel/sched/core.c
extern struct static_key_true cpu_cgrp_subsys_enabled_key;
extern struct static_key_true cpu_cgrp_subsys_on_dfl_key;

kernel/cgroup/cgroup.c 中以 SUBSYS(_x) 定义的宏,代入 cpu 这个 cgroup 子系统,展开为:

struct cgroup_subsys *cgroup_subsys[] = {
    [cpu_cgrp_id] = &cpu_cgrp_subsys,
    ....
};

static const char *cgroup_subsys_name[] = {
    [cpu_cgrp_id] = "cpu",
};

/* 就是一个static key,只不过需要用 static_key_true()判断key是真还是假 */
DEFINE_STATIC_KEY_TRUE(cpu_cgrp_subsys_enabled_key);
DEFINE_STATIC_KEY_TRUE(cpu_cgrp_subsys_on_dfl_key);
EXPORT_SYMBOL_GPL(cpu_cgrp_subsys_enabled_key);
EXPORT_SYMBOL_GPL(cpu_cgrp_subsys_on_dfl_key);

static struct static_key_true *cgroup_subsys_enabled_key[] = {
{
    [cpu_cgrp_id] = &cpu_cgrp_subsys_enabled_key,
    ...
}

static struct static_key_true *cgroup_subsys_on_dfl_key[] = {
    [cpu_cgrp_id] = &cpu_cgrp_subsys_on_dfl_key,
    ...
};

从这里可以看到每个 cgroup 子系统的 xx_cgrp_id 值和其在 cgroup_subsys.h 中在使能的部分中的位置有关。名字字符串也是直接来自 cgroup_subsys.h 中。cgroup core 通过 cgroup_subsys 数组对各子系统进行引用。#########

 

2. struct cgroup_file

不要从 cgroup 核心外部访问任何字段

/*
 * cgroup_file 是在 cgroup 中创建的文件实例的句柄,例如,用于生成文件更改通知。
 * 这可以通过设置 cftype->file_offset 来获得。
 */
struct cgroup_file { //cgroup-defs.h
    struct kernfs_node *kn;
    unsigned long notified_at;
    struct timer_list notify_timer;
};

 

3. struct cgroup_subsys_state

struct cgroup_subsys_state {
    struct cgroup *cgroup; //PI
    struct cgroup_subsys *ss; //PI
    struct percpu_ref refcnt;
    struct list_head sibling;
    struct list_head children;
    struct list_head rstat_css_node;
    int id; //PI
    unsigned int flags;
    u64 serial_nr;
    atomic_t online_cnt;
    struct work_struct destroy_work;
    struct rcu_work destroy_rwork;
    struct cgroup_subsys_state *parent;
};

系统维护的 per-subsystem/per-cgroup 状态。这是控制器处理的基本结构构建块。标有“PI:”的成员是公开的和不可变的,可能不带同步的直接访问。

cgroup: PI:这个css附加到的cgroup
ss: PI:这个css附加到的cgroup子系统
refcnt: 引用计数 - 通过 css_[try]get() 和 css_put() 访问
sibling: 锚定在 parent->children 的兄弟列表
rstat_css_node: 刷新锚定在 cgrp->rstat_css_list 上的目标列表
id: PI:子系统唯一 ID。 0 未使用,root 始终为 1。可以使用 css_from_id() 查找匹配的 css。cgroup_init_subsys()中标记需要early初始化的为1,否则是idr分配的一个,但随后early init的又在 cgroup_init()中赋值为idr分配的了。
flags: 标志,如 CSS_NO_REF
serial_nr: 单调递增的唯一序列号,它定义了所有 css 之间的统一顺序。保证所有 ->children 列表都按 ->serial_nr 的升序排列,并用于允许打断和resume遍历。init_and_link_css()中初始化为 css_serial_nr_next++
online_cnt: 由online的自己和孩子增加。用于保证parent不在孩子之前offline。
destroy_work: percpu_ref killing 和 RCU 释放。
parent: 父CSS。放在这里是为了缓存接近包含结构的以下字段。


4. struct css_set

struct css_set {
    struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
    refcount_t refcount;
    struct css_set *dom_cset;
    struct cgroup *dfl_cgrp;
    int nr_tasks;
    struct list_head tasks;
    struct list_head mg_tasks;
    struct list_head dying_tasks;
    struct list_head task_iters;
    struct list_head e_cset_node[CGROUP_SUBSYS_COUNT];
    struct list_head threaded_csets;
    struct list_head threaded_csets_node;
    struct hlist_node hlist;
    struct list_head cgrp_links;
    struct list_head mg_preload_node;
    struct list_head mg_node;
    struct cgroup *mg_src_cgrp;
    struct cgroup *mg_dst_cgrp;
    struct css_set *mg_dst_cset;
    bool dead;
    struct rcu_head rcu_head;
};

css_set 是一个包含指向一组 cgroup_subsys_state 对象的指针的结构。这节省了 task_struct 中的空间并加快了 fork()/exit(),因为单个 inc/dec 和 list_add()/del() 可以增加任务的整个 cgroup set 的引用计数。

subsys[]: 一组子系统的状态,每个子系统一个。 除了子系统注册期间(启动时)的 init_css_set 之外,该数组在创建后是不可变的。cgroup_init_subsys()中: init_css_set.subsys[ss->id] = css
dom_cset: 对于 domain cgroup,以下指向自己. 如果线程化,则到最近域祖先的匹配cset。dom_cset 提供对域 cgroup 及其 csses 的访问,域级资源消耗应计入其中。
dfl_cgrp: 与此 css_set 关联的默认 cgroup。
nr_tasks: 内部任务计数,受 css_set_lock 保护
tasks: 列出使用此 cgroup 组运行的所有任务。
mg_tasks: 列出了属于此 cset 但正在迁移出或迁移入的任务。被 css_set_rwsem 保护,但是,在迁移过程中,一旦将任务移动到 mg_tasks,就可以在持有 cgroup_mutex 的同时安全地读取它。
task_iters: 当前正在执行此 cset 的所有 css_task_iters
e_cset_node[]: 在默认层次结构中,->subsys[ssid] 可能指向附加到祖先的 css,而不是与 css_set 关联的 cgroup。 以下节点锚定在 ->subsys[ssid]->cgroup->e_csets[ssid] 并提供了一种方法来遍历所有附加到给定 cgroup 的 css。
threaded_csets: ->dom_cset 指向此 cset 的所有线程 cset
hlist: 列出在同一哈希slot中运行的所有 cgroup 组。 受 css_set_lock 保护
cgrp_links: 指向从此 css_set 引用的 cgroups 的 cgrp_cset_links 列表。受 css_set_lock 保护。
mg_preload_node: 列出作为源或目标,参与正在进行的迁移的 cset 列表。受 cgroup_mutex 保护
mg_src_cgrp/mg_dst_cgrp/mg_dst_cset: 如果此 cset 充当迁移的源,则设置以下两个字段。mg_src_cgrp 和 mg_dst_cgrp 分别是正在进行的迁移的源 cgroup 和目标 cgroup。mg_dst_cset 是此 cset 上的目标任务应迁移到的目标 cset。受 cgroup_mutex 保护。
dead: 死亡和被清理,迁移时忽略它。
rcu_head: 用于RCU保护下的删除。


5. struct cgroup

struct cgroup {
    struct cgroup_subsys_state self;
    unsigned long flags;
    int level;
    int max_depth;
    int nr_descendants;
    int nr_dying_descendants;
    int max_descendants;
    int nr_populated_csets;
    int nr_populated_domain_children;
    int nr_populated_threaded_children;
    int nr_threaded_children;
    struct kernfs_node *kn;        /* cgroup kernfs entry */
    struct cgroup_file procs_file;    /* handle for "cgroup.procs" */
    struct cgroup_file events_file;    /* handle for "cgroup.events" */
    u16 subtree_control;
    u16 subtree_ss_mask;
    u16 old_subtree_control;
    u16 old_subtree_ss_mask;
    struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT];
    struct cgroup_root *root;
    struct list_head cset_links;
    struct list_head e_csets[CGROUP_SUBSYS_COUNT];
    struct cgroup *dom_cgrp;
    struct cgroup *old_dom_cgrp;        /* used while enabling threaded */
    struct cgroup_rstat_cpu __percpu *rstat_cpu;
    struct list_head rstat_css_list;
    struct cgroup_base_stat last_bstat;
    struct cgroup_base_stat bstat;
    struct prev_cputime prev_cputime;    /* for printing out cputime */
    struct list_head pidlists;
    struct mutex pidlist_mutex;
    wait_queue_head_t offline_waitq;
    struct work_struct release_agent_work;
    struct psi_group psi;
    struct cgroup_bpf bpf;
    atomic_t congestion_count;
    struct cgroup_freezer_state freezer;
    u64 ancestor_ids[];
};

self: 带有 NULL ->ss 的 self css,指向这个 cgroup
flags: 位掩码,init_cgroup_root()中若 ctx->cpuset_clone_children 为真就设置上 CGRP_CPUSET_CLONE_CHILDREN
level: 此 cgroup 所在的深度。根的深度为零,层次结构的每一层都会增加深度。这与 ancestor_ids[] 一起可以确定给定 cgroup 是否是另一个 cgroup 的后代,而无需遍历层次结构。
max_depth: 最大允许下降树深度
nr_descendants: 跟踪可见和dying descent cgroup 的总数。dying cgroups 是被用户删除的 cgroups,但由于其他人持有引用而仍然存在。max_descendants 是允许的最大descent cgroup 数。
nr_descendants 和 nr_dying_descendants 受 cgroup_mutex 和 css_set_lock 保护。读它们时可以持有 cgroup_mutex 和 css_set_lock 中的任何一个;而写,两个锁都应该被持有。
nr_populated_csets: 与此 cgroup 关联的每个非空 css_set 都向 nr_populated_csets 贡献一个。如果此 cgroup 没有任何任务,则计数为零。所有具有非零 nr_populated_csets 和/或 nr_populated_children 的子节点
根据其类型向 nr_populated_domain_children 或 nr_populated_threaded_children 贡献一个。 如果子树中所有类型的 cgroup 都没有任何任务,则每个计数器为零。
nr_threaded_children: 实时线程子 cgroups
subtree_control: 在child cgroup 上启用的子系统的位掩码。 ->subtree_control 是通过“cgroup.subtree_control”配置的,而 ->child_ss_mask 是有效的,可以启用更多子系统。如果在 ->subtree_control 中启用了,则控制器旋钮可用。
subsys[]: 每个注册子系统的私有指针
root: init_cgroup_root()中 cgrp_dfl_root.cgrp.root 指向 cgrp_dfl_root
cset_links: 指向 css_sets 的 cgrp_cset_links 列表,其中包含此 cgroup 中的任务。 受 css_set_lock 保护。
e_csets[]: 在默认层次结构中,禁用了某些 susbsys 的 cgroup 的 css_set 将指向与启用了 subsys 的最近祖先相关联的 css。下面列出了所有指向给定子系统的 cgroup 的 css 的 css_sets。
dom_cgrp: 如果 !threaded,self。如果线程化,它指向最近的域祖先。 在线程子树中,cgroup 不受进程粒度和无内部任务约束。与特定任务无关的域级资源消耗计入 dom_cgrp。
rstat_cpu: 每个 CPU 的递归资源统计。def_cgrp_group 定义时初始化为 &cgrp_dfl_root_rstat_cpu
last_bstat: cgroup基础资源统计
pidlists: pidlists 列表,每个命名空间最多两个(一个用于 procs,一个用于tasks),按需创建。
offline_waitq: 用于等待csses下线
release_agent_work: 用于调度 release agent
psi: 用于跟踪pressure stalls
bpf: 用于存放eBPF程序
congestion_count: 用于判断此 cgroup 上是否存在块拥塞。
freezer: 用于存储内部freezer状态
ancestor_ids[]: 每个级别的祖先的 ID,包括自己。

 

6. struct cgroup_root

struct cgroup_root {
    struct kernfs_root *kf_root;
    unsigned int subsys_mask;
    int hierarchy_id;
    struct cgroup cgrp;
    u64 cgrp_ancestor_id_storage; /* for cgrp->ancestor_ids[0] */
    atomic_t nr_cgrps;
    struct list_head root_list;
    unsigned int flags;
    char release_agent_path[PATH_MAX];
    char name[MAX_CGROUP_ROOT_NAMELEN];
};

cgroup_root 结构代表 cgroup 层次结构的根,并且可以与 kernfs_root 相关联以形成活动层次结构。这是 cgroup core内部的,不要直接从控制器访问。

subsys_mask: 附加到此层次结构的子系统的位掩码。cgroup_init()中 cgrp_dfl_root.subsys_mask 在初始化时,已经初始化的子系统在这个mask中。
hierarchy_id: 此层次结构中保持唯一的ID。
cgrp: 根cgroup。 Root 在释放时被销毁。
nr_cgrps: 层次结构中的 cgroup 数量,仅用于 /proc/cgroups
root_list: 遍历活动层次结构的列表
flags: 特定于层次结构的标志
release_agent_path[]: 用于release notifications的路径。初始化为 ctx->release_agent.
name[]: 此层次结构的名称,可能为空。init_cgroup_root中初始化为 cxt->name。


7. struct cftype

cgroup各文件节点的定义结构,如 "cgroup.procs",主要在 cgroup_init_cftypes 中初始化。

/*
 * struct cftype:cgroup控制文件的处理程序定义
 *
 * 读/写文件时:
 * - 要使用的 cgroup 是 file->f_path.dentry->d_parent->d_fsdata
 * - 文件的 'cftype' 是 file->f_path.dentry->d_fsdata
 */
struct cftype {
    char name[MAX_CFTYPE_NAME];
    unsigned long private;
    size_t max_write_len;
    unsigned int flags; /* CFTYPE_* flags */
    unsigned int file_offset;
    struct cgroup_subsys *ss;    /* NULL for cgroup core files */
    struct list_head node;        /* anchored at ss->cfts */
    struct kernfs_ops *kf_ops;

    int (*open)(struct kernfs_open_file *of);
    void (*release)(struct kernfs_open_file *of);
    u64 (*read_u64)(struct cgroup_subsys_state *css, struct cftype *cft);
    s64 (*read_s64)(struct cgroup_subsys_state *css, struct cftype *cft);
    int (*seq_show)(struct seq_file *sf, void *v);

    /* optional ops, implement all or none */
    void *(*seq_start)(struct seq_file *sf, loff_t *ppos);
    void *(*seq_next)(struct seq_file *sf, void *v, loff_t *ppos);
    void (*seq_stop)(struct seq_file *sf, void *v);
    int (*write_u64)(struct cgroup_subsys_state *css, struct cftype *cft, u64 val);
    int (*write_s64)(struct cgroup_subsys_state *css, struct cftype *cft, s64 val);

    ssize_t (*write)(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off);
    __poll_t (*poll)(struct kernfs_open_file *of, struct poll_table_struct *pt);

#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lock_class_key    lockdep_key;
#endif
};

name[]: 按照惯例,名称应以子系统的名称开头,后跟一个句点。零长度字符串表示 cftype 数组的结尾。
max_write_len: 可以传递给写入的字符串的最大长度,不包括尾随 nul。 如果 <PAGE_SIZE-1,则假定为 PAGE_SIZE-1。
flags: cgroup_init()中 ss->dfl_cftypes 中指定的文件节点会或上 __CFTYPE_ONLY_ON_DFL。ss->legacy_cftypes 中指定的文件节点会或上 __CFTYPE_NOT_ON_DFL,若二者相同指向,就不会或上任何标志。
file_offset: 如果非零,则应包含从 css 开始到 struct cgroup_file 字段的偏移量。cgroup 会将创建的文件的句柄记录到其中。只要包含的 css 保持可访问性,就可以使用记录的句柄。
ss: 用于内部簿记的字段。 注册时自动初始化。cgroup_init_cftypes()中指向对应的子系统。
kf_ops: cgroup_init_cftypes: 若文件节点实现了.seq_start 回调就指向全局 cgroup_kf_ops,否则指向全局 cgroup_kf_single_ops。
write: write()是通用的写回调,它直接映射到 kernfs 写操作并覆盖所有其他操作。最大写入大小由 ->max_write_len 决定。使用 of_css/cft() 访问关联的 css 和 cft。


8. struct cgroup_subsys

cgroup 子系统类型。也就是一个 cgroup 子系统对应一个此结构。详见 Documentation/admin-guide/cgroup-v1/cgroups.rst

struct cgroup_subsys {
    struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css);
    int (*css_online)(struct cgroup_subsys_state *css);
    void (*css_offline)(struct cgroup_subsys_state *css);
    void (*css_released)(struct cgroup_subsys_state *css);
    void (*css_free)(struct cgroup_subsys_state *css);
    void (*css_reset)(struct cgroup_subsys_state *css);
    void (*css_rstat_flush)(struct cgroup_subsys_state *css, int cpu);
    int (*css_extra_stat_show)(struct seq_file *seq, struct cgroup_subsys_state *css);
    int (*can_attach)(struct cgroup_taskset *tset);
    void (*cancel_attach)(struct cgroup_taskset *tset);
    void (*attach)(struct cgroup_taskset *tset);
    void (*post_attach)(void);
    int (*can_fork)(struct task_struct *task, struct css_set *cset);
    void (*cancel_fork)(struct task_struct *task, struct css_set *cset);
    void (*fork)(struct task_struct *task);
    void (*exit)(struct task_struct *task);
    void (*release)(struct task_struct *task);
    void (*bind)(struct cgroup_subsys_state *root_css);

    bool early_init:1;
    bool implicit_on_dfl:1;
    bool threaded:1;
    bool broken_hierarchy:1;
    bool warned_broken_hierarchy:1;
    int id;
    const char *name;
    const char *legacy_name;
    struct cgroup_root *root;
    struct idr css_idr; /* idr for css->id */
    struct list_head cfts;
    struct cftype *dfl_cftypes;    /* for the default hierarchy */
    struct cftype *legacy_cftypes;    /* for the legacy hierarchies */
    unsigned int depends_on;
};

css_alloc: 创建 cgroup_subsys_state 结构体,和 .css_free 相对。
css_online: 对此子系统调用 cgroup_init_subsys 之后调用。
can_attach: 在将task附着到cgroup之前进行检查,如果失败则停止附着过程。
cancel_attach: attach失败时调用。
attach: 将task附着到cgroup上。
can_fork: cgroup_init_subsys()中 can_fork、fork、exit、release 若有提供这四个回调函数则全局 have_xx_callback 变量对应的 ss->id 位就设为1。
fork: 将一个task写入cgroup。
exit: 将一个task移出cgroup。
bind: cgroup_init 中会调用,传参为 init_css_set.subsys[ssid]。
early_init: cgroup_init_early()中若为true就会提前调用cgroup_init_subsys(ss, true)对其初始化。
implicit_on_dfl: 如果%true,默认层次结构上的控制器不会显示在“cgroup.controllers”或“cgroup.subtree_control”中,默认层次结构上的所有 cgroup 上都会隐式启用,并绕过“无内部进程”约束. 这适用于对用户空间透明的实用程序类
型控制器。隐式控制器可以随时从默认层次结构中被盗,因此必须可以与来自先前层次结构的离线 csses 与当前 csses 共存。
threaded: 如果为 %true,则控制器在默认层次结构上支持线程模式。在线程子树中,进程粒度和无内部进程约束都被忽略,线程控制器应该能够处理它。请注意,由于默认层次结构上的所有 cgroup 上都会自动启用隐式控制器,因此它也应该是线程化的。不支持隐式 && !threaded。
broken_hierarchy: 如果为 %false,则该子系统具有适当的层次结构 - 对父 cgroup 的配置、资源统计和限制涵盖其子 cgroup 的那些。 如果 %true,层次结构支持在某些方面被破坏 - 一些子系统完全忽略层次结构,而其他子系统只实现了一半。
如果子系统损坏并且 cgroup 核心将在这种情况下发出警告消息,现在不允许创建嵌套 cgroup。 最终,所有子系统都将正确分层,这将消失。
id: 在开机时初始化。cgroup_init_early: 赋值为其在 cgroup_subsys_id 中的 id,例如cpu的就是cpu_cgrp_id
name: 在开机时初始化。cgroup_init_early: 赋值为其在cgroup_subsys_name[]中的name,例如cpu的就是"cpu"。
legacy_name: 可选,如果未设置则在启动期间自动初始化。cgroup_init_early: 若是没有初始化就是cpu_cgrp_subsys[]中的name, 例如cpu的cpu_cgrp_subsys没有初始化就是"cpu"。
root: 链接到父级,受 cgroup_lock() 保护。cgroup_init_subsys: 标记early_init=true的子系统指向 cgrp_dfl_root。
cfts: cftype 列表。 每个条目都是以零长度名称终止的数组的第一个条目。
dfl_cftypes: 自动注册的基本 cftype。 两者可以指向同一个数组。
legacy_cftypes: cgroup子系统的节点,这些节点都是在 xxx_cgrp_subsys 中定义的。
depends_on: 一个子系统可能依赖于其他子系统。 当在 cgroup 上启用此类子系统时,如果可用,则依赖的子系统将一起启用。由于依赖关系而启用的子系统在显式启用之前对用户空间不可见。这里指定了这个依赖的子系统的掩码。


9. task_struct 中和cgroup相关的部分

struct task_struct {
    ...
#ifdef CONFIG_CGROUPS
    unsigned            no_cgroup_migration:1;
    unsigned            frozen:1;
    struct css_set __rcu        *cgroups;
    struct list_head        cg_list;
#endif
    ...
};

no_cgroup_migration: 禁止 userland 启动的 cgroup 迁移
frozen: 任务被冻结/停止(由 cgroup freezer 使用)
cgroups: 受 css_set_lock 保护的控制组信息。cgroup_init_early: 将 init_task.cgroups 初始化为 struct css_set init_css_set
cg_list: cg_list 受 css_set_lock 和 tsk->alloc_lock 保护。


三、全局变量

1. struct cgroup_root cgrp_dfl_root;

在 cgroup.c 中定义,并 EXPORT_SYMBOL_GPL(cgrp_dfl_root) 导出。其 .cgrp 是default cgroup。

2. static struct cftype cgroup_base_files[]

在 cgroup.c 中定义,指定 "cgroup.type"、"cgroup.procs" 等文件节点,在 cgroup_init() 中初始化。

3. struct cftype cgroup1_base_files[];

历史遗留的cgroup层次结构文件,在 cgroup.c 中定义,指定 "cgroup.procs"、"cgroup.clone_children"、"tasks" 等
文件节点,在 cgroup_init() 中初始化。

4. struct css_set init_css_set;

在 cgroup.c 中定义并初始化,在 cgroup_init_subsys() 中 init_css_set.subsys[ss->id] = css,指向各个子系统的
cgroup_subsys_state 结构。

5. 一些位掩码标记

have_fork_callback
have_exit_callback
have_release_callback
have_canfork_callback

位掩码,标记各个cgroup子系统是否实现 fork、exit、release、canfork 回调函数,各个子系统分别对应 ss->id 位。

 

posted on 2023-03-03 11:34  Hello-World3  阅读(628)  评论(0编辑  收藏  举报

导航