eas k5.4 (二):v5.4 - Util(ization) clamping

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

目录

简介

一. 数据结构

1. rq中uclamp的实现

2.  task中uclamp的实现

3. 扩展 CPU's cgroup controller

二. 关键函数

1. uclamp的初始化

2. fork task的uclamp

3. userspace设置task的uclamp- __setscheduler_uclamp()

4. uclamp_rq_inc_id() 

5.  uclamp_rq_dec_id()

6.  uclamp_rq_util_with()

7. 获取task有效的clamp值

8. 修改group的clamp并update到group中的task

9. sysctl接口

三. 参考资料



简介

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不能接受(\Delta10%),比如功耗增加显著?可以增加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(&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值