[cpufreq governor] scale_freq_capacity/scale_cpu_capacity怎么计算的

本文深入解析Linux内核调度器中频率与CPU容量缩放机制,包括scale_freq_capacity和scale_cpu_capacity函数的工作原理,以及它们在cpufreq_set_policy和cpufreq_freq_transition_begin函数中的调用流程。探讨了CONFIG_CPU_FREQ配置下,如何根据当前频率调整CPU的性能等级。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义的地方

#ifdef CONFIG_CPU_FREQ  
#define arch_scale_freq_capacity cpufreq_scale_freq_capacity  
extern unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu);  
extern unsigned long cpufreq_scale_max_freq_capacity(int cpu);  
#endif  
#ifndef CONFIG_64BIT_ONLY_CPU  
#define arch_scale_cpu_capacity scale_cpu_capacity  
extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu);  
#endif  

默认配置:CONFIG_CPU_FREQ=y,CONFIG_64BIT_ONLY_CPU=n

  • scale_freq_capacity获取的地方:
static DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE;  
static DEFINE_PER_CPU(unsigned long, max_freq_scale) = SCHED_CAPACITY_SCALE;  
  
static void  
scale_freq_capacity(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs)  
{  
    unsigned long cur = freqs ? freqs->new : policy->cur;  
    unsigned long scale = (cur << SCHED_CAPACITY_SHIFT) / policy->max;  
    struct cpufreq_cpuinfo *cpuinfo = &policy->cpuinfo;  
    int cpu;  
  
    pr_debug("cpus %*pbl cur/cur max freq %lu/%u kHz freq scale %lu\n",  
         cpumask_pr_args(policy->cpus), cur, policy->max, scale);  
  
    for_each_cpu(cpu, policy->cpus)  
        per_cpu(freq_scale, cpu) = scale;  
  
    if (freqs)  
        return;  
  
    scale = (policy->max << SCHED_CAPACITY_SHIFT) / cpuinfo->max_freq;  
  
    pr_debug("cpus %*pbl cur max/max freq %u/%u kHz max freq scale %lu\n",  
         cpumask_pr_args(policy->cpus), policy->max, cpuinfo->max_freq,  
         scale);  
  
    for_each_cpu(cpu, policy->cpus)  
        per_cpu(max_freq_scale, cpu) = scale;  
}  
  
unsigned long cpufreq_scale_freq_capacity(struct sched_domain *sd, int cpu)  
{  
    return per_cpu(freq_scale, cpu);  
}  
 
unsigned long cpufreq_scale_max_freq_capacity(int cpu)  
{  
    return per_cpu(max_freq_scale, cpu);  
}  

怎么知道scale_freq_capacity会被调用呢?可以看到存在两个地方存在调用:
cpufreq_set_policy
cpufreq_freq_transition_begin
这两个函数必定会被调用到,第二个函数会被频繁的调用,因为每次freq的变化都会调用它。

而且我也通过log验证了确实被调用了,scale_freq_capacity也符合curr_freq * 1024/max_freq计算结果

  • scale_cpu_capacity获取的地方:
static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;  
  
unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu)  
{  
#ifdef CONFIG_CPU_FREQ  
    unsigned long max_freq_scale = cpufreq_scale_max_freq_capacity(cpu);  
  
    return per_cpu(cpu_scale, cpu) * max_freq_scale >> SCHED_CAPACITY_SHIFT;  
#else  
    return per_cpu(cpu_scale, cpu);  
#endif  
}  
  
static void set_capacity_scale(unsigned int cpu, unsigned long capacity)  
{  
    per_cpu(cpu_scale, cpu) = capacity;  
}  
  
static void update_cpu_capacity(unsigned int cpu)  
{  
    unsigned long capacity = SCHED_CAPACITY_SCALE;  
  
    if (cpu_core_energy(cpu)) {  
        int max_cap_idx = cpu_core_energy(cpu)->nr_cap_states - 1;  
        capacity = cpu_core_energy(cpu)->cap_states[max_cap_idx].cap;  
    }  
  
    set_capacity_scale(cpu, capacity);  
  
    pr_info("CPU%d: update cpu_capacity %lu\n",  
        cpu, arch_scale_cpu_capacity(NULL, cpu));  
}
 
static inline  
const struct sched_group_energy * const cpu_core_energy(int cpu)  
{  
    struct sched_group_energy *sge = sge_array[cpu][SD_LEVEL0];  
  
#ifndef CONFIG_DEFAULT_USE_ENERGY_AWARE  
    return NULL;  
#endif  
  
    if (!sge) {  
        pr_warn("Invalid sched_group_energy for CPU%d\n", cpu);  
        return NULL;  
    }  
  
    return sge;  
}  

也能够看到,都是通过per_cpu来设定和获取的,以后得好好梳理下per_cpu的来龙去脉,实在是太有用了。
由于定义了CONFIG_CPU_FREQ,所以需要先获取max_freq_scale的数值,从第一章,能够知道cpufreq_scale_max_freq(cpu)的数值为1024,在scale_freq_capacity中并没有成功修改scale_max_freq数值。所以scale_cpu_capacity就是每个cpu自身的capacity数值,最后的数值就是cpu_scale,这个数值是从dts里面获取的。我们之前的文章讲解了kernel/sched/energy.c文件获取cpu power、capacity,cpu idle power了,里面的这些数据全部存储在sge_array全局二维数组中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值