电子说
介绍
CPU dvfs(dynamic voltage frequency scaling)子系统负责cpu运行时,对其频率及电压进行调整,以求性能满足的前提下,cpu的功耗尽可能低。
芯片的CMOS威廉希尔官方网站 的功耗有个计算公式,比较复杂,简单来说功耗跟电压平方成正比,跟频率成正比,因此CPU dvfs在涉及到电压调整的时候,功耗受益还是很明显的。但是仅调频的话,收益是比较小的,因为频率对应着算力,当频率减少,对应的算力也减弱,这样执行时间也会变长。
CPU dvfs framework(也常被称为cpufreq framework)和其他的linux framework类似。主要解决两个问题:什么时候调频调压,怎么调频调压。cpufreq driver提供调频调压的机制,cpufreq governor提供不同的策略,cpufreq core对通用的调频逻辑做抽象,为上层提供功能、接口封装,对下层调用抽象封装的硬件控制接口。此外,还借助频率电压对opp(operating performance points)功能,clk driver、regulator driver对频率及电压做硬件时钟及电压的调整。
2. 框架
cpufreq core:是cpufreq framework的核心模块,和kernel其它framework类似,主要实现三类功能
抽象调频调压的公共逻辑和接口,主要围绕struct cpufreq_driver、struct cpufreq_policy和struct cpufreq_governor三个数据结构进行
以sysfs的形式向用户空间提供统一的接口,以notifier的形式向其它driver提供频率变化的通知。
提供CPU频率和电压控制的驱动框架,方便底层driver的开发;同时,提供governor框架,用于实现不同的频率调整机制。
cpufreq governor:负责调频调压的各种策略,每种governor计算频率的方式不同,根据提供的频率范围和参数(阈值等),计算合适的频率。
userspace:用户通过操作scaling_setspeed文件节点操作频率及电压的调整。
ondemand:根据CPU当前的使用率,动态调整cpu的频率及电压。Sched通过调用ondemand注册进来的回调函数来触发负载的估算,它以一定时间间隔对系统负载进行采样,按需调整cpu的频率及电压,若当前cpu的利用率超过设定的阈值,就会立即调整到最大的频率。调频速度快,但是不够精确。
conservative:类似ondemand,在调频调节时会平滑一下,以防最大、最小频率之间来回跳变。调整的时候会以一定步长调整,而不是直接调整到目标值。同时会周期的计算系统负载,用以决定调到什么频率。
schedutil:通过将自己的调频策略注册到hook,在负载发生变化的时候,会调用该hook,此时就可以进行调频决策或执行调频动作。前面的调频策略都是周期采样计算cpu负载有滞后性,精度也有限,而schedutil可以使用PELT(per entity load tracking)或者WALT(window assist load tracking)准确的计算task的负载。如果支持fast_switch的功能,可以在中断上下文直接进行调频。
cpufreq driver:负责平台相关的调频调压机制的实现,基于cpu subsystem driver、OPP、clock driver、regulator driver等模块,提供对CPU频率和电压的控制。
cpufreq stats:负责调频信息和各频点运行时间等统计,提供每个cpu的cpufreq有关的统计信息。
3. 数据结构
struct cpufreq_policy:linux使用cpufreq policy来抽象cpu设备的调频调压功能,用于描述不同的policy,包含频率表、cpuinfo等各种信息,并且每个policy都会对应某个具体的governor。
struct cpufreq_governor:不同policy的管理策略,根据使用场景的不同,会有不同的调频调压策略。
struct cpufreq_driver:用于描述cpufreq的驱动,是驱动工程师最关注的结构。
4. 初始化及工作流程
4.1 初始化流程
cpufreq_register_driver函数为cpufreqdriver注册的入口,驱动程序通过调用该函数进行初始化,并传入相关的struct cpufreq_driver,cpufreq_register_driver会调用subsys_interface_register,最终执行回调函数cpufreq_add_dev。
系统中可以同时存在多个governor,policy通过cpufreq_policy->governor指针和某个governor相关联。要想一个governor能够被使用,首先要把该governor注册到cpufreq framework中。
cpufreq core定义了一个全局链表变量:cpufreq_governor_list,注册函数首先根据governor的名称,通过__find_governor()函数查找该governor是否已经被注册过,如果没有被注册过,则把代表该governor的结构体添加到cpufreq_governor_list链表中。
4.2 工作流程
不同的governor的触发调频调压流程不一样,这里以scheduutil governor为例。
CFS负载变化的时候或者RT、DL任务状态更新的时候,就会启动调频这几个scheduler类会调用cpufreq_update_util函数(前面注册进来的hook函数)触发schedutil工作。每个cpu最终会回调到sugov_upate_shared或者sugov_upate_single函数中的一个。
由于是从scheduler里直接调用下来的,最终执行调频切换时,无论是快速路径触发的简单写寄存器,还是慢速路径触发的kthread都不会占用过多时间或者调度开销。
具体的触发时机如下:
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !