在kernel中实现了两个driver,一个负责控制开关CPU的核数,叫做hot-plug驱动,另一个负责调整CPU的频率,叫做DVFS驱动。kernel中的driver会根据系统的负载,自动调整使用几个CPU和调整CPU频率。如果负载高了,提高频率,或者多开几个核,或者开大核。如果负载降下去了,就可以关大核,关核,降频。
以下是两个知名手机厂商开源的config配置文档:
60 CONFIG_CPU_FREQ=y | 446 CONFIG_CPU_FREQ=y 61 CONFIG_CPU_FREQ_STAT_DETAILS=y | 447 CONFIG_CPU_FREQ_STAT=y 62 CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y | 448 CONFIG_CPU_FREQ_STAT_DETAILS=y 63 CONFIG_CPU_FREQ_GOV_PERFORMANCE=y | 449 # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set 64 CONFIG_CPU_FREQ_GOV_POWERSAVE=y | 450 # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set 65 CONFIG_CPU_FREQ_GOV_USERSPACE=y | 451 # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set 66 CONFIG_CPU_FREQ_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set 67 CONFIG_NET=y | 453 # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set 68 CONFIG_PACKET=y | 454 CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y 69 CONFIG_UNIX=y | 455 # CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set 70 CONFIG_XFRM_MIGRATE=y | 456 # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set 71 CONFIG_NET_KEY=y | 457 # CONFIG_CPU_FREQ_GOV_USERSPACE is not set 72 CONFIG_INET=y | 458 # CONFIG_CPU_FREQ_GOV_ONDEMAND is not set 73 CONFIG_IP_MULTICAST=y | 459 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y 74 CONFIG_IP_ADVANCED_ROUTER=y | 460 # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set 75 CONFIG_IP_MULTIPLE_TABLES=y | 461 # CONFIG_CPUFREQ_DT is not set在adb shell命令下,和cpu频率相关的目录:
/sys/devices/system/cpu/cpuX, X表示cpu number.
:/sys/devices/system/cpu/cpu0/cpufreq # ls
affected_cpus related_cpus scaling_governor
cpuinfo_cur_freq scaling_available_frequencies scaling_max_freq
cpuinfo_max_freq scaling_available_governors scaling_min_freq
cpuinfo_min_freq scaling_cur_freq scaling_setspeed
cpuinfo_transition_latency scaling_driver stats
这些文件节点的读写属性:
cpufreq_freq_attr_ro_perm(cpuinfo_cur_freq, 0400); 936 cpufreq_freq_attr_ro(cpuinfo_min_freq); 937 cpufreq_freq_attr_ro(cpuinfo_max_freq); 938 cpufreq_freq_attr_ro(cpuinfo_transition_latency); 939 cpufreq_freq_attr_ro(scaling_available_governors); 940 cpufreq_freq_attr_ro(scaling_driver); 941 cpufreq_freq_attr_ro(scaling_cur_freq); 942 cpufreq_freq_attr_ro(bios_limit); 943 cpufreq_freq_attr_ro(related_cpus); 944 cpufreq_freq_attr_ro(affected_cpus); 945 cpufreq_freq_attr_rw(scaling_min_freq); 946 cpufreq_freq_attr_rw(scaling_max_freq); 947 cpufreq_freq_attr_rw(scaling_governor); 948 cpufreq_freq_attr_rw(scaling_setspeed);cpuinfo_cur_freq: 当前cpu正在运行的工作频率
cpuinfo_max_freq:该文件指定了处理器能够运行的最高工作频率 (单位: 千赫兹)
cpuinfo_min_freq :该文件指定了处理器能够运行的最低工作频率 (单位: 千赫兹)
cpuinfo_transition_latency:该文件定义了处理器在两个不同频率之间切换时所需要的时间 (单位: 纳秒)
scaling_available_frequencies:所有支持的主频率列表 (单位: 千赫兹)
scaling_available_governors:该文件显示当前内核中支持的所有cpufreq governor类型
scaling_cur_freq:被governor和cpufreq核决定的当前CPU工作频率。该频率是内核认为该CPU当前运行的主频率
scaling_driver:该文件显示该CPU正在使用何种cpufreq driver
scaling_governor:通过echo命令,能够改变当前处理器的governor类型
scaling_max_freq:显示当前policy的上下限 (单位: 千赫兹)需要注意的是,当改变cpu policy时,需要首先设置scaling_max_freq, 然后才是scaling_min_freq
scaling_setspeed:如果用户选择了“userspace” governor, 那么可以设置cpu工作主频率到某一个指定值。只需要这个值在scaling_min_freq 和 scaling_max_freq之间即可。
工作模式:cat scaling_available_governors
:/sys/devices/system/cpu/cpu0/cpufreq # cat scaling_available_governors
ondemand userspace powersave interactive performance
CPU的频率调节模式:
1. Performance. 不考虑耗电,只用最高频率。
2. Interactive. 直接上最高频率,然后看CPU负荷慢慢降低。
3. Powersave. 通常以最低频率运行,流畅度会受影响,一般不会用这个吧!
4. Userspace. 可以在用户空间手动调节频率。
5. Ondemand. 定期检查负载,根据负载来调节频率。
对于这项飞思卡尔的实现:
默认使用了performance,不过freescale在boot完成后改成了interactive.
device/fsl/tek_mx6/init.rc:
on property:sys.boot_completed=1
# Set default CPU frequency governor
# Set timer 40ms, min sample 60ms,hispeed at cpufreq MAX freq in freq_table at load 40%
write /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor interactive
最终可通过scaling_governor文件查看。
工作频率:
当前支持的cpu调节模式可通过 scaling_available_frequencies 查看。
:/sys/devices/system/cpu/cpu0/cpufreq # cat scaling_available_frequencies
1092000 988000 858000 793000 637000 494000 364000 221000
kernel-4.4$ vi ./include/linux/cpufreq.h +59
struct cpufreq_policy { /* CPUs sharing clock, require sw coordination */ cpumask_var_t cpus; /* Online CPUs only */ cpumask_var_t related_cpus; /* Online + Offline CPUs */ cpumask_var_t real_cpus; /* Related and present */ unsigned int shared_type; /* ACPI: ANY or ALL affected CPUs should set cpufreq */ unsigned int cpu; /* cpu managing this policy, must be online */ struct clk *clk; struct cpufreq_cpuinfo cpuinfo;/* see above */ unsigned int min; /* in kHz */ unsigned int max; /* in kHz */ unsigned int cur; /* in kHz, only needed if cpufreq * governors are used */ unsigned int restore_freq; /* = policy->cur before transition */ unsigned int suspend_freq; /* freq to set during suspend */ unsigned int policy; /* see above */ unsigned int last_policy; /* policy before unplug */ struct cpufreq_governor *governor; /* see below */ void *governor_data; bool governor_enabled; /* governor start/stop flag */ char last_governor[CPUFREQ_NAME_LEN]; /* last governor used */ struct work_struct update; /* if update_policy() needs to be * called, but you're in IRQ context */ struct cpufreq_user_policy user_policy; struct cpufreq_frequency_table *freq_table; struct list_head policy_list; struct kobject kobj; struct completion kobj_unregister; /* * The rules for this semaphore: * - Any routine that wants to read from the policy structure will * do a down_read on this semaphore. * - Any routine that will write to the policy structure and/or may take away * the policy altogether (eg. CPU hotplug), will hold this lock in write * mode before doing so. * * Additional rules: * - Lock should not be held across * __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT); */ struct rw_semaphore rwsem; /* * Fast switch flags: * - fast_switch_possible should be set by the driver if it can * guarantee that frequency can be changed on any CPU sharing the * policy and that the change will affect all of the policy CPUs then. * - fast_switch_enabled is to be set by governors that support fast * freqnency switching with the help of cpufreq_enable_fast_switch(). */ bool fast_switch_possible; bool fast_switch_enabled; /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ unsigned int cached_target_freq; int cached_resolved_idx; /* Synchronization for frequency transitions */ bool transition_ongoing; /* Tracks transition status */ spinlock_t transition_lock; wait_queue_head_t transition_wait; struct task_struct *transition_task; /* Task which is doing the transition */ /* cpufreq-stats */ struct cpufreq_stats *stats; /* For cpufreq driver's internal use */ void *driver_data; };根据平台以及默认的最大频率来选择对应的频率列表。
所以降频有两种方法:
1. 直接编译静态修改频率列表。
2. 通过scaling_max_freq文件动态写入。
/ # cat /proc/cpufreq/cpufreq_ptpod_freq_volt
cat /proc/cpufreq/cpufreq_ptpod_freq_volt
[0] = { .cpufreq_khz = 1092000, .cpufreq_volt = 119375, .cpufreq_volt_org = 125000, },
[1] = { .cpufreq_khz = 988000, .cpufreq_volt = 114375, .cpufreq_volt_org = 121875, },
[2] = { .cpufreq_khz = 858000, .cpufreq_volt = 108125, .cpufreq_volt_org = 118750, },
[3] = { .cpufreq_khz = 793000, .cpufreq_volt = 105000, .cpufreq_volt_org = 115000, },
[4] = { .cpufreq_khz = 637000, .cpufreq_volt = 101250, .cpufreq_volt_org = 110000, },
[5] = { .cpufreq_khz = 494000, .cpufreq_volt = 101250, .cpufreq_volt_org = 105000, },
[6] = { .cpufreq_khz = 364000, .cpufreq_volt = 101250, .cpufreq_volt_org = 100000, },
[7] = { .cpufreq_khz = 221000, .cpufreq_volt = 101250, .cpufreq_volt_org = 95000, },
/ # cat proc/cpufreq/cpufreq_oppidx
cat proc/cpufreq/cpufreq_oppidx
[MT_CPU_DVFS_LITTLE/0]
cpufreq_oppidx = 1
OP(1092000, 119375),
OP(988000, 114375),
OP(858000, 108125),
OP(793000, 105000),
OP(637000, 101250),
OP(494000, 101250),
OP(364000, 101250),
OP(221000, 101250),