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 在频率调节工作流中的协作关系:

CPUFreq 三大核心结构体协作关系 cpufreq_policy 上下文容器:频率范围、CPU 集合、状态锁 cpufreq_driver 硬件操作:init/target/get cpufreq_governor 决策算法:start/stop/limits policy->driver policy->governor 典型调频工作流 1. Governor 检测到负载变化,调用 policy->governor->dbs_target() 2. Governor 计算目标频率,调用 cpufreq_driver_target(policy, target_freq) 3. CPUFreq 核心层获取 policy->transition_lock,防止并发 4. 调用 policy->driver->target() 设置硬件频率 5. 更新 policy->cur,释放锁,通知相关子系统

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 图展示了三个结构体在内存中的指针连接关系,帮助理解数据流向:

结构体指针关系与数据流向 cpufreq_policy governor → 0xFFFF driver → 0xFFF0 driver_data → 0x1234 cpufreq_driver target() get() init() cpufreq_governor start() stop() limits() policy->driver policy->governor driver_data 指向 OPP 表或寄存器基址

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 架构下频率调节的正确性。