当前位置: 首页 > news >正文

Linux 内核学习(4) --- devfreq 动态调频框架

目录

      • Linux devfreq 简介
      • 核心数据结构
        • devfreq_dev_profile 结构体
        • devfreq_governor 结构体
        • devfreq 结构体
      • 工作流程
        • devFreq framework 初始化
        • governor 初始化
        • devfreq Device 注册
        • 动态变频的实现
        • device_unregister 流程
      • 用户空间节点
        • 参考文章

Linux devfreq 简介

现在的 Soc 由众多的子模块构成,比如CNNDSPISPCPU等,在不同的场景下,并非所有的模块都要保持最高的性能,因此,SoC 设计的时候会划分一些电压域,这些电压域内的模块,可以根据具体需要调整电压和频率,从而达到既能实现功能,又能降低功耗的目的

不过频率电压并不是随意搭配的,一般情况下,高频对应着高压,低频对应着低压,这是由于晶体管的电器特性决定的

Linux 内核用 OPP(Operation Performance Point) 对这些设备支持的频率和电压进行描述和管理,CPUDVFS 也就是 cpufreq 也是基于 OPP 实现的,但是仅仅支持 CPU 设备的调频和调压,这里主要介绍的是设备的 DVFS,也是基于 OPP 实现的,Linux 内核实现了一个 devfreq framework 用于实现和管理 devicedvfs,用于支持非CPU设备的调频,调压,并且设备可以匹配自己的 governor 策略

devFreq framework 规范了设备调频调压的过程,也标准化了用户空间的控制接口,通过设备的需求,利用 governor 策略控制频率,进而根据 opp table 选择对应的电压
devfreq_framework

核心数据结构

devfreq_dev_profile 结构体

devfreq_dev_profile 结构体, OPP device 注册到 devfreq framework 的数据结构,主要包含 OPP 设备的频率信息和相关的回调函数,是 devfreq frameworkOPP device 的交互接口,将 OPP device driverdevfreq 的使用,简化为对 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_governorgovernor 注册到devfreq framework 的数据结构,主要包含 governor 的相关属性和具体的函数实现,是devfreq frameworkgovernor 的交互接口

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 devicedevfreq_dev_profilegovernordevfreq_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);

主要流程:

  1. 填充 devfreq_governor 结构体,不同类型的结构体,对 get_target_freqevent_handler 会有不同的实现
  2. 调用 devfreq_add_governor 加入到 devfreq frameworkgovernor 列表中
  3. 加入到 subsys_initcall,系统启动时进行初始化

目前系统支持下面几种 governor

  • Simple_ondemand: 按需调整模式,根据负载动态频率,平衡性能和功耗
  • Performance: 性能优先模式,调整到最大频率
  • Powersave: 功耗优先模式,调整到最小频率
  • Userspace: 用户指定模式,根据用于 sysfs 节点写入进行调整
  • Passive: 被动模式,使用设备指定方法做调整或者跟随father devfreq 设备的governor
devfreq Device 注册

这里以开源的 mali gpu midgard 内核为例

  1. 配置正常的OPP table,确定正常的 OPP 电压和频率
  2. 获取 OPP device 的频率信息,完善正确的回调函数,最后填充 devfreq profile 结构体
  3. 调用 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 有不同的管理算法

  1. 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;
}
  1. governorevent_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;......
}
  1. 负载监控程序
  • 调用 governorget_target_freq 方法,获取下一次的调频目标值
  • 调用 OPP device 注册到 devfreq_frameworktarget 函数,设置新的频率信息
  • 调度延迟工作队列,延迟 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 函数,完成下面的事务:

  1. 发送 DEVFREQ_GOV_STOP eventgovernor停止运行
  2. 回调 OPP device 注册到 devfreq framework 的exit函数
  3. 释放 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 函数调用实例&#xff0c;涵盖从函数定义、注册到实际调用的全流程&#xff0c;以「天气查询」功能为例&#xff0c;结合代码详细说明&#xff1a; 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 剧本&#xff08;Playbooks&#xff09;2.2. 变量定义2.3. 角色&#xff08;Roles&#xff09;2.4. Inventory 文件2.5. 数据类型2.6. 引用变量 在 Ansible 里&#x…...

kamailio-5.8.4-centos9编译

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

单例模式 - 单例模式的实现与应用

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

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是一个开源的分布式协调服务&#xff0c;由Apache软件基金会提供&#xff0c;主要用于解决分布式应用中的数据管理、状态同步和集群协调等问题。通过提供一个高性能、高可用的协调服务&#xff0c;帮助构建可靠的分布式系统。 Zookeeper的特点和功能…...

DiffuEraser: 一种基于扩散模型的视频修复技术

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

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 珠宝加工与常用宝石列表&#xff08;紫色史诗级&#xff09;&#xff1a; World of Warcraft [CLASSIC] Jewelcrafting & Gemstone_wlk宝石属性一览表-CSDN博客...

AI刷题-最小化团建熟悉程度和

目录 问题描述 输入格式 输出格式 解题思路&#xff1a; 状态表示 状态转移 动态规划数组 预处理 实现&#xff1a; 1.初始化&#xff1a; 2.动态规划部分&#xff1a; &#xff08;1&#xff09;对于已分组状态的&#xff0c;跳过&#xff1a; &#xff08;2&…...

一文详解Filter类源码和应用

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

应用层协议 HTTP 讲解实战:从0实现HTTP 服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; HTTP 协议 &#x1f98b; 认识 URL&#x1f98b; urlencode 和 urldecode 二&#xff1a;&#x1f525; HTTP 协议请求与响应格式 &#x1f98b; HTTP 请求…...

DDD-全面理解领域驱动设计中的各种“域”

一、DDD-领域 在领域驱动设计&#xff08;Domain-Driven Design&#xff0c;DDD&#xff09;中&#xff0c;**领域&#xff08;Domain&#xff09;**指的是软件系统所要解决的特定业务问题的范围。它涵盖了业务知识、规则和逻辑&#xff0c;是开发团队与领域专家共同关注的核心…...

PHP防伪溯源一体化管理系统小程序

&#x1f50d; 防伪溯源一体化管理系统&#xff0c;品质之光&#xff0c;根源之锁 &#x1f680; 引领防伪技术革命&#xff0c;重塑品牌信任基石 我们自豪地站在防伪技术的前沿&#xff0c;为您呈现基于ThinkPHP和Uniapp精心锻造的多平台&#xff08;微信小程序、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用于密码加密

通过一定的哈希算法&#xff08;典型的有MD5&#xff0c;SHA-1等&#xff09;&#xff0c;将一段较长的数据映射为较短小的数据&#xff0c;这段小数据就是大数据的哈希值。他最大的特点就是唯一性&#xff0c;一旦大数据发生了变化&#xff0c;哪怕是一个微小的变化&#xff0…...

如何建设一个企业级的数据湖

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

目标跟踪之sort算法(3)

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

【java数据结构】HashMapOJ练习题

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

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...