目录
3. userspace设置task的uclamp- __setscheduler_uclamp()
8. 修改group的clamp并update到group中的task
简介
uclamp是在kernel5.3引入的新feature,又在kernel5.4引入了cgroup的的支持。通过设置task的util值,可以boost task util,达到和android schedtune相似的效果。

一. 数据结构
1. rq中uclamp的实现
图1-1 rq中uclamp的实现
#ifdef CONFIG_UCLAMP_TASK
/* Utilization clamp values based on CPU's RUNNABLE tasks */
struct uclamp_rq uclamp[UCLAMP_CNT] ____cacheline_aligned;
unsigned int uclamp_flags;
#define UCLAMP_FLAG_IDLE 0x01
struct uclamp_rq {
unsigned int value;
struct uclamp_bucket bucket[UCLAMP_BUCKETS];
};
在rq中,通过嵌入两组uclamp,即一组最小值uclamp[UCLAMP_MIN]和一组最大值uclamp[UCLAMP_MAX]实现对cpu的util clamp。
每组中的value表示rq当前生效的clamp值,两组中默认各包含5个buckets,可以通过CONFIG_UCLAMP_BUCKETS_COUNT
配置其它buckets数(5~20个)。uclamp_flags在uclamp初始化init_uclamp()时置零,目前只有一个标志位UCLAMP_FLAG_IDLE ,
标识cpu上是否还有task运行。
每个bucket表示一定范围的util值,以系统默认的5个buckets为例,每个bucket的范围是cpu最大capacity的20%:
SCHED_CAPACITY_SCALE/UCLAMP_BUCKETS_COUNT,即1024/5。
rq上的task会根据task的值将其规划到对应的bucket中。比如taskA在cpu0上run,要求25%的util值,会被规划到bucket[1]中,
bucket[1]::value=25%,记录生效的util值,tasks计数加1,表示规划到当前bucket中task又多了一个。如果同时35% util值的taskB被
调度到cpu0上,则此时bucket[1]::value=35%,tasks计数加1。此时taskA受益于taskB 35%的util值,直到taskA退出rq。如果系统中
对taskA受益taskB更高的boot util不能接受(10%),比如功耗增加显著?可以增加bucket数量,这样就减少了每个bucket对应的
util范围,提高了bucket util的统计精度,代价是使用更多的memory分配buckets。当bucket中没有task时,value被设置成默认的
bucket范围的最小值,bucket[1]中没有task时,bucket[1]::value=%20。
2. task中uclamp的实现

