当前位置: 首页 > 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; 先遍历一遍数组…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...