4. CPUFreq 核心数据结构:结构体定义与字段解析
CPUFreq 子系统的软件架构围绕三个核心数据结构展开:cpufreq_policy(策略)、cpufreq_driver(驱动)和 cpufreq_governor(调节器)。这三个结构体共同定义了 CPU 频率调节的完整工作流:驱动提供硬件能力,调节器决定何时调频,策略则作为上下文容器串联两者。理解它们的字段含义是进行驱动开发和功耗优化的第一步。
核心关系速记: cpufreq_driver 是“硬件层”,cpufreq_governor 是“决策层”,cpufreq_policy 是“管理层”——策略持有驱动和调节器的指针,并维护每个 CPU 集群的调频状态。
4.1 cpufreq_policy:频率调节的“上下文容器”
cpufreq_policy 是每个 CPU 集群(policy 实例)的频率管理上下文。它记录了当前频率范围、调节器实例、驱动回调、以及调频过程中的同步状态。该结构体定义在 include/linux/cpufreq.h 中,核心字段如下:
| 字段名 | 类型 | 描述 |
|---|---|---|
cpu |
unsigned int | 该 policy 管理的第一个 CPU 编号(管理的最小 CPU) |
cpus |
cpumask_var_t | 该 policy 覆盖的所有 CPU 的 cpumask(共享同一频率的 CPU 集合) |
related_cpus |
cpumask_var_t | 硬件上频率相关的 CPU 集合(通常与 cpus 相同,但可能包含离线 CPU) |
min / max |
unsigned int | 当前允许的最小/最大频率(kHz),由用户态或调节器动态调整 |
cur |
unsigned int | 当前实际频率(kHz),通常由驱动通过 get 回调读取 |
governor |
struct cpufreq_governor * | 指向当前绑定的调节器结构体指针 |
governor_data |
void * | 调节器的私有数据指针(例如 governor 内部统计信息) |
policy |
unsigned int | 策略标志:CPUFREQ_POLICY_POWERSAVE 或 CPUFREQ_POLICY_PERFORMANCE |
transition_ongoing |
atomic_t | 原子变量,标记是否有频率切换正在进行(用于互斥) |
transition_lock |
struct mutex | 频率切换时的互斥锁,防止并发调频 |
driver_data |
void * | 驱动私有数据指针(例如 OPP 表句柄或寄存器基址) |
开发提示: 在驱动中访问
policy->cpus 可以判断当前调频操作是否影响多个 CPU。对于 DSU(DynamIQ)架构,一个 policy 可能管理 2~8 个 CPU,此时 related_cpus 与 cpus 的差异可能指示硬件上存在频率耦合但软件上允许独立控制的 CPU。
4.2 cpufreq_driver:硬件操作的“抽象层”
cpufreq_driver 是驱动开发者需要实现的核心结构体。它定义了 CPUFreq 子系统与硬件交互的所有回调函数。每个注册的 cpufreq_driver 实例代表一种频率控制硬件方案(如 cpufreq-dt、acpi-cpufreq、intel_pstate)。
struct cpufreq_driver {
const char *name;
unsigned int flags;
/* 必需回调 */
int (*init)(struct cpufreq_policy *policy);
int (*verify)(struct cpufreq_policy_data *policy);
int (*setpolicy)(struct cpufreq_policy *policy);
int (*target)(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation);
unsigned int (*get)(unsigned int cpu);
/* 可选回调 */
int (*exit)(struct cpufreq_policy *policy);
int (*suspend)(struct cpufreq_policy *policy);
int (*resume)(struct cpufreq_policy *policy);
int (*online)(struct cpufreq_policy *policy);
int (*offline)(struct cpufreq_policy *policy);
void (*update_limits)(struct cpufreq_policy *policy);
};
| 回调 | 必要性 | 触发时机与作用 |
|---|---|---|
init |
必需 | policy 初始化时调用,填充频率表、设置 policy->min/max、分配驱动私有数据 |
verify |
必需 | 验证并修正频率范围,确保 min <= max 且频率在硬件支持范围内 |
target |
必需 | 设置目标频率,relation 参数指定取整策略(向上/向下/最近) |
setpolicy |
可选 | 直接设置硬件策略(仅用于硬件自带调频策略的驱动,如 intel_pstate) |
get |
必需 | 读取当前 CPU 的实际运行频率(kHz) |
exit |
可选 | policy 销毁时清理驱动私有数据 |
suspend/resume |
可选 | 系统休眠/唤醒时保存/恢复频率状态 |
常见陷阱:
target 回调中不要直接调用 cpufreq_update_util 或触发 governor 的更新,否则可能造成递归调用。正确的做法是只操作硬件寄存器,频率更新后的通知由 CPUFreq 核心层自动完成。
4.3 cpufreq_governor:频率决策的“算法引擎”
cpufreq_governor 定义了频率调节的决策逻辑。每个 governor 通过注册该结构体来接入 CPUFreq 框架。内核内置了 performance、powersave、userspace、ondemand、conservative、schedutil 等 governor。
struct cpufreq_governor {
const char *name;
unsigned int flags;
/* 状态机回调 */
int (*init)(struct cpufreq_policy *policy);
void (*exit)(struct cpufreq_policy *policy);
int (*start)(struct cpufreq_policy *policy);
void (*stop)(struct cpufreq_policy *policy);
void (*limits)(struct cpufreq_policy *policy);
/* 频率调整回调(仅用于非 schedutil 类 governor) */
void (*dbs_update)(struct cpufreq_policy *policy);
unsigned int (*dbs_target)(struct cpufreq_policy *policy,
unsigned int target_freq);
};
| 回调 | 描述 |
|---|---|
init |
governor 绑定到 policy 时调用,分配 governor 私有数据(如采样率定时器) |
start |
governor 开始工作时调用,启动采样定时器或注册利用率回调 |
stop |
governor 停止工作时调用,停止定时器或注销回调 |
limits |
policy 的 min/max 发生变化时调用,governor 据此调整内部状态 |
dbs_update |
周期性采样回调(用于 ondemand/conservative),更新 CPU 负载统计 |
dbs_target |
根据负载计算目标频率(用于 ondemand/conservative) |
架构演进: 从内核 4.7 开始,
schedutil governor 不再使用 dbs_* 回调,而是直接通过 schedutil_update_util 从调度器获取利用率信号。这使得频率调节延迟从毫秒级降低到微秒级,是 DVFS 实时性的重要提升。
4.4 三大结构体的协作关系
下图展示了 cpufreq_policy、cpufreq_driver 和 cpufreq_governor 在频率调节工作流中的协作关系:
4.5 关键字段的初始化时机与生命周期
理解字段何时被赋值、何时被读取,对驱动调试至关重要。下表总结了三个结构体中关键字段的初始化时机:
| 结构体 | 字段 | 初始化者 | 初始化时机 |
|---|---|---|---|
| cpufreq_policy | cpus |
CPUFreq 核心层 | policy 创建时,根据 CPU 拓扑自动设置 |
| cpufreq_policy | min/max |
驱动 init 回调 |
驱动初始化时从 OPP 表或硬件寄存器读取 |
| cpufreq_policy | governor |
CPUFreq 核心层 | 用户写入 scaling_governor 或内核默认 governor 绑定 |
| cpufreq_policy | driver_data |
驱动 init 回调 |
驱动私有数据分配后赋值 |
| cpufreq_driver | name |
驱动开发者 | 编译时静态初始化 |
| cpufreq_driver | flags |
驱动开发者 | 编译时静态初始化(如 CPUFREQ_ASYNC_NOTIFICATION) |
| cpufreq_governor | name |
governor 开发者 | 编译时静态初始化 |
| cpufreq_governor | governor_data |
governor init 回调 |
governor 绑定到 policy 时分配 |
调试技巧: 在
/sys/devices/system/cpu/cpuX/cpufreq/ 目录下可以查看 policy 的运行时状态:scaling_cur_freq 对应 policy->cur,scaling_min_freq 和 scaling_max_freq 对应 policy->min 和 policy->max。如果 scaling_cur_freq 始终为 0,说明驱动 get 回调未正确实现。
4.6 结构体之间的指针关系图
下面的 SVG 图展示了三个结构体在内存中的指针连接关系,帮助理解数据流向:
4.7 驱动开发中的字段使用要点
- 不要直接修改
policy->cur:该字段由 CPUFreq 核心层在驱动target回调返回后自动更新。驱动只需在get回调中返回硬件实际频率即可。 - 使用
policy->transition_lock保护临界区:如果驱动在target回调中需要访问共享寄存器,应使用mutex_lock(&policy->transition_lock)确保原子性。 - 正确设置
policy->cpus:对于 DSU 或集群架构,如果多个 CPU 共享同一电压域,必须通过cpumask_set_cpu将所有受影响 CPU 加入cpus掩码,否则调频操作可能遗漏某些 CPU。 - 利用
driver_data传递平台信息:在init回调中分配结构体(如struct cpufreq_dt_private),将 OPP 表句柄、regmap、时钟句柄等存入其中,避免使用全局变量。 - governor 的
limits回调不可忽略:当用户通过 sysfs 修改scaling_min_freq或scaling_max_freq时,limits回调会被触发。governor 必须在此回调中重新评估当前频率是否超出新范围,必要时立即调频。
实战案例: 在
cpufreq-dt 驱动中,init 回调通过 dev_pm_opp_of_get_sharing_cpus() 获取共享同一 OPP 表的 CPU 掩码,然后赋值给 policy->cpus。如果该函数返回错误,驱动会退而使用 cpumask_of(policy->cpu) 仅管理单个 CPU。这一做法确保了 DSU 架构下频率调节的正确性。