深圳网站建设延安网站建设 東道网络
目录
- Linux devfreq 简介
- 核心数据结构
- devfreq_dev_profile 结构体
- devfreq_governor 结构体
- devfreq 结构体
- 工作流程
- devFreq framework 初始化
- governor 初始化
- devfreq Device 注册
- 动态变频的实现
- device_unregister 流程
- 用户空间节点
- 参考文章
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 的时间间隔,单位 msunsigned 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() callvoid (*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 nameconst 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 等 eventint (*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 workstruct 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.cstatic 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_START事件,调度工作队列,运行负载监控程序
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
