在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频率相关的目录:
这些文件节点的读写属性:
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正在运行的工作频率
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
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;
};
根据平台以及默认的最大频率来选择对应的频率列表。