Linux 内核学习(4) --- devfreq 动态调频框架
目录
- 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_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
相关文章:

Linux 内核学习(4) --- devfreq 动态调频框架
目录 Linux devfreq 简介核心数据结构devfreq_dev_profile 结构体devfreq_governor 结构体devfreq 结构体 工作流程devFreq framework 初始化governor 初始化devfreq Device 注册动态变频的实现device_unregister 流程 用户空间节点参考文章 Linux devfreq 简介 现在的 Soc 由…...

Spring Boot 无缝集成SpringAI的函数调用模块
这是一个 完整的 Spring AI 函数调用实例,涵盖从函数定义、注册到实际调用的全流程,以「天气查询」功能为例,结合代码详细说明: 1. 环境准备 1.1 添加依赖 <!-- Spring AI OpenAI --> <dependency><groupId>o…...

Ansible自动化运维实战--yaml的使用和配置(7/8)
文章目录 一、YAML 基本语法1.1. 缩进1.2. 注释1.3. 列表1.4. 字典 二、Ansible 中 YAML 的应用2.1. Ansible 剧本(Playbooks)2.2. 变量定义2.3. 角色(Roles)2.4. Inventory 文件2.5. 数据类型2.6. 引用变量 在 Ansible 里&#x…...

kamailio-5.8.4-centos9编译
安装必要的依赖包 在开始编译之前,你需要安装编译 Kamailio 所需的一些基础依赖包: dnf install -y make gcc gcc-c flex bison libxml2-devel openssl-devel sqlite-devel mysql-devel pcre-devel libcurl-devel下载并解压 Kamailio 源码包 假设你已经…...

单例模式 - 单例模式的实现与应用
引言 单例模式(Singleton Pattern)是设计模式中最简单且最常用的模式之一。它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式常用于需要全局唯一对象的场景,如配置管理、日志记录、线程池等。 本文将详细介…...

hadoop==docker desktop搭建hadoop
hdfs map readuce yarn https://medium.com/guillermovc/setting-up-hadoop-with-docker-and-using-mapreduce-framework-c1cd125d4f7b 清理资源 docker-compose down docker system prune -f...

zookeeper的介绍和简单使用
1 zookerper介绍 zookeeper是一个开源的分布式协调服务,由Apache软件基金会提供,主要用于解决分布式应用中的数据管理、状态同步和集群协调等问题。通过提供一个高性能、高可用的协调服务,帮助构建可靠的分布式系统。 Zookeeper的特点和功能…...

DiffuEraser: 一种基于扩散模型的视频修复技术
视频修复算法结合了基于流的像素传播与基于Transformer的生成方法,利用光流信息和相邻帧的信息来恢复纹理和对象,同时通过视觉Transformer完成被遮挡区域的修复。然而,这些方法在处理大范围遮挡时常常会遇到模糊和时序不一致的问题࿰…...

CentOS/Linux Python 2.7 离线安装 Requests 库解决离线安装问题。
root@mwcollector1 externalscripts]# cat /etc/os-release NAME=“Kylin Linux Advanced Server” VERSION=“V10 (Sword)” ID=“kylin” VERSION_ID=“V10” PRETTY_NAME=“Kylin Linux Advanced Server V10 (Sword)” ANSI_COLOR=“0;31” 这是我系统的版本,由于是公司内网…...

World of Warcraft [CLASSIC] Jewelcrafting Gemstone 2
World of Warcraft [CLASSIC] Jewelcrafting & Gemstone 2 珠宝加工与常用宝石列表(紫色史诗级): World of Warcraft [CLASSIC] Jewelcrafting & Gemstone_wlk宝石属性一览表-CSDN博客...

AI刷题-最小化团建熟悉程度和
目录 问题描述 输入格式 输出格式 解题思路: 状态表示 状态转移 动态规划数组 预处理 实现: 1.初始化: 2.动态规划部分: (1)对于已分组状态的,跳过: (2&…...

一文详解Filter类源码和应用
背景 在日常开发中,经常会有需要统一对请求做一些处理,常见的比如记录日志、权限安全控制、响应处理等。此时,ServletApi中的Filter类,就可以很方便的实现上述效果。 Filter类 是一个接口,属于 Java Servlet API 的一部…...

应用层协议 HTTP 讲解实战:从0实现HTTP 服务器
🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 一:🔥 HTTP 协议 🦋 认识 URL🦋 urlencode 和 urldecode 二:🔥 HTTP 协议请求与响应格式 🦋 HTTP 请求…...

DDD-全面理解领域驱动设计中的各种“域”
一、DDD-领域 在领域驱动设计(Domain-Driven Design,DDD)中,**领域(Domain)**指的是软件系统所要解决的特定业务问题的范围。它涵盖了业务知识、规则和逻辑,是开发团队与领域专家共同关注的核心…...

PHP防伪溯源一体化管理系统小程序
🔍 防伪溯源一体化管理系统,品质之光,根源之锁 🚀 引领防伪技术革命,重塑品牌信任基石 我们自豪地站在防伪技术的前沿,为您呈现基于ThinkPHP和Uniapp精心锻造的多平台(微信小程序、H5网页&…...

纯css实现div宽度可调整
<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>纯css实现div尺寸可调整</title><style…...

C# 中使用Hash用于密码加密
通过一定的哈希算法(典型的有MD5,SHA-1等),将一段较长的数据映射为较短小的数据,这段小数据就是大数据的哈希值。他最大的特点就是唯一性,一旦大数据发生了变化,哪怕是一个微小的变化࿰…...

如何建设一个企业级的数据湖
建设一个企业级的数据湖是一项复杂且系统化的工程,需要从需求分析、技术选型、架构设计到实施运维等多个方面进行综合规划和实施。以下是基于我搜索到的资料,详细阐述如何建设企业级数据湖的步骤和关键要点: 一、需求分析与规划 明确业务需…...

目标跟踪之sort算法(3)
这里写目录标题 1 流程1 预处理2 跟踪 2 代码 参考:sort代码 https://github.com/abewley/sort 1 流程 1 预处理 1.1 获取离线检测数据。1.2 实例化跟踪器。2 跟踪 2.1 轨迹处理。根据上一帧的轨迹预测当前帧的轨迹,剔除到当前轨迹中为空的轨迹得到当前…...

【java数据结构】HashMapOJ练习题
【java数据结构】HashMapOJ练习题 一、只出现一次的数字二 、随机链表的复制三 、宝石与石头四、坏键盘打字五、前K个高频单词 博客最后附有整篇博客的全部代码!!! 一、只出现一次的数字 只出现一次的数字 思路: 先遍历一遍数组…...

Nginx前端后端共用一个域名如何配置
在 Nginx 中配置前端和后端共用一个域名的情况,通常是通过路径或子路径将请求转发到不同的服务。以下是一个示例配置,假设: 前端静态文件在 /var/www/frontend/。 后端 API 服务运行在 http://127.0.0.1:5000。 域名是 example.comÿ…...

SpringBoot3+Vue3开发学生选课管理系统
功能介绍 分三个角色登录:学生登录,老师登录,教务管理员登录,不同用户功能不同! 1.学生用户功能 选课记录,查看选课记录,退选。选课管理,进行选课。通知管理,查看通知消…...

Linux系统 C/C++编程基础——基于GTK+的图形用户界面编程
ℹ️大家好,我是练小杰,今天星期三了,距离除夕又少了一天,新年的钟声就快敲响了😆 本文是有关Linux C/C编程中的基于GTK的图形用户界面编程知识点,后续会不断添加相关内容 ~~ 回顾:【使用make工具和Makefil…...

【Leetcode 每日一题】40. 组合总和 II
问题背景 给定一个候选人编号的集合 c a n d i d a t e s candidates candidates 和一个目标数 t a r g e t target target,找出 c a n d i d a t e s candidates candidates 中所有可以使数字和为 t a r g e t target target 的组合。 c a n d i d a t e s c…...

python 变量范围的定义与用法
文章目录 1. 局部变量(Local Scope)示例: 2. 嵌套函数变量(Enclosing Scope)示例:说明: 3. 全局变量(Global Scope)示例:说明: 4. 内置变量&#…...

TRTC实时对话式AI解决方案,助力人机语音交互极致体验
近年来,AI热度持续攀升,无论是融资规模还是用户热度都大幅增长。2023 年,中国 AI 行业融资规模达2631亿人民币,较2022年上升51%;2024年第二季度,全球 AI 初创企业融资规模为 240 亿美金,较第一季…...

dev c++ ‘unordered_set‘ does not name a type
参考:https://blog.csdn.net/Zaczc/article/details/142531525 启用C11标准步骤 工具->编译选项 勾选编译时加入以下命令 在空白处添加:-stdc11 单击确定,启用成功...

算法每日双题精讲 —— 二分查找(寻找旋转排序数组中的最小值,点名)
🌟快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。 🌟 别再犹豫了!快来订阅我们的算法每日双题精讲专栏,一起踏上算法学习的精彩之旅吧💪 在算法的…...

three.js+WebGL踩坑经验合集(4.2):为什么不在可视范围内的3D点投影到2D的结果这么不可靠
上一篇,笔者留下了一个问题,three.js内置的THREE.Vector3.project方法算出来的结果对于超出屏幕可见范围的点来说错得相当离谱。 three.jsWebGL踩坑经验合集(4.1):THREE.Line2的射线检测问题(注意本篇说的是Line2,同样也不是阈值…...

Kafka运维宝典 (二)- kafka 查看kafka的运行状态、broker.id不一致导致启动失败问题、topic消息积压量告警监控脚本
Kafka运维宝典 (二) 文章目录 Kafka运维宝典 (二)一、kafka broker.id冲突问题1. broker.id 冲突的影响2. 如何发现 broker.id 冲突3. 解决 broker.id 冲突的方法4. broker.id 配置管理5. 集群启动后确认 broker.id 唯一性6. brok…...