【原创】Linux cpufreq framework
程序员文章站
2022-04-20 18:06:52
背景 By 鲁迅 By 高尔基 说明: 1. Kernel版本:4.14 2. ARM64处理器 3. 使用工具:Source Insight 3.5, Visio 1. 介绍 子系统负责在运行时对CPU频率和电压的动态调整,以达到性能和功耗的平衡,它也叫 。 原理:CMOS电路中功耗与电压的平方成 ......
背景
-
read the fucking source code!
--by 鲁迅 -
a picture is worth a thousand words.
--by 高尔基
说明:
- kernel版本:4.14
- arm64处理器
- 使用工具:source insight 3.5, visio
1. 介绍
-
cpufreq
子系统负责在运行时对cpu频率和电压的动态调整,以达到性能和功耗的平衡,它也叫dvfs(dynamic voltage frequency scaling)
。 -
dvfs
原理:cmos电路中功耗与电压的平方成正比,与频率也成正比。此外,频率越高,性能也越强,相应的能耗就增大了,所以tradeoff依旧是一门艺术。 -
cpufreq framework
类似于cpuidle framework
,提供机制(cpufreq driver
)与策略(cpufreq governor
),此外提供了cpufreq core
来对机制和策略进行管理。
2. 框架
主要代码路径:driver/cpufreq/cpufreq.c
include/linux/cpufreq.h
drivers/cpufreq/cpufreq_userspace.c
先上框架图:
粗一看与cpuidle framework
的图很像,但是有些差别如下:
- 用户层与
cpufreq framework
的交互,主要是通过sysfs
,这个可以在/sys
下看到很多文件,而kernel module
也可以使用某些接口来回调它; - 系统只允许有一个platform drivers,为全局变量
cpufreq_driver
,cpufreq core
通过它去回调驱动; - 驱动与硬件的交互,通过如
set_clk_rate/regulator_set_voltage
等接口去设置cpu的时钟和电压,而不再是cpu_ops
; - 有一个全局的
governor
链表cpufreq_governor_list
,可以通过查找链表来选择合适的governor
;
3. 数据结构
核心的数据结构有三个:
-
struct cpufreq_policy
:用于描述不同的policy,涉及到频率表、cpuinfo等各种信息,并且每个policy都会指向某个governor
; -
struct cpufreq_governor
:用于对policy的管理; -
struct cpufreq_driver
:用于描述具体的驱动程序;
如下图:
4. 流程
4.1 cpufreq_driver注册
仔细观察上图中struct cpufreq_driver
结构体,你会发现它与传统的设备模型中的驱动结构不一致,它并没有内嵌struct bus_type
或struct device_driver
类型,这就决定了它不属于“device<--->bus<--->driver”
这种模型。
事实上,cpufreq_driver
是一个全局的变量,不属于任何一个拓扑的结构。它的注册从cpufreq_register_driver
开始。
流程如下图:
4.2 governor注册
接口为:cpufreq_register_governor
,这个操作实在是太简单了,添加到全局链表即可,完事!
顺带提一句吧,还有一个接口cpufreq_register_notifier
,这个用于通知机制,具体不再深入分析了。
4.3 sysfs访问
cpufreq core
会在/sys目录下创建相应的节点,如下图所示:
用户态可以通过cat/echo
命令来读取/设置相应的值。
对应结构体如下:
static struct attribute *default_attrs[] = { &cpuinfo_min_freq.attr, &cpuinfo_max_freq.attr, &cpuinfo_transition_latency.attr, &scaling_min_freq.attr, &scaling_max_freq.attr, &affected_cpus.attr, &related_cpus.attr, &scaling_governor.attr, &scaling_driver.attr, &scaling_available_governors.attr, &scaling_setspeed.attr, null };
各个字段含义如下:
-
affected_cpus
:需要软件调整频率的cpu列表; -
related_cpus
:需要软件或硬件来调整频率的cpu列表; -
cpuinfo_max_freq
:cpu能够支持的最高频率(khz); -
cpuinfo_min_freq
:cpu能够支持的最小频率(khz); -
cpuinfo_transition_latency
:cpu频率切换时的时间开销(ns); -
scaling_available_governors
:内核中支持的governor
; -
scaling_driver
:硬件驱动,比如cpufreq-dt
; -
scaling_cur_freq
:cpu工作频率; -
scaling_max_freq
:当前policy的频率上限; -
scaling_min_freq
:当前policy的频率下限; -
scaling_governor
:cpu调频策略,可以修改; -
scaling_setspeed
:设置cpu运行频率;
如下图:
sysfs
回调下来后,会进入xxx_store/xxx_show
函数来进行具体的设置,至于设置的流程大体与cpufreq_driver
注册图中类似,不再深入分析了。
驱动的实现变成了实现struct cpufreq_driver
函数指针中的函数,并注册即可。目前的驱动开发大抵如此,变成了一道填空题,当然我们也需要去了解背后的原理。
推荐阅读
-
【原创】ARMv8 MMU及Linux页表映射
-
【原创】(十三)Linux内存管理之vma/malloc/mmap
-
【原创】Linux cpu hotplug
-
【原创】(十)Linux内存管理 - zoned page frame allocator - 5
-
【原创】Linux PSCI框架
-
【原创】(十四)Linux内存管理之page fault处理
-
【原创】(二)Linux物理内存初始化
-
【原创】(八)Linux内存管理 - zoned page frame allocator - 3
-
【原创】Linux Suspend流程分析
-
【原创】(十二)Linux内存管理之vmap与vmalloc