目录
Linux devfreq 简介
现在的 Soc
由众多的子模块构成,比如CNN
,DSP
,ISP
,CPU
等,在不同的场景下,并非所有的模块都要保持最高的性能,因此,SoC
设计的时候会划分一些电压域,这些电压域内的模块,可以根据具体需要调整电压和频率,从而达到既能实现功能,又能降低功耗的目的
不过频率电压并不是随意搭配的,一般情况下,高频对应着高压,低频对应着低压,这是由于晶体管的电器特性决定的
Linux
内核用 OPP(Operation Performance Point)
对这些设备支持的频率和电压进行描述和管理,CPU
的 DVFS
也就是 cpufreq
也是基于 OPP
实现的,但是仅仅支持 CPU
设备的调频和调压,这里主要介绍的是设备的 DVFS
,也是基于 OPP
实现的,Linux
内核实现了一个 devfreq framework
用于实现和管理 device
的 dvfs
,用于支持非CPU
设备的调频,调压,并且设备可以匹配自己的 governor
策略
devFreq framework
规范了设备调频调压的过程,也标准化了用户空间的控制接口,通过设备的需求,利用 governor
策略控制频率,进而根据 opp table
选择对应的电压
核心数据结构
devfreq_dev_profile 结构体
devfreq_dev_profile
结构体, 是OPP device
注册到 devfreq framework
的数据结构,主要包含 OPP
设备的频率信息和相关的回调函数,是 devfreq framework
和 OPP device
的交互接口,将 OPP device driver
对 devfreq
的使用,简化为对 devfreq profile
结构体的填充
// drivers/devfreq/devfreq.c
// include/linux/devfreq.h
struct devfreq_dev_profile {
// devfreq 注册的初始化频率
unsigned long initial_freq;
// governor polling 的时间间隔,单位 ms
unsigned int polling_ms;
// devfreq framework用于设定 OPP device frequency的回调函数
int (*target)(struct device *dev, unsigned long *freq, u32 flags);
// devfreq framework 用于获取 OPP device 负载状态的回调函数
int (*get_dev_status)(struct device *dev, struct devfreq_dev_status *stat);
// devfreq framework 用于获取当前频率的回调函数
int (*get_cur_freq)(struct device *dev, unsigned long *freq);
// 当 devfreq 设备从 devfreq framework 移除的时候调用
// 调用时机是 error 或者 devfreq_remove_device() call
void (*exit)(struct device *dev);
unsigned long *freq_table;
unsigned int max_state;
};
devfreq_governor 结构体
devfreq_governor
是 governor
注册到devfreq framework
的数据结构,主要包含 governor
的相关属性和具体的函数实现,是devfreq framework
和 governor
的交互接口
struct devfreq_governor {
// 将 devfreq_governor 作为链表节点进行管理
struct list_head node;
// governor name
const char name[DEVFREQ_NAME_LEN];
// 是否可以切换为其他 governor 1:不能切换
const unsigned int immutable;
// governor 注册到 devfreq framework 的算法实现函数,返回调整后的频率
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
// governor 注册到 devfreq framework 的 event 处理函数,处理 start,stop,suspend,resume 等 event
int (*event_handler)(struct devfreq *devfreq,
unsigned int event, void *data);
};
其中的 get_target_freq
一般会调用 devfreq_dev_profile.get_dev_status()
获取当前 OPP device
的负载情况 (load = busy_time / total_time
)
devfreq 结构体
devfreq
结构体是 devfreq device
的核心结构,每个注册的设备都会新建一个 devfreq
结构体,
作用是将上述 OPP device
的 devfreq_dev_profile
和 governor
的 devfreq_governor
连接到一起,
并通过设备模型的 device
类,为user
空间提供接口
struct devfreq {
struct list_head node;
struct mutex lock;
// device 属于 devfreq_class, 父节点就是使用 devfreq 的 device 比如 GPU,DDR等
struct device dev;
// OPP device 注册到 devfreq framework 的配置信息
struct devfreq_dev_profile *profile;
// governor注册到devfreq framework的配置信息
const struct devfreq_governor *governor;
char governor_name[DEVFREQ_NAME_LEN];
struct notifier_block nb;
// 用于监控负载情况的 delayed work
struct delayed_work work;
unsigned long previous_freq;
struct devfreq_dev_status last_status;
void *data; /* private data for governors */
// 限制用户请求的最小频率
unsigned long min_freq;
// 限制用户请求的最大频率
unsigned long max_freq;
bool stop_polling;
/* information for device frequency transition */
// 记录 devfreq 中 frequency 的转移信息
unsigned int total_trans;
unsigned int *trans_table;
unsigned long *time_in_state;
unsigned long last_stat_updated;
struct srcu_notifier_head transition_notifier_list;
};
工作流程
devFreq framework 初始化
逻辑非常简单,主要完成下面的任务:
- 创建
devfreq_class
设备类 - 创建
delayed work
,是用于监控负载的工作队列 - 加入到
subsys_initcall
,系统启动时进行初始化
static int __init devfreq_init(void)
{
devfreq_class = class_create(THIS_MODULE, "devfreq");
devfreq_wq = create_freezable_workqueue("devfreq_wq");
devfreq_class->dev_groups = devfreq_groups;
return 0;
}
subsys_initcall(devfreq_init);
governor 初始化
系统中可以支持多个 governor
,在系统启动时就会进行初始化,并注册到 devFreq framework
中,后续的 OPP device
创建 devFreq
设备,
会根据 governor name
从已经初始化的 governor
列表中,查找对应的 governor
实例
// driver/devfreq 目录下 governor_performance.c
// driver/devfreq 目录下 governor_simpleondemand.c
static struct devfreq_governor devfreq_performance = {
.name = "performance",
.get_target_freq = devfreq_performance_func,
.event_handler = devfreq_performance_handler,
};
static int __init devfreq_performance_init(void)
{
return devfreq_add_governor(&devfreq_performance);
}
subsys_initcall(devfreq_performance_init);
主要流程:
- 填充
devfreq_governor
结构体,不同类型的结构体,对get_target_freq
和event_handler
会有不同的实现 - 调用
devfreq_add_governor
加入到devfreq framework
的governor
列表中 - 加入到
subsys_initcall
,系统启动时进行初始化
目前系统支持下面几种 governor
Simple_ondemand
: 按需调整模式,根据负载动态频率,平衡性能和功耗Performance
: 性能优先模式,调整到最大频率Powersave
: 功耗优先模式,调整到最小频率Userspace
: 用户指定模式,根据用于sysfs
节点写入进行调整Passive
: 被动模式,使用设备指定方法做调整或者跟随father devfreq
设备的governor
devfreq Device 注册
这里以开源的 mali gpu midgard
内核为例
- 配置正常的
OPP table
,确定正常的OPP
电压和频率 - 获取
OPP device
的频率信息,完善正确的回调函数,最后填充devfreq profile
结构体 - 调用
devfreq_add_device
函数,将devfreq profile
结构,governor name
添加到devfreq
框架
int kbase_devfreq_init(struct kbase_device *kbdev)
{
struct devfreq_dev_profile *dp;
dp->initial_freq = kbdev->current_freqs[0];
dp->polling_ms = 100;
dp->target = kbase_devfreq_target;
dp->get_dev_status = kbase_devfreq_status;
dp->get_cur_freq = kbase_devfreq_cur_freq;
dp->exit = kbase_devfreq_exit;
kbdev->devfreq = devfreq_add_device(kbdev->dev, dp, "simple_ondemand", NULL);
}
动态变频的实现
devfreq framework
负责监控程序的运行,governor
提供算法,OPP device
提供自身负载状态和频率设置放的实现,系统中有不同的 governor
,不同的 governor
有不同的管理算法
devfreq_add_device
时发送DEVFEQ_GOV_START
事件
struct devfreq *devfreq_add_device(struct device *dev,
struct devfreq_dev_profile *profile,
const char *governor_name,
void *data) {
......
devfreq->governor = governor;
err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START,
NULL);
goto err_init;
}
governor
的event_handler
收到DEVFREQ_GOV_STAR
T事件,调度工作队列,运行负载监控程序
Note: 这里以
simple_ondemand governor
为例
static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
unsigned int event, void *data) {
switch (event) {
case DEVFREQ_GOV_START:
devfreq_monitor_start(devfreq);
break;
......
}
- 负载监控程序
- 调用
governor
的get_target_freq
方法,获取下一次的调频目标值 - 调用
OPP device
注册到devfreq_framework
的target
函数,设置新的频率信息 - 调度延迟工作队列,延迟
OPP device
设置的轮询间隔,再次运行
static void devfreq_monitor(struct work_struct *work)
{
int err;
struct devfreq *devfreq = container_of(work,
struct devfreq, work.work);
mutex_lock(&devfreq->lock);
// monitor 程序的核心函数
err = update_devfreq(devfreq);
/* 以 poolling_ms 为周期进行周期监控 */
queue_delayed_work(devfreq_wq, &devfreq->work,
msecs_to_jiffies(devfreq->profile->polling_ms));
mutex_unlock(&devfreq->lock);
}
int update_devfreq(struct devfreq *devfreq)
{
/*获取要调频到的结果频率*/
devfreq->governor->get_target_freq(devfreq, &freq);
/*在调频前后都有通知发出来*/
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE);
/*调用 OPP devices的 target 函数设置目标频率*/
devfreq->profile->target(devfreq->dev.parent, &freq, flags);
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
}
device_unregister 流程
device_unregister
注销过程中会调用 devfreq_dev_release
函数,完成下面的事务:
- 发送
DEVFREQ_GOV_STOP event
,governor
停止运行 - 回调
OPP device
注册到devfreq framework
的exit函数 - 释放
devfreq device
申请的资源
static void devfreq_dev_release(struct device *dev)
{
struct devfreq *devfreq = to_devfreq(dev);
......
if (devfreq->governor)
devfreq->governor->event_handler(devfreq,
DEVFREQ_GOV_STOP, NULL);
用户空间节点
在 devfreq framework
初始化时,会创建下面的节点:
static struct attribute *devfreq_attrs[] = {
&dev_attr_governor.attr,
&dev_attr_available_governors.attr, // 可用的 governor
&dev_attr_cur_freq.attr, //当前频率
&dev_attr_available_frequencies.attr,// 可用频率列表
&dev_attr_target_freq.attr, // 目标频率
&dev_attr_polling_interval.attr, // 调度间隔
&dev_attr_min_freq.attr, // 最小频率
&dev_attr_max_freq.attr, // 最大频率
&dev_attr_trans_stat.attr, // 状态调整记录表
NULL,
};
ATTRIBUTE_GROUPS(devfreq);
available_frequencies
: 可用的频率列表
available_governors
:可用的governor
cur_freq
:当前频率
governor
: 当前governor
max_freq
:最大频率
min_freq
:最小频率
polling_interval
:governor调度的时间间隔,单位是ms.
target_freq
:目标频率
trans_stat
:状态调整表记录
参考文章
https://mp.weixin.qq.com/s/TI3ryUewRgt9LFKr-tDkJQ
https://www.cnblogs.com/hellokitty2/p/13061707.html