一文搞懂linux cpu dvfs

电子说

1.3w人已加入

描述

1.介绍

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都不会占用过多时间或者调度开销。

具体的触发时机如下:

功耗

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分