linux cpufreq framework(1)_概述
作者:wowo 发布于:2015-6-13 22:20 分类:电源管理子系统
1. 前言
linux kernel主要通过三类机制实现SMP系统CPU core的电源管理功能:
1)cpu hotplug。根据应用场景,enable/disable CPU core,具体可参考“Linux CPU core的电源管理(4)_cpu control”。
2) cpuidle framework。在没有进程调度的时候,让CPU core进入idle状态,具体可参考“cpuidle framework系列文章”。
3) cpufreq framework。根据使用场景和系统负荷,调整CPU core的电压(voltage)和频率(frequency),具体可参考本文以及后续cpufreq相关的。
对CPU core来说,功耗和性能是一对不可调和的矛盾,通过调整CPU的电压和频率,可以在功耗和性能之间找一个平衡点。由于调整是在系统运行的过程中,因此cpufreq framework的功能也称作动态电压/频率调整(Dynamic Voltage/Frequency Scaling,DVFS)。
本文主要从功能说明和软件架构两个角度介绍cpufreq framework。
2. 功能说明
cpufreq framework的核心功能,是通过调整CPU core的电压和频率,兼顾系统的性能和功耗。在不需要高性能时,降低电压和频率,以降低功耗;在需要高性能时,提高电压和频率,以提高性能。要达到此目的,有两个关键点:
1)如果控制CPU core的电压和频率。
2)何时改变CPU core的电压和频率。
针对这两个关键点,CPU core有两种实现。
实现1:CPU core根据自身的负荷,自动调整电压和频率,不需要OS级别的软件参与。
这种实现,软件复杂度非常低,通常情况下,只需要告诉CPU core电压和频率的调整范围(通过频率表示,scaling_min_freq和scaling_max_freq,也称作policy),CPU core即可自行调整。因此:
关键点1,由CPU core自行处理;
关键点2,OS需要根据大致的应用场景(例如,是高性能场景,还是低性能场景),设定一个频率范围,改变时机,由CPU core自行决定。
注1:由于软件参与度小,该实现的省电效率可能较低。
实现2:CPU core不参与任何的逻辑动作,由OS软件根据系统运行情况,调整电压和频率。
这种实现,几乎完全由软件掌控DVFS行为:
关键点1,基于clock framework和regulator framework提供的接口,控制CPU core的频率和电压;
关键点2,根据应用场景,手动(用户发起,例如省电模式)或者自动(软件自动调整,例如HMP)的调整。
注2:对关键点2来说,如果调整比较频繁,则需要CPU core在不同频率之间转换的速度足够快,后面会详细介绍。
为了实现上述功能需求,cpufreq framework抽象出cpufreq driver、cpufreq policy(策略)、cpufreq governor等多个软件实体,具体请参考下面的说明。
3. 软件架构
cpufreq framework的软件架构如下面图片所示:
对下,cpufreq framework基于cpu subsystem driver、OPP、clock framework、regulator framework等模块,提供对CPU core频率和电压的控制。这一部分主要由cpufreq driver实现。
对上,cpufreq framework会通过cpufreq core、cpufreq governors、cpufreq stats等模块,以sysfs的形式,向用户空间提供cpu frequency的查询、控制等接口。同时,在频率改变的时候,通过notifier通知关心的driver。
内部,cpufreq framework包括cpufreq core、cpufreq driver、cpufreq governors、cpufreq stats等模块,具体功能会在下一章详细分析。
注3:cpufreq driver中有,有一个特别的driver----arm big·little driver,用于实现ARM平台big·little的切换逻辑。虽然arm bit·little和cpufreq不是同一个概念,但它们的目的和逻辑非常类似,因此就放到这里了。后续会有专门的文章介绍该功能,因此分析cpufreq framework其它内容时,会直接把它忽略不表。
4. 软件模块的功能及API描述
4.1 cpufreq core
cpufreq core是cpufreq framework的核心模块,和kernel其它framework类似,它主要实现三类功能:
对上,以sysfs的形式向用户空间提供统一的接口,以notifier的形式向其它driver提供频率变化的通知;
对下,提供CPU core频率和电压控制的驱动框架,方便底层driver的开发;同时,提供governor框架,用于实现不同的频率调整机制;
内部,封装各种逻辑,实现所需功能。这些逻辑主要围绕struct cpufreq_driver、struct cpufreq_policy和struct cpufreq_governor三个数据结构进行,下面会详细分析。
1)struct cpufreq_driver
struct cpufreq_driver用于抽象cpufreq驱动,是平台驱动工程师关注最多的结构,其定义如下:
1: /* include/linux/cpufreq.h */
2: struct cpufreq_driver {
3: char name[CPUFREQ_NAME_LEN];
4: u8 flags;
5: void *driver_data;
6:
7: /* needed by all drivers */
8: int (*init) (struct cpufreq_policy *policy);
9: int (*verify) (struct cpufreq_policy *policy);
10:
11: /* define one out of two */
12: int (*setpolicy) (struct cpufreq_policy *policy);
13:
14: /*
15: * On failure, should always restore frequency to policy->restore_freq
16: * (i.e. old freq).
17: */
18: int (*target) (struct cpufreq_policy *policy, /* Deprecated */
19: unsigned int target_freq,
20: unsigned int relation);
21: int (*target_index) (struct cpufreq_policy *policy,
22: unsigned int index);
23: /*
24: * Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION
25: * unset.
26: *
27: * get_intermediate should return a stable intermediate frequency
28: * platform wants to switch to and target_intermediate() should set CPU
29: * to to that frequency, before jumping to the frequency corresponding
30: * to 'index'. Core will take care of sending notifications and driver
31: * doesn't have to handle them in target_intermediate() or
32: * target_index().
33: *
34: * Drivers can return '0' from get_intermediate() in case they don't
35: * wish to switch to intermediate frequency for some target frequency.
36: * In that case core will directly call ->target_index().
37: */
38: unsigned int (*get_intermediate)(struct cpufreq_policy *policy,
39: unsigned int index);
40: int (*target_intermediate)(struct cpufreq_policy *policy,
41: unsigned int index);
42:
43: /* should be defined, if possible */
44: unsigned int (*get) (unsigned int cpu);
45:
46: /* optional */
47: int (*bios_limit) (int cpu, unsigned int *limit);
48:
49: int (*exit) (struct cpufreq_policy *policy);
50: void (*stop_cpu) (struct cpufreq_policy *policy);
51: int (*suspend) (struct cpufreq_policy *policy);
52: int (*resume) (struct cpufreq_policy *policy);
53: struct freq_attr **attr;
54:
55: /* platform specific boost support code */
56: bool boost_supported;
57: bool boost_enabled;
58: int (*set_boost) (int state);
59: };
介绍该结构之前,我们先思考一个问题:由设备模型可知,driver是用来驱动设备的,那么struct cpufreq_driver所对应的设备是什么?也许从该结构中回调函数的参数可以猜到,是struct cpufreq_policy。但这相当难以理解,后面再分析。
name,该driver的名字,需要唯一,因为cpufreq framework允许同时注册多个driver,用户可以根据实际情况选择使用哪个driver。driver的标识,就是name。
flags,一些flag,具体会在后续的文章中介绍。
init,driver的入口,由cpufreq core在设备枚举的时候调用,driver需要根据硬件情况,填充policy的内容。
verify,验证policy中的内容是否符合硬件要求。它和init接口都是必须实现的接口。
setpolicy,对于第2章所讲的“实现一”,driver需要提供这个接口,用于设置CPU core动态频率调整的范围(即policy)。
target、target_index,对于第2章所讲的“实现二”,driver需要实现这两个接口中的一个(target为旧接口,不推荐使用),用于设置CPU core为指定频率(同时修改为对应的电压)。
后面的接口都是可选的,会在后续的章节中再分析。
有关struct cpufreq_driver的API包括:
1: int cpufreq_register_driver(struct cpufreq_driver *driver_data);
2: int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
3:
4: const char *cpufreq_get_current_driver(void);
5: void *cpufreq_get_driver_data(void);
分别为driver的注册、注销。获取当前所使用的driver名称,以及该driver的私有数据结构(driver_data字段)。
2)struct cpufreq_policy
struct cpufreq_policy是比较抽象的一个数据结构(蜗蜗觉得,是cpufreq framework中最难理解的地方),我们需要借助cpufreq core中的一些实现逻辑,去分析、理解它。
前面我们提到过一个问题,cpufreq driver对应的设备是什么?kernel是这样抽象cpufreq的:
抽象出一个CPU bus(对应的sysfs目录为/sys/devices/system/cpu/,具体可参考cpu subsystem driver相关的描述),所有的CPU device都挂在这个bus上。cpufreq是CPU device的一类特定功能,被抽象为一个subsys interface(有关subsys interface的概念,请参考“Linux设备模型(6)_Bus”)。
当CPU device和CPU driver匹配时,bus core会调用subsys interface的add_dev回调函数,相当于为该特定功能添加一个“device”,进而和该特定功能的“driver”(这里为cpufreq driver)匹配,执行driver的初始化(probe,或者其它)接口。
那么该“特定功能”应该用什么样的“device”表示呢?应具体功能具体对待。kernel使用cpufreq policy(即“调频策略”)来抽象cpufreq。所谓的调频策略,即频率调整的范围,它从一定程度上,代表了cpufreq的属性。这就是struct cpufreq_policy结构的现实意义:
1: struct cpufreq_policy {
2: /* CPUs sharing clock, require sw coordination */
3: cpumask_var_t cpus; /* Online CPUs only */
4: cpumask_var_t related_cpus; /* Online + Offline CPUs */
5:
6: unsigned int shared_type; /* ACPI: ANY or ALL affected CPUs
7: should set cpufreq */
8: unsigned int cpu; /* cpu nr of CPU managing this policy */
9: unsigned int last_cpu; /* cpu nr of previous CPU that managed
10: * this policy */
11: struct clk *clk;
12: struct cpufreq_cpuinfo cpuinfo;/* see above */
13:
14: unsigned int min; /* in kHz */
15: unsigned int max; /* in kHz */
16: unsigned int cur; /* in kHz, only needed if cpufreq
17: * governors are used */
18: unsigned int restore_freq; /* = policy->cur before transition */
19: unsigned int suspend_freq; /* freq to set during suspend */
20:
21: unsigned int policy; /* see above */
22: struct cpufreq_governor *governor; /* see below */
23: void *governor_data;
24: bool governor_enabled; /* governor start/stop flag */
25:
26: struct work_struct update; /* if update_policy() needs to be
27: * called, but you're in IRQ context */
28:
29: struct cpufreq_real_policy user_policy;
30: struct cpufreq_frequency_table *freq_table;
31:
32: struct list_head policy_list;
33: struct kobject kobj;
34: struct completion kobj_unregister;
35:
36: /*
37: * The rules for this semaphore:
38: * - Any routine that wants to read from the policy structure will
39: * do a down_read on this semaphore.
40: * - Any routine that will write to the policy structure and/or may take away
41: * the policy altogether (eg. CPU hotplug), will hold this lock in write
42: * mode before doing so.
43: *
44: * Additional rules:
45: * - Lock should not be held across
46: * __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
47: */
48: struct rw_semaphore rwsem;
49:
50: /* Synchronization for frequency transitions */
51: bool transition_ongoing; /* Tracks transition status */
52: spinlock_t transition_lock;
53: wait_queue_head_t transition_wait;
54: struct task_struct *transition_task; /* Task which is doing the transition */
55:
56: /* For cpufreq driver's internal use */
57: void *driver_data;
58: };
该结构看着很复杂,现在只需要关心几个事情:
min/max frequency,调频范围,对于可以自动调频的CPU而言,只需要这两个参数就够了。
current frequency和governor,对于不能自动调频的CPU,需要governor设置具体的频率值。下面介绍一下governor。
struct cpufreq_policy不会直接对外提供API。
3) cpufreq governors
governor的概念可参考“Linux cpuidle framework(1)_概述和软件架构”中相关的描述。对于不能自动调频的CPU core,必须由软件设定具体的频率值。根据使用场景的不同,会有不同的调整方案,这是由governor模块负责的,如下:
1: struct cpufreq_governor {
2: char name[CPUFREQ_NAME_LEN];
3: int initialized;
4: int (*governor) (struct cpufreq_policy *policy,
5: unsigned int event);
6: ssize_t (*show_setspeed) (struct cpufreq_policy *policy,
7: char *buf);
8: int (*store_setspeed) (struct cpufreq_policy *policy,
9: unsigned int freq);
10: unsigned int max_transition_latency; /* HW must be able to switch to
11: next freq faster than this value in nano secs or we
12: will fallback to performance governor */
13: struct list_head governor_list;
14: struct module *owner;
15: };
name,该governor的名称。
governor,用于governor状态切换的回调函数。
show_setspeed、store_setspeed,用于提供sysfs “setspeed” attribute文件的回调函数。
max_transition_latency,该governor所能容忍的最大频率切换延迟。
cpufreq governors主要向具体的governor模块提供governor的注册和注销接口,具体会在后续的文章中详细描述。
4)通过sysfs向用户空间提供的接口
请参考“linux cpufreq framework(3)_cpufreq core”。
4.3 cpufreq drivers
各个driver模块会基于cpufreq core实现具体的driver,请参考“linux cpufreq framework(2)_cpufreq driver”。
4.4 cpufreq stats
提供cpufreq有关的统计信息,请参考“linux cpufreq framework(3)_cpufreq core”。
5. 总结
本文介绍了cpufreq framework的基本情况,后面通过以下的文章,分析其它内容:
linux cpufreq framework(2)_cpufreq driver,从平台驱动工程师的角度,介绍怎么编写cpufreq驱动;
linux cpufreq framework(3)_cpufreq core,分析cpufreq的内部实现,并总结cpufreq提供的sysfs接口,介绍怎么通过sysfs,控制系统的调频行为;
linux cpufreq framework(4)_cpufreq governor,分析cpufreq governor的实现逻辑,并介绍几种常用的governor;
linux cpufreq framework(5)_ARM big·little driver及HMP,认识ARM平台HMP功能。
原创文章,转发请注明出处。蜗窝科技,www.wowotech.net。

评论:
2016-03-07 20:45
关于电源管理,有个问题请教,是这样的(高通soc芯片):
需要regulator供电的外设,都会在其DTS中对regulator进行设置(暂且称之为regulator设置节点),例如:
qcom,ctrl-supply-entry@0 {
qcom,supply-name = "vdda";
qcom,supply-min-voltage = <1250000>;
qcom,supply-max-voltage = <1250000>;
...
};
驱动代码中会对该节点进行解析,利用regulator_get、regulator_count_voltages等API函数进行设置。
通常,外设的DTS中还有一个这样的节点,例如:
vdda-supply = <&pm8994_l2>
该节点的解释是“Phandle for vreg regulator device node”,暂且称为regulator设备节点。但是我在代码中并没有找到对该节点的解析,我想请问,regulator设备节点与regulator设置节点是怎么联系起来的?
2015-09-27 15:18
文中多次提到cpu core 自动调频,可以根据自身的负载进行调频,只需要软件设置频率最大和最小调节范围即可,这个自调节是硬件自己实现的吗?之前没听说过,有没有相关的资料或者代码之类的可以阅读参考?
2015-08-20 17:15
你在本文中说的 hot plut 功能可以参考文章“Linux CPU core的电源管理(4)_cpu control” 这个文章我怎么一直没找到呢,是不是还没有完成啊?
2015-07-14 21:43
2015-07-14 22:09
cpu_up-->_cpu_up-->__cpu_up(arch/arm/kernel/smp.c)-->smp_ops.smp_boot_secondary
在这些函数里面加一些打印,查一下在哪里终止的,大概就明白原因了。
2015-07-17 12:41
axxx_secondary_startup secondary_startup __secondary_switched secondary_start_kernel
in C function “arm_xxx_boot_secondary” (located in file platsmp-xxx.c), pointer of “axxx_secondary_startup” is written to a address as following, do you know when and where to run the code in that address of “pmsu_mp_base + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)”?
if want to start cpu1, secondary_start_kernel must be called, but from my log, this function is not called when the issue happen? I need to know why it is not called.
Would you like to tell me some details about it? thank you.
pmsu_set_cpu_boot_addr(cpu, axxx_secondary_startup);
……
void pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
{
writel(virt_to_phys(boot_addr), pmsu_mp_base +
PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu));
}
谢谢
2015-07-17 14:23
cpu up失败,没有执行secondary_startup的原因,可能包括:
1. 上电失败,CPU压根没有执行。
2. CPU上电到执行“pmsu_mp_base + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)”处代码之间,出现问题。
3. “pmsu_mp_base + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)”中的数据丢失,
但和网卡的中断发流有什么关系呢?发流导致有些内存被覆盖?
正面的查法,还是顺着secondary cpu boot的流程,检查异常的位置。不用打印,可以用其它方法,比如点LED,实在不行,拉GPIO也行。找到异常位置之后,就很好找到原因。
侧面的查法,可以让网卡只产生中,不发流,看看是否出问题,这样可以知道到底是中断的原因,还是发流的原因,然后再进一步debug。
功能
最新评论
- wangjing
写得太好了 - wangjing
写得太好了! - DRAM
圖面都沒辦法顯示出來好像掛點了。 - Simbr
bus至少是不是还有个subsystem? - troy
@testtest:只要ldrex-modify-strex... - gh
Linux 内核在 sparse 内存模型基础上实现了vme...
文章分类
随机文章
文章存档
- 2025年4月(5)
- 2024年2月(1)
- 2023年5月(1)
- 2022年10月(1)
- 2022年8月(1)
- 2022年6月(1)
- 2022年5月(1)
- 2022年4月(2)
- 2022年2月(2)
- 2021年12月(1)
- 2021年11月(5)
- 2021年7月(1)
- 2021年6月(1)
- 2021年5月(3)
- 2020年3月(3)
- 2020年2月(2)
- 2020年1月(3)
- 2019年12月(3)
- 2019年5月(4)
- 2019年3月(1)
- 2019年1月(3)
- 2018年12月(2)
- 2018年11月(1)
- 2018年10月(2)
- 2018年8月(1)
- 2018年6月(1)
- 2018年5月(1)
- 2018年4月(7)
- 2018年2月(4)
- 2018年1月(5)
- 2017年12月(2)
- 2017年11月(2)
- 2017年10月(1)
- 2017年9月(5)
- 2017年8月(4)
- 2017年7月(4)
- 2017年6月(3)
- 2017年5月(3)
- 2017年4月(1)
- 2017年3月(8)
- 2017年2月(6)
- 2017年1月(5)
- 2016年12月(6)
- 2016年11月(11)
- 2016年10月(9)
- 2016年9月(6)
- 2016年8月(9)
- 2016年7月(5)
- 2016年6月(8)
- 2016年5月(8)
- 2016年4月(7)
- 2016年3月(5)
- 2016年2月(5)
- 2016年1月(6)
- 2015年12月(6)
- 2015年11月(9)
- 2015年10月(9)
- 2015年9月(4)
- 2015年8月(3)
- 2015年7月(7)
- 2015年6月(3)
- 2015年5月(6)
- 2015年4月(9)
- 2015年3月(9)
- 2015年2月(6)
- 2015年1月(6)
- 2014年12月(17)
- 2014年11月(8)
- 2014年10月(9)
- 2014年9月(7)
- 2014年8月(12)
- 2014年7月(6)
- 2014年6月(6)
- 2014年5月(9)
- 2014年4月(9)
- 2014年3月(7)
- 2014年2月(3)
- 2014年1月(4)
2016-12-09 08:52