第一篇中复现程序触发问题逻辑以及 patch-1 的修复逻辑
1 创建任务组
首先按照脚本逻辑创建任务组,接着将睡眠任务设置到对应任务组,最后设置睡眠任务 cpuset时有以下流程逻辑:
首先是:创建任务组,当前创建的是cfs 任务组时将会有如下调用:
alloc_fair_sched_group -------------------------------(1)
-> init_tg_cfs_entry -----------------------------(2)
-> init_entity_runnable_average ------------------(3)
(1)alloc_fair_sched_group
int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent)
{
struct sched_entity *se;
struct cfs_rq *cfs_rq;
int i;
tg->cfs_rq = kcalloc(nr_cpu_ids, sizeof(cfs_rq), GFP_KERNEL); /* 1 */
if (!tg->cfs_rq)
goto err;
tg->se = kcalloc(nr_cpu_ids, sizeof(se), GFP_KERNEL); /* 2 */
if (!tg->se)
goto err;
tg->shares = NICE_0_LOAD; /* 3 */
init_cfs_bandwidth(tg_cfs_bandwidth(tg));
for_each_possible_cpu(i) {
/* 4 */
cfs_rq = kzalloc_node(sizeof(struct cfs_rq),
GFP_KERNEL, cpu_to_node(i));
if (!cfs_rq)
goto err;
se = kzalloc_node(sizeof(struct sched_entity),
GFP_KERNEL, cpu_to_node(i));
if (!se)
goto err_free_rq;
init_cfs_rq(cfs_rq);
init_tg_cfs_entry(tg, cfs_rq, se, i, parent->se[i]);
init_entity_runnable_average(se);
}
return 1;
err_free_rq:
kfree(cfs_rq);
err:
return 0;
}
1)2)为一个新的任务的指针数组分配空间,后续每一个 cpu 的 cfs_rq/se 地址都存在这个里面。
3)一个新的任务组对应的 shares 权重默认为 1024。
4)对每一个cpu分配 cfs_rq 和 se,并初始化它们。
(2)init_tg_cfs_entry
void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq,
struct sched_entity *se, int cpu,
struct sched_entity *parent)
{
struct rq *rq = cpu_rq(cpu);
cfs_rq->tg = tg; // 设置 cfs_rq 对应的任务组
cfs_rq->rq = rq; // 对应的 rq
init_cfs_rq_runtime(cfs_rq);
tg->cfs_rq[cpu] = cfs_rq; // 设置任务组的 cfs_rq[cpu]
tg->se[cpu] = se; // 设置任务组的 se[cpu]
/* se could be NULL for root_task_group */
if (!se)
return;
if (!parent) {
se->cfs_rq = &rq->cfs; // 对于根 root_task_group 普通任务的 cfs_rq 就是就绪队列的
se->depth = 0; // cfs,并且深度为零。
} else {
se->cfs_rq = parent->my_q; // 这里可以看到任务组的se->cfs_rq指向自己所属的任务组cfs_rq[cpu]
se->depth = parent->depth + 1; // 任务组 se[cpu] 的深度
}
se->my_q = cfs_rq; // 参与挂入红黑树对应的 cfs_rq。
/* guarantee group entities always have weight */
update_load_set(&se->load, NICE_0_LOAD); // 设置 se 权重默认为 NICE_0_LOAD,对应任务组将会在后续 update_cfs_group 时根据任务组 shares 和 load_avg 更新权重。
se->parent = parent;
}
(3)init_entity_runnable_average 为 se 设置新的任务负载,具体逻辑可以之前对 init_entity_runnable_average 函数的描述及分析。
至此当创建一个新的任务组时对应的 cfs_rq/se 的初始化完成。接着开始将任务附加到任务组中。
2 设置一个任务到任务组中
在脚本中有如下示例:
任务组所属层次:
cgroup
-> zy_test
-> sub-1
-> test_sub
我们将任务通过写 cgoup.procs 文件设置到 test_sub 任务组中:触发如下逻辑:
cgroup_attach_task
-> cgroup_migrate_prepare_dst
-> cgroup_migrate
-> cgroup_migrate_execute
-> cpu_cgroup_attach
-> sched_move_task ------------------------- (1)
-> task_change_group_fair
-> task_move_group_fair ------------- (2)
-> cgroup_migrate_finish
(1)当设置 cgroup.procs 时将会调用 sched_move_task:
void sched_move_task(struct task_struct *tsk)
{
int queued, running, queue_flags =
DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK;
struct rq_flags rf;
struct rq *rq;
rq = task_rq_lock(tsk, &rf);
update_rq_clock(rq);
running = task_current(rq, tsk); ------------------(1)
queued = task_on_rq_queued(tsk); ------------------(2)
if (queued)
dequeue_task(rq, tsk, queue_flags);
if (running)
put_prev_task(rq, tsk);
sched_change_group(tsk, TASK_MOVE_GROUP); ----------(3)
----------------------------------------------------(4)
if (queued)
enqueue_task(rq, tsk, queue_flags);
if (running)
set_curr_task(rq, tsk);
task_rq_unlock

本文围绕Linux CFS Group展开,阐述复现程序触发问题的逻辑。先介绍创建任务组、设置任务到任务组、设置睡眠任务cpuset的流程,指出设置cpuset时睡眠任务负载不能正确衰减的问题,影响任务组se权重计算。最后说明patch修复逻辑,使任务组load_avg正确衰减。
最低0.47元/天 解锁文章
787

被折叠的 条评论
为什么被折叠?