图1-2 task中uclamp的实现
struct task_struct
{
#ifdef CONFIG_UCLAMP_TASK
/* Clamp values requested for a scheduling entity */
struct uclamp_se uclamp_req[UCLAMP_CNT]; (1)
/* Effective clamp values used for a scheduling entity */
struct uclamp_se uclamp[UCLAMP_CNT]; (2)
#endif
}
struct uclamp_se {
unsigned int value : bits_per(SCHED_CAPACITY_SCALE);
unsigned int bucket_id : bits_per(UCLAMP_BUCKETS);
unsigned int active : 1;
unsigned int user_defined : 1;
};
value:最大值是1024,11bit,表示调度实体的clamp值
bucket_id:默认每个uclamp_id各5个bucket,使用3bit表示,表示clamp值对应的bucket id
active:task被规划到rq的一个bucket中,此bucket::tasks值为此task计数加1,bucket::value值也作用于此task
user_defined:标识是usersapce请求的clamp值 。可以更改system给task默认分配的boost.
uclamp以调度实体的方式嵌入task_struct中,包含两种uclamp_se:
(1) 记录请求的clamp值的request se
(2) 记录生效的clamp值的avtive se
3. 扩展 CPU's cgroup controller
cpu cgroup支持uclamp,通过在cftype cpu_files中添加新系统节点uclamp.{min,max},可以设置group中所有task的boost util值和最大util值,需要配置CONFIG_UCLAMP_TASK_GROUP。
kernel5.4/kernel/sched/sched.h
struct task_group {
#ifdef CONFIG_UCLAMP_TASK_GROUP
/* The two decimal precision [%] value requested from user-space */
unsigned int uclamp_pct[UCLAMP_CNT];
/* Clamp values requested for a task group */
struct uclamp_se uclamp_req[UCLAMP_CNT];
/* Effective clamp values used for a task group */
struct uclamp_se uclamp[UCLAMP_CNT];
/* Latency-sensitive flag used for a task group */
unsigned int latency_sensitive;
#endif
}
kernel5.4/kernel/sched/core.c
static struct cftype cpu_files[] = {
#ifdef CONFIG_UCLAMP_TASK_GROUP
{
.name = "uclamp.min",
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = cpu_uclamp_min_show,
.write = cpu_uclamp_min_write,
},
{
.name = "uclamp.max",
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = cpu_uclamp_max_show,
.write = cpu_uclamp_max_write,
},
{
.name = "uclamp.latency_sensitive",
.flags = CFTYPE_NOT_ON_ROOT,
.read_u64 = cpu_uclamp_ls_read_u64,
.write_u64 = cpu_uclamp_ls_write_u64,
},
#endif
}
通过android的配置,示例如下,通过task group实现对task util的钳制。root_task_group不支cpu.uclamp.min{max}的配置,参见”7274a5c sched/uclamp: Propagate system defaults to the root group“
/dev/cpuctl/top-app/cpu.uclamp.min 30.00
/dev/cpuctl/top-app/cpu.uclamp.max 100.00
/dev/cpuctl/top-app/cpu.uclamp.latency_sensitive 0
二. 关键函数
1. uclamp的初始化
static void __init init_uclamp(void)
{
struct uclamp_se uc_max = {};
enum uclamp_id clamp_id;
int cpu;
mutex_init(&uclamp_mutex);
for_each_possible_cpu(cpu)
init_uclamp_rq(cpu_rq(cpu)); (1)
for_each_clamp_id(clamp_id) {
uclamp_se_set(&init_task.uclamp_req[clamp_id], (2)
uclamp_none(clamp_id), false);
}
/* System defaults allow max clamp values for both indexes */
uclamp_se_set(&uc_max, uclamp_none(UCLAMP_MAX), false); (3)
for_each_clamp_id(clamp_id) {
uclamp_default[clamp_id] = uc_max; (4)
#ifdef CONFIG_UCLAMP_TASK_GROUP (5)
root_task_group.uclamp_req[clamp_id] = uc_max;
root_task_group.uclamp[clamp_id] = uc_max;
#endif
}
}
uclamp的初始化函数,在调度器初始化函数sched_init()最后被调用。
(1) 初始化每个cpu所属rq的uclamp,设置为默认值rq->uclamp[0,1].value={0,1024}。
(2) 初始化init_task的request se,UCLAMP_MIN初始化value=0,UCLAMP_MAX初始化value=1024,并计算对应的bucket id。
(3) 定义了一个uc_max se,并初始化value=1024,计算对应的bucket, uc_max表示clamp的最大值。
(4) 定义了uclamp_default se,并初始化UCLAMP_MIN和UCLAMP_MAX value都为uc_max。uclamp_defaut是所有clamp se的上限,任何clamp se都要小于等于uclamp_default的值。
(5) 初始化root_task_group的request se和active se为uc_max
2. fork task的uclamp
static void uclamp_fork(struct task_struct *p)
{
enum uclamp_id clamp_id;
for_each_clamp_id(clamp_id)
p->uclamp[clamp_id].active = false; (1)
if (likely(!p->sched_reset_on_fork)) (2)
return;
for_each_clamp_id(clamp_id) {
unsigned int clamp_value = uclamp_none(clamp_id);
/* By default, RT tasks always get 100% boost */
if (unlikely(rt_task(p) && clamp_id == UCLAMP_MIN)) (3)
clamp_value = uclamp_none(UCLAMP_MAX);
uclamp_se_set(&

本文深入解析uclamp特性,介绍其在Linux内核5.3版本引入,并在5.4版本中增强,通过设置task的util值,提升taskutil,达到类似Android SchedTune的效果。文章详细讲解了uclamp的数据结构、关键函数以及与cgroup的整合,同时提供丰富的参考资料。
最低0.47元/天 解锁文章
1万+

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



