您的位置:首页 > 运维架构 > Linux

CPU frequency and voltage scaling code in the Linux(TM) kernel

2012-10-23 09:02 531 查看
1. 基本介绍

1) 调整CPU运行频率是一个节能的好方法,CPU运行频率越低,CPU功耗越小。

2) 下面的我现在正在使用的CPU为例进行说明。触发CPU频率调整的有两个源:

1)根据CPU负荷进行调整(代码位于:kernel/drivers/cpufreq,下面以cpufreq_interactive.c为例,当/sys/drivers/system/cpu/cpu0/cpufreq/scaling_governor中的值为interactive时生效)

2)根据CPU的温度时行调整(需要CPU内有温度传感器,且在cpufreq_driver中实现,它由各个芯片厂家实现)

2. 根据CPU负荷进行调整的调用流程

cpufreq_interactive_up_task(或cpufreq_interactive_freq_down

或cpufreq_governor_interactive :cpufreq_interactive.c)->

__cpufreq_driver_target->

cpufreq_driver->target(mycpu_cpufreq_driver.target=mycpu_target)->

mycpu_target->

clk_set_rate->

dvfs_set_rate->

dvfs_target_cpu (或dvfs_target_core)->

dvfs_clk_get_ref_volt(获得与频率对应的参考电压)

dvfs_scale_volt_direct(设置电压)

clk_set_rate_locked->clk_set_rate_nolock->clk->set_rate(设置频率)

3. cpufreq_interactive工作流程

1) 驱动入口:cpufreq_interactive_init

a) 注册CPU timer函数:cpufreq_interactive_timer

b) 创建线程:kinteractiveup,入口函数:cpufreq_interactive_up_task

c) 创建workqueue “knteractive_down”,其工作处理函数为:cpufreq_interactive_freq_down

d) 注册idle notify函数cpufreq_interactive_idle_nb

e) 注册cpufreq_governor cpufreq_gov_interactive,其中的governor函数为:cpufreq_governor_interactive

2) cpufreq_interactive_timer

这个函数通过timer触发,它执行cpu负荷计算。timer的默认间隔为20ms。根据CPU负荷计算CPU的频率的代码如下:

/*
* Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time,
* this lets idle exit know the current idle time sample has
* been processed, and idle exit can generate a new sample and
* re-arm the timer.  This prevents a concurrent idle
* exit on that CPU from writing a new set of info at the same time
* the timer function runs (the timer function can't use that info
* until more time passes).
*/
time_in_idle = pcpu->time_in_idle;
idle_exit_time = pcpu->idle_exit_time;
now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time);
smp_wmb();

/* If we raced with cancelling a timer, skip. */
if (!idle_exit_time)
goto exit;

delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle);
delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time,
idle_exit_time);

/*
* If timer ran less than 1ms after short-term sample started, retry.
*/
if (delta_time < 1000)
goto rearm;

if (delta_idle > delta_time)
cpu_load = 0;
else
cpu_load = 100 * (delta_time - delta_idle) / delta_time;

delta_idle = (unsigned int) cputime64_sub(now_idle,
pcpu->freq_change_time_in_idle);
delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time,
pcpu->freq_change_time);

if ((delta_time == 0) || (delta_idle > delta_time))
load_since_change = 0;
else
load_since_change =
100 * (delta_time - delta_idle) / delta_time;

/*
* Choose greater of short-term load (since last idle timer
* started or timer function re-armed itself) or long-term load
* (since last frequency change).
*/
if (load_since_change > cpu_load)
cpu_load = load_since_change;

if (cpu_load >= go_hispeed_load) {
if (pcpu->policy->cur == pcpu->policy->min)
new_freq = hispeed_freq;
else
new_freq = pcpu->policy->max * cpu_load / 100;
} else {
new_freq = pcpu->policy->cur * cpu_load / 100;
}

if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
new_freq, CPUFREQ_RELATION_H,
&index)) {
pr_warn_once("timer %d: cpufreq_frequency_table_target error\n",
(int) data);
goto rearm;
}

new_freq = pcpu->freq_table[index].frequency;

a) 如果新的频率大于当前工作频率,则唤醒进程kinteractiveup进行处理,此进程调用__cpufreq_driver_target设置新的工作频率。

b) 如果新的频率小于当前工作频率,则把一个工作放于workqueue “knteractive_down”,它调用__cpufreq_driver_target设置新的工作频率。

4. 根据温度调节CPU频率

把温度检测和频率调整工作放于一个work中, 然后加入一个workqueue,delay 2ms再执行。其调用流程如下:

温度检测及频率计算函数->

cpufreq_driver_target->

__cpufreq_driver_target->

...与第2.中的一样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: