轉自:https://www.cnblogs.com/lvzh/p/13169941.html
linux cpufreq
cpufreq概述
cpufreq的核心功能,是通過調整CPU的電壓和頻率,兼顧系統的性能和功耗。在不需要高性能時,降低電壓和頻率,以降低功耗;在需要高性能時,提高電壓和頻率,以提高性能。
cpufreq軟件框架
對下,cpufreq基於clock、regulator、pmu等模塊實現頻率和電壓的控制。
對上,cpufreq通過cpufreq core、cpufreq governor、cpufreq stats等模塊以sysfs的形式向用戶空間提供頻率的查詢、控制等接口。
內部,cpufreq內部分為core、governor、drivers等模塊。
cpufreq調頻策略
- Performance
性能優先,CPU固定工作在其支持的最高頻率。
- Powersave
功耗優先,CPU固定工作在其支持的最低頻率。
- Userspace
系統將變頻策略的決策權交給用戶態應用程序,並提供了相應的接口供用戶態程序設置CPU 頻率。
- Ondemand
按需動態調整CPU頻率, 只要CPU負載超過閾值up_threshold就會立即設置為最大頻率,其他時候根據負載計算出合適的頻率。
- Conservative
與ondemand不同,Conservative不是一味追求最高頻率,而是平滑地調整CPU頻率,頻率的升降是漸變式的。
cpufreq調測命令
- 查詢
以下文件節點均可通過cat命令顯示
# ls /sys/devices/system/cpu/cpu0/cpufreq/
affected_cpus //當前策略作用於哪些online core
cpuinfo_cur_freq //當前CPU硬件頻率
cpuinfo_max_freq //CPU硬件支持的最低頻率
cpuinfo_min_freq //CPU硬件支持的最高頻率
cpuinfo_transition_latency //硬件支持的切換頻率最小間隔
related_cpus //online和offline core
scaling_available_frequencies //軟件支持的頻率列表
scaling_available_governors //支持的策略列表
scaling_cur_freq //軟件設置的當前頻率,通常與cpuinfo_cpus相同,如果出現硬件問題可能導致不一致
scaling_driver //當前使用的driver
scaling_governor //當前使用的governor
scaling_max_freq //軟件governor設置的最高頻率
scaling_min_freq //軟件governor設置的最低頻率
scaling_setspeed //需將governor類型切換為userspace,才會出現,通過echo修改數值,會切換主頻
- 設置
可以通過 echo配置scaling_governor,scaling_max_freq,scaling_min_freq
例如:echo 1400 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
cpufreq編譯配置
#CPU Frequency scaling
CONFIG_CPU_FREQ=y #主開關
#CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y #default gov任選某個宏打開即可,決定了cpufreq初始化使用的governor,后續可在init.rc修改文件結點
#CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
#CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_STAT=y #維測開關,查看cpufreq統計信息:/sys/devices/system/cpu/cpu0/cpufreq/stats
performance/powersave策略
performance/powersave策略都是靜態頻率,performance設置為最高頻,powersave設置為最低頻。在切換governor的時候配置好頻率:
cpufreq_set_policy->cpufreq_governor_limits
Userspace策略
用戶寫文件節點/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed時,調用store_scaling_setspeed函數修改頻率。
Interactive策略
重要概念
hispeed_freq:當CPU頻率較低且負載突然超過go_hispeed_load時,CPU跳到此頻率,如果在
go_hispeed_load:hispeed_freq對應的負載
min_sample_time:在降頻前需要在當前頻率運行保持的時間
sampling_rate:interative管理器的采樣間隔
target_loads:為每個CPU頻率設置理想的CPU負載,以負載+頻率的數組形式存儲,如75:800:80:900:85:1300: 90:1500:95,含義是負載75以下時頻率為800MHz,75~80時,頻率為900MHz。
above_hispeed_delay:頻率升高時的需要保持的時間,以頻率+時間的數組形式存儲
調頻基本流程
設置sched的回調函數,每次發生調度變化時設置一個irq_work任務,在irq_work中重新計算目標頻率
gov_set_update_util->cpufreq_add_update_util_hook->cpufreq_update_util->update_util_handler->irq_work_queue->eval_target_freq->update_load==choose_freq
update_load:以CPU idle運行時間,計算移動平均頻率
choose_freq:使用平均負載頻率,預估合適的目標頻率
static u64 update_load(struct interactive_cpu *icpu, int cpu) { struct interactive_tunables *tunables = icpu->ipolicy->tunables; u64 now_idle, now, active_time, delta_idle, delta_time; now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy); /* 系統啟動之后CPU處於idle的總時間 */ delta_idle = (now_idle - icpu->time_in_idle); /* 本次與上次進入update_load之間,CPU處於idle的總時間 */ delta_time = (now - icpu->time_in_idle_timestamp); /* 本次與上次進入update_load時間只差 */ if (delta_time <= delta_idle) active_time = 0; else active_time = delta_time - delta_idle; icpu->cputime_speedadj += active_time * icpu->ipolicy->policy->cur; /* 移動平均值,代表CPU實際需要的頻率值 */ icpu->time_in_idle = now_idle; icpu->time_in_idle_timestamp = now; return now; }
/* Re-evaluate load to see if a frequency change is required or not */ static void eval_target_freq(struct interactive_cpu *icpu) { ... spin_lock_irqsave(&icpu->load_lock, flags); now = update_load(icpu, smp_processor_id()); delta_time = (unsigned int)(now - icpu->cputime_speedadj_timestamp); cputime_speedadj = icpu->cputime_speedadj; spin_unlock_irqrestore(&icpu->load_lock, flags); spin_lock_irqsave(&icpu->target_freq_lock, flags); do_div(cputime_speedadj, delta_time); /* loadadjfreq = (cputime_speedadj + active_time * policy->cur) / delta_time *100 ≈ cur_load * cur_freq;表示在周期內CPU需要的平均負載頻率 */ loadadjfreq = (unsigned int)cputime_speedadj * 100; /* cpu_load = (cputime_speedadj / policy->cur + active_time ) / * delta_time) *100 ≈ active_time/delta_time*100≈cur_load;表示CPU平均負載*/ cpu_load = loadadjfreq / policy->cur; .... /* choose_freq中使用loadadjfreq、target_loads的負載和頻率,計算預期的頻率 */ choose_freq(icpu, loadadjfreq); ... }
/* * If increasing frequencies never map to a lower target load then * choose_freq() will find the minimum frequency that does not exceed its * target load given the current load. */ static unsigned int choose_freq(struct interactive_cpu *icpu, unsigned int loadadjfreq) { struct cpufreq_policy *policy = icpu->ipolicy->policy; struct cpufreq_frequency_table *freq_table = policy->freq_table; unsigned int prevfreq, freqmin = 0, freqmax = UINT_MAX, tl; unsigned int freq = policy->cur; int index; do { prevfreq = freq; tl = freq_to_targetload(icpu->ipolicy->tunables, freq); /* 根據目標freq返回目標負載 */ /* * Find the lowest frequency where the computed load is less * than or equal to the target load. * target_frqe = loadadjfreq / tl = cur_freq * cur_load / tl; /* 根據這個公式逐漸收縮,多次調整找到最佳tl和目標freq */ */ index = cpufreq_frequency_table_target(policy, loadadjfreq / tl, CPUFREQ_RELATION_L); freq = freq_table[index].frequency; if (freq > prevfreq) { /* The previous frequency is too low */ ... } else if (freq < prevfreq) { /* The previous frequency is high enough. */ ... } /* If same frequency chosen as previous then done. */ } while (freq != prevfreq); return freq; }
Schedutil策略
基本思想
cpufreq_add_update_util_hook注冊回調函數,當負載變化時通知調頻
負載變化時調用這個函數
以下都會調用update_load_avg。
enqueue_entity
dequeue_entity
set_next_entity
put_prev_entity
entity_tick
enqueue_task_fair
dequeue_task_fair
update_blocked_averages
propagate_entity_cfs_rq
detach_entity_cfs_rq
attach_entity_cfs_rq
init_tg_cfs_entry
sched_group_set_shares
schedutil介紹
https://blog.csdn.net/wukongmingjing/article/details/81784727
根據下面文檔做一下cpufreq的總結
Documentation/admin-guide/pm/cpufreq.rst
load和util的區別