Skip to content

Latest commit

 

History

History
3288 lines (2586 loc) · 120 KB

File metadata and controls

3288 lines (2586 loc) · 120 KB

Linux 内核 Thermal 热管理子系统深度分析

目录

  1. 框架概述
  2. 整体架构
  3. 核心数据结构
  4. Trip Point 机制
  5. Governor 算法详解
  6. CPU Cooling Device
  7. devfreq Cooling Device
  8. Intel 平台集成
  9. ACPI Thermal Zone 集成
  10. IPA 智能功率分配算法
  11. hwmon 与 Thermal 的关系
  12. sysfs 接口与用户空间
  13. Netlink 事件通知
  14. Device Tree 集成
  15. 调试与追踪
  16. 关键流程分析
  17. 总结
  18. 用户阈值机制(User Threshold)
  19. 温度感知工作原理与驱动实现
  20. 其他 Cooling Device 类型
  21. Debugfs 深度解析
  22. Thermal 子系统初始化序列
  23. 暂停与恢复处理
  24. 并发与锁机制
  25. 能量模型(Energy Model)与 Thermal 的关系
  26. 实际平台案例:Rockchip TSADC
  27. 温度线性补偿:slope 与 offset
  28. Thermal 子系统常见问题与调优
  29. 内核配置选项(Kconfig)全解析
  30. 完整数据流图

1. 框架概述

Linux Thermal 热管理子系统(Thermal Management Subsystem)自 2008 年由 Intel 工程师 Zhang Rui 引入,提供了一套通用的、与硬件无关的热管理框架。其核心目标是:在系统温度过高时,通过控制各类冷却设备(风扇、CPU 频率限制等)将温度维持在安全范围内,同时尽量保留系统性能。

源码入口文件:

  • drivers/thermal/thermal_core.c:核心框架,注册/注销 thermal zone 和 cooling device
  • include/linux/thermal.h:公开 API 和数据结构定义
  • include/uapi/linux/thermal.h:用户空间可见的数据结构和枚举
  • drivers/thermal/thermal_core.h:内核内部结构体(thermal_zone_devicethermal_governor 等)

框架的四个核心概念:

概念 说明
Thermal Zone 一个可监测温度的逻辑区域(如 CPU 包、GPU、电池等)
Cooling Device 一个可降温的设备(如 CPU 频率限制、风扇)
Trip Point 触发冷却动作的温度阈值
Governor 决定如何调节冷却设备的策略算法

2. 整体架构

+-----------------------------------------------------------+
|                   用户空间 / 工具层                         |
|   thermald    lm-sensors    /sys/class/thermal/           |
+-------------------+-------------------+-------------------+
                    |                   |
          sysfs接口层             Netlink Generic
    (thermal_sysfs.c)           (thermal_netlink.c)
                    |
+-------------------v-------------------------------------------+
|                  Thermal 核心框架 (thermal_core.c)              |
|                                                               |
|  +------------------+   +-------------------+                |
|  | thermal_tz_list  |   | thermal_cdev_list |                |
|  | (thermal zones)  |   | (cooling devices) |                |
|  +------------------+   +-------------------+                |
|          |                        |                          |
|  +-------+--------+    +----------+---------+               |
|  | thermal_zone   |    | thermal_cooling    |               |
|  | _device_update |    | _device_register   |               |
|  +-------+--------+    +--------------------+               |
|          |                                                   |
|  +-------v--------+                                         |
|  |   Governor     |   step_wise / power_allocator /         |
|  |   管理层       |   bang_bang / fair_share                 |
|  +----------------+                                         |
+---------------------------------------------------------------+
          |                            |
+---------v-----------+   +-----------v-------------+
| Thermal Zone Drivers|   | Cooling Device Drivers   |
|                     |   |                          |
| ACPI thermal zone   |   | cpufreq_cooling.c        |
| Intel pkg_temp      |   | devfreq_cooling.c        |
| Rockchip, ARM...    |   | intel_powerclamp.c       |
| ARM SCMI, DT-based  |   | pcie_cooling.c           |
+---------------------+   +--------------------------+
          |
+---------v-----------+
| hwmon 接口层         |
| (thermal_hwmon.c)   |
| /sys/class/hwmon/   |
+---------------------+

全局链表(thermal_core.c 第 33-35 行):

static LIST_HEAD(thermal_tz_list);       // 所有 thermal zone 的链表
static LIST_HEAD(thermal_cdev_list);     // 所有 cooling device 的链表
static LIST_HEAD(thermal_governor_list); // 所有 governor 的链表

两把全局锁:

  • thermal_list_lock:保护 thermal_tz_listthermal_cdev_list
  • thermal_governor_lock:保护 thermal_governor_list

3. 核心数据结构

3.1 thermal_zone_device

定义于 drivers/thermal/thermal_core.h,第 119-156 行:

struct thermal_zone_device {
    int id;                           // 唯一 ID,用于 /sys/class/thermal/thermal_zoneN
    char type[THERMAL_NAME_LENGTH];   // 类型字符串(最大 20 字节)
    struct device device;             // 内嵌设备模型对象
    struct completion removal;        // 注销完成量
    struct completion resume;         // 恢复完成量
    struct attribute_group trips_attribute_group;
    struct list_head trips_high;      // 温度还未到达的 trip 点(有序链表)
    struct list_head trips_reached;   // 温度已超过的 trip 点(有序链表)
    struct list_head trips_invalid;   // 温度无效的 trip 点
    enum thermal_device_mode mode;    // ENABLED / DISABLED
    void *devdata;                    // 驱动私有数据
    int num_trips;                    // trip point 数量
    unsigned long passive_delay_jiffies; // passive 冷却轮询间隔
    unsigned long polling_delay_jiffies; // 普通轮询间隔(0 表示中断驱动)
    unsigned long recheck_delay_jiffies; // 温度读取失败后重试延迟
    int temperature;                  // 当前温度(毫摄氏度)
    int last_temperature;             // 上次温度
    int emul_temperature;             // 仿真温度(CONFIG_THERMAL_EMULATION)
    int passive;                      // 当前处于被动冷却的 trip 计数
    struct thermal_zone_device_ops ops; // 驱动回调函数集
    struct thermal_zone_params *tzp;  // 参数(governor 名、PID 参数等)
    struct thermal_governor *governor; // 当前使用的 governor
    void *governor_data;              // governor 私有数据
    struct ida ida;                   // 分配 cooling device 绑定 ID
    struct mutex lock;                // 保护 thermal_instances 链表
    struct list_head node;            // 挂载到 thermal_tz_list
    struct delayed_work poll_queue;   // 轮询工作队列
    enum thermal_notify_event notify_event;
    u8 state;                         // TZ_STATE_READY / SUSPENDED / RESUMING 等
    struct list_head user_thresholds; // 用户空间阈值列表
    struct thermal_trip_desc trips[]; // 柔性数组,存储所有 trip 描述符
};

trips 是一个柔性数组(flexible array),在注册时根据 num_trips 动态分配。

状态标志(thermal_core.h 第 66-71 行):

#define TZ_STATE_FLAG_SUSPENDED  BIT(0)
#define TZ_STATE_FLAG_RESUMING   BIT(1)
#define TZ_STATE_FLAG_INIT       BIT(2)
#define TZ_STATE_FLAG_EXIT       BIT(3)
#define TZ_STATE_READY           0

3.2 thermal_cooling_device

定义于 include/linux/thermal.h,第 123-139 行:

struct thermal_cooling_device {
    int id;                          // 唯一 ID,对应 cooling_deviceN
    const char *type;                // 冷却设备类型字符串
    unsigned long max_state;         // 最大冷却状态
    struct device device;            // 内嵌设备模型
    struct device_node *np;          // 设备树节点(可为 NULL)
    void *devdata;                   // 驱动私有数据
    void *stats;                     // 状态统计(用于调试)
    const struct thermal_cooling_device_ops *ops; // 操作回调
    bool updated;                    // true 表示无需更新
    struct mutex lock;               // 保护 thermal_instances 链表
    struct list_head thermal_instances; // 绑定的 thermal_instance 链表
    struct list_head node;           // 挂载到 thermal_cdev_list
};

冷却设备操作回调(include/linux/thermal.h 第 114-121 行):

struct thermal_cooling_device_ops {
    int (*get_max_state)(struct thermal_cooling_device *, unsigned long *);
    int (*get_cur_state)(struct thermal_cooling_device *, unsigned long *);
    int (*set_cur_state)(struct thermal_cooling_device *, unsigned long);
    // 以下三个为 IPA(power_allocator governor)扩展接口
    int (*get_requested_power)(struct thermal_cooling_device *, u32 *);
    int (*state2power)(struct thermal_cooling_device *, unsigned long, u32 *);
    int (*power2state)(struct thermal_cooling_device *, u32, unsigned long *);
};

冷却状态(cooling state)是从 0 到 max_state 的整数。0 表示不冷却(最高性能),max_state 表示最强冷却(最低性能)

3.3 thermal_tripthermal_trip_desc

Trip point 的公开表示(include/linux/thermal.h 第 72-78 行):

struct thermal_trip {
    int temperature;         // 触发温度(毫摄氏度)
    int hysteresis;          // 迟滞值(毫摄氏度),防止频繁切换
    enum thermal_trip_type type; // 类型:ACTIVE/PASSIVE/HOT/CRITICAL
    u8 flags;                // THERMAL_TRIP_FLAG_RW_TEMP | THERMAL_TRIP_FLAG_RW_HYST
    void *priv;              // 驱动私有数据
};

内核内部使用 thermal_trip_descdrivers/thermal/thermal_core.h 第 31-37 行):

struct thermal_trip_desc {
    struct thermal_trip trip;           // 公开的 trip 信息
    struct thermal_trip_attrs trip_attrs; // sysfs 属性
    struct list_head list_node;         // 挂入有序链表(trips_high / trips_reached)
    struct list_head thermal_instances; // 绑定到此 trip 的 cooling device 实例
    int threshold;                      // 当前有效阈值(考虑 hysteresis 后)
};

两个有序链表 trips_hightrips_reached 的维护逻辑:

  • 当温度低于 trip 的 temperature 时,trip 在 trips_high 中,threshold = trip.temperature
  • 当温度高于等于 trip 的 temperature 时,trip 移入 trips_reachedthreshold = trip.temperature - trip.hysteresis(利用迟滞防止频繁切换)

3.4 thermal_governor

定义于 drivers/thermal/thermal_core.h,第 53-64 行:

struct thermal_governor {
    const char *name;
    int (*bind_to_tz)(struct thermal_zone_device *tz);
    void (*unbind_from_tz)(struct thermal_zone_device *tz);
    void (*trip_crossed)(struct thermal_zone_device *tz,
                         const struct thermal_trip *trip,
                         bool upward);      // trip 被穿越时回调
    void (*manage)(struct thermal_zone_device *tz); // 温度更新时调用
    void (*update_tz)(struct thermal_zone_device *tz,
                      enum thermal_notify_event reason);
    struct list_head governor_list;
};

3.5 thermal_zone_params

用于在注册 thermal zone 时传入配置参数(include/linux/thermal.h 第 145-192 行):

struct thermal_zone_params {
    const char *governor_name;   // 指定使用的 governor 名称
    bool no_hwmon;               // 是否禁用 hwmon 接口
    u32 sustainable_power;       // 热区可持续耗散功率(mW),IPA 使用
    s32 k_po;   // PID 超调比例系数(temperature > control_temp 时)
    s32 k_pu;   // PID 欠调比例系数(temperature < control_temp 时)
    s32 k_i;    // PID 积分系数
    s32 k_d;    // PID 微分系数
    s32 integral_cutoff; // 积分截止误差
    int slope;           // 温度线性调整斜率
    int offset;          // 温度线性调整偏移
};

3.6 thermal_instance

连接 thermal zone(的一个 trip point)与 cooling device 的绑定对象,由 thermal_bind_cdev_to_trip() 创建(thermal_core.c 第 847-857 行):

dev->cdev = cdev;
dev->trip = &td->trip;
dev->upper = cool_spec->upper;   // 允许的最高冷却状态
dev->upper_no_limit = upper_no_limit;
dev->lower = cool_spec->lower;   // 允许的最低冷却状态
dev->target = THERMAL_NO_TARGET; // 当前目标状态(-1UL 表示无目标)
dev->weight = cool_spec->weight; // 权重(IPA 中使用)

3.7 thermal_notify_event 枚举

定义于 include/linux/thermal.h 第 45-62 行,描述触发热区更新的原因:

enum thermal_notify_event {
    THERMAL_EVENT_UNSPECIFIED,              // 未指定事件
    THERMAL_EVENT_TEMP_SAMPLE,             // 新温度采样(轮询触发)
    THERMAL_TRIP_VIOLATED,                 // Trip Point 被穿越
    THERMAL_TRIP_CHANGED,                  // Trip 参数(温度/迟滞)被修改
    THERMAL_DEVICE_DOWN,                   // 热区设备下线
    THERMAL_DEVICE_UP,                     // 热区设备上线(从 DOWN 恢复)
    THERMAL_DEVICE_POWER_CAPABILITY_CHANGED, // 功率能力变化(IPA 重新计算)
    THERMAL_TABLE_CHANGED,                 // 热区频率/功率表变化
    THERMAL_EVENT_KEEP_ALIVE,             // 要求用户空间 handler 响应(保活)
    THERMAL_TZ_BIND_CDEV,                 // cooling device 绑定到热区
    THERMAL_TZ_UNBIND_CDEV,               // cooling device 解绑
    THERMAL_INSTANCE_WEIGHT_CHANGED,       // thermal_instance 权重被修改
    THERMAL_TZ_RESUME,                     // 系统从睡眠中恢复
    THERMAL_TZ_ADD_THRESHOLD,             // 添加用户阈值
    THERMAL_TZ_DEL_THRESHOLD,             // 删除用户阈值
    THERMAL_TZ_FLUSH_THRESHOLDS,          // 清除所有用户阈值
};

这个枚举贯穿了整个热管理框架,每次调用 __thermal_zone_device_update() 都需要传入事件类型,便于 governor 和调试系统记录触发原因。


4. Trip Point 机制

4.1 Trip Point 类型

enum thermal_trip_type 定义于 include/uapi/linux/thermal.h 第 14-19 行:

enum thermal_trip_type {
    THERMAL_TRIP_ACTIVE   = 0,  // 主动冷却(如风扇)
    THERMAL_TRIP_PASSIVE  = 1,  // 被动冷却(如 CPU 降频)
    THERMAL_TRIP_HOT      = 2,  // 热点通知(通知用户空间)
    THERMAL_TRIP_CRITICAL = 3,  // 临界点(系统关机)
};

各类型的详细说明:

类型 触发动作 Governor 处理
THERMAL_TRIP_ACTIVE 驱动主动冷却设备(风扇) step_wise、bang_bang 处理
THERMAL_TRIP_PASSIVE 限制设备性能(CPU/GPU 降频) step_wise、power_allocator 处理;tz->passive 计数递增
THERMAL_TRIP_HOT 通知用户空间 + 调用 tz->ops.hot() 不由 governor 处理,在 handle_critical_trips() 中直接处理
THERMAL_TRIP_CRITICAL 调用 thermal_zone_device_critical() 触发系统关机 不由 governor 处理,直接调用 __hw_protection_trigger()

4.2 Trip Point 穿越检测

温度更新函数 __thermal_zone_device_update()thermal_core.c 第 609-657 行)是核心控制循环:

void __thermal_zone_device_update(struct thermal_zone_device *tz,
                                  enum thermal_notify_event event)
{
    struct thermal_governor *governor = thermal_get_tz_governor(tz);
    int low = -INT_MAX, high = INT_MAX;
    int temp, ret;

    // 检查状态
    if (tz->state != TZ_STATE_READY || tz->mode != THERMAL_DEVICE_ENABLED)
        return;

    // 读取当前温度
    ret = __thermal_zone_get_temp(tz, &temp);
    // ...错误处理...

    tz->last_temperature = tz->temperature;
    tz->temperature = temp;

    // 检测 trip 穿越(核心逻辑)
    thermal_zone_handle_trips(tz, governor, &low, &high);

    // 处理用户空间阈值
    thermal_thresholds_handle(tz, &low, &high);

    // 通知硬件下一个中断阈值(interrupt-driven 模式)
    thermal_zone_set_trips(tz, low, high);

    // 调用 governor 的 manage 回调
    if (governor->manage)
        governor->manage(tz);

monitor:
    monitor_thermal_zone(tz); // 调度下一次轮询
}

thermal_zone_handle_trips() 遍历两个有序链表,检测是否有 trip 被穿越(第 561-607 行)。

4.3 HOT 和 CRITICAL 的特殊处理

临界温度处理(thermal_core.c 第 372-412 行):

void thermal_zone_device_critical(struct thermal_zone_device *tz)
{
    thermal_zone_device_halt(tz, HWPROT_ACT_DEFAULT);
}

// 三种关机动作:
void thermal_zone_device_critical_shutdown(...)   // 正常关机
void thermal_zone_device_critical_reboot(...)     // 重启

static void thermal_zone_device_halt(struct thermal_zone_device *tz,
                                     enum hw_protection_action action)
{
    int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS;
    const char *msg = "Temperature too high";
    dev_emerg(&tz->device, "%s: critical temperature reached\n", tz->type);
    __hw_protection_trigger(msg, poweroff_delay_ms, action);
}

4.4 Hysteresis(迟滞)机制

迟滞防止在 trip 温度附近振荡。当温度超过 trip.temperature 触发冷却后,只有当温度降至 trip.temperature - trip.hysteresis 时才会解除冷却。

threshold 字段(thermal_trip_desc.threshold)的更新逻辑(thermal_core.c 第 435-447 行):

static void move_to_trips_high(struct thermal_zone_device *tz,
                                struct thermal_trip_desc *td) {
    td->threshold = td->trip.temperature;         // 触发阈值 = trip 温度
    move_trip_to_sorted_list(td, &tz->trips_high);
}

static void move_to_trips_reached(struct thermal_zone_device *tz,
                                   struct thermal_trip_desc *td) {
    td->threshold = td->trip.temperature - td->trip.hysteresis; // 解除阈值
    move_trip_to_sorted_list(td, &tz->trips_reached);
}

4.5 Trip Point 标志位

include/linux/thermal.h 第 80-81 行定义了两个 trip 标志位:

#define THERMAL_TRIP_FLAG_RW_TEMP   BIT(0)  // 温度值可由用户空间修改
#define THERMAL_TRIP_FLAG_RW_HYST   BIT(1)  // 迟滞值可由用户空间修改

设备树注册的 trip point 默认设置 THERMAL_TRIP_FLAG_RW_TEMPthermal_of.c:89 行),允许用户运行时调整温度阈值,而不需要重新编译驱动。

对于临界 trip(CRITICAL 类型),驱动通常设置 RW_TEMP 标志,防止用户随意提高临界温度导致硬件损坏。

4.6 有序链表的维护细节

trips_hightrips_reached 均为按 threshold 升序排列的链表。thermal_zone_handle_trips() 利用有序性进行早期退出优化:

  • 遍历 trips_high 时:若当前 trip 的 threshold > temperature,由于链表升序,后续所有 trip 的 threshold 也都 > temperature,直接停止遍历
  • 遍历 trips_reached 时:若当前 trip 的 threshold <= temperature,说明温度仍在 threshold 以上,继续遍历

这种设计使得在常温状态下(绝大多数 trip 未触达),链表遍历的复杂度接近 O(1)。

trips_high 链表(升序,threshold = trip.temperature):
  [60000] --> [75000] --> [90000] --> [110000]
  当 temperature=65000 时,60000 节点被穿越,移入 trips_reached:

trips_reached 链表(升序,threshold = trip.temperature - hysteresis):
  [58000] (60000 - 2000 hysteresis)
  trips_high:
  [75000] --> [90000] --> [110000]

5. Governor 算法详解

5.1 Governor 注册机制

所有 governor 通过宏 THERMAL_GOVERNOR_DECLARE 注册到专用 ELF section(thermal_core.h 第 194-198 行):

#define THERMAL_TABLE_ENTRY(table, name)             \
    static typeof(name) *__thermal_table_entry_##name \
    __used __section("__" #table "_thermal_table") = &name

#define THERMAL_GOVERNOR_DECLARE(name)  THERMAL_TABLE_ENTRY(governor, name)

内核初始化时 thermal_register_governors() 遍历该 section 注册所有 governor(thermal_core.c 第 232-259 行)。

默认 governor 由内核配置决定(thermal_core.h 第 175-185 行):

#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
#define DEFAULT_THERMAL_GOVERNOR "power_allocator"
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_BANG_BANG)
#define DEFAULT_THERMAL_GOVERNOR "bang_bang"
// ...
#endif

5.2 step_wise Governor

源码drivers/thermal/gov_step_wise.c

step_wise 是最经典的 governor,实现了**逐步调节(one step at a time)**策略。

核心逻辑(第 32-79 行):

温度 >= trip 阈值(throttle = true):
  趋势 RISING  →  冷却状态 += 1(加强冷却)
  趋势 DROPPING →  冷却状态 -= 1(但保持 >= lower + 1,继续降温)

温度 < trip 阈值(throttle = false):
  趋势 RISING  →  保持当前状态
  趋势 DROPPING →  冷却状态 = lower(最小冷却)或 THERMAL_NO_TARGET(停止冷却)

关键函数 get_target_state()(第 32-79 行):

static unsigned long get_target_state(struct thermal_instance *instance,
                                      enum thermal_trend trend, bool throttle)
{
    struct thermal_cooling_device *cdev = instance->cdev;
    unsigned long cur_state;

    cdev->ops->get_cur_state(cdev, &cur_state);

    if (!instance->initialized) {
        if (throttle)
            return clamp(cur_state + 1, instance->lower, instance->upper);
        return THERMAL_NO_TARGET;
    }

    if (throttle) {
        if (trend == THERMAL_TREND_RAISING)
            return clamp(cur_state + 1, instance->lower, instance->upper);
        if (trend == THERMAL_TREND_DROPPING)
            return clamp(cur_state - 1,
                         min(instance->lower + 1, instance->upper),
                         instance->upper);
    } else if (trend == THERMAL_TREND_DROPPING) {
        if (cur_state <= instance->lower)
            return THERMAL_NO_TARGET;
        return instance->lower;
    }

    return instance->target;
}

step_wise_manage() 排除了 HOT 和 CRITICAL 类型的 trip(第 131-146 行):

for_each_trip_desc(tz, td) {
    const struct thermal_trip *trip = &td->trip;
    if (trip->temperature == THERMAL_TEMP_INVALID ||
        trip->type == THERMAL_TRIP_CRITICAL ||
        trip->type == THERMAL_TRIP_HOT)
        continue;
    thermal_zone_trip_update(tz, td, td->threshold);
}

温度趋势(thermal_trend)通过驱动提供的 ops.get_trend() 回调获取,若驱动未实现则比较 tz->temperaturetz->last_temperature

step_wise 状态机图:

温度趋势: RAISING(+) / DROPPING(-) / STABLE(=)
冷却强度:  0  1  2  3  4  ... max_state

场景1:温度持续上升,超过 trip
  [未超trip] --> [超trip, RAISING] --> [超trip, RAISING] --> ...
     state=0         state=1              state=2
     (增加1)        (增加1)

场景2:温度在 trip 附近振荡
  超trip, RISING  → state+1
  超trip, DROP    → state-1(但至少 lower+1)
  未超trip, DROP  → state=lower(或 THERMAL_NO_TARGET)

场景3:温度下降,低于 trip
  超trip, STABLE → 保持当前 state
  未超trip, DROP → state=lower

5.3 bang_bang Governor

源码drivers/thermal/gov_bang_bang.c

bang_bang 是最简单的二值调节器,冷却设备只有 ON/OFF 两种状态。

数学原理(注释中的图示,第 14-31 行):

            Fan:   OFF    ON

                          |
                          |
        trip_temp:  +---->+
                    |     |        ^
                    |     |        |
                    |     |   Temperature
(trip_temp - hyst): +<----+
                    |
  • 当温度超过 trip_temp → 打开风扇(state = 1)
  • 当温度低于 trip_temp - hysteresis → 关闭风扇(state = 0)

实现(第 63-78 行):

static void bang_bang_trip_crossed(struct thermal_zone_device *tz,
                                   const struct thermal_trip *trip,
                                   bool upward)
{
    const struct thermal_trip_desc *td = trip_to_trip_desc(trip);
    struct thermal_instance *instance;

    list_for_each_entry(instance, &td->thermal_instances, trip_node)
        bang_bang_set_instance_target(instance, upward);
        // upward=true  → target=1(ON)
        // upward=false → target=0(OFF)
}

bang_bang 使用 trip_crossed 回调(而非 manage),完全依赖 trip 穿越事件驱动,是事件驱动式 governor。

bang_bang_manage() 函数(第 80-110 行)只在首次运行或新绑定 cdev 后初始化未初始化的实例状态,之后 tz->governor_data 被设为 (void *)true 作为标志,后续调用直接返回。

5.4 fair_share Governor

源码drivers/thermal/gov_fair_share.c

fair_share 根据每个 trip 点超出温度的程度,按比例分配冷却状态。适用于多个 trip 点共享同一个冷却设备的场景,公平共享冷却资源。

throttle 公式(第 57-62 行注释):

new_state[i] = (trip_level / max_no_of_trips) * (weight[i] / total_weight) * max_state

三个参数含义:

  • P1 = max_state:冷却设备最大状态
  • P2 = weight[i] / total_weight:当前冷却设备在此 trip 中的权重占比
  • P3 = trip_level / max_no_of_trips:当前温度超过了多少个 trip(反映超温程度)

实现(第 63-94 行):

static void fair_share_throttle(struct thermal_zone_device *tz,
                                const struct thermal_trip_desc *td,
                                int trip_level)
{
    list_for_each_entry(instance, &td->thermal_instances, trip_node) {
        u64 dividend = trip_level;
        u32 divisor  = tz->num_trips;

        dividend *= cdev->max_state;
        if (total_weight) {
            dividend *= instance->weight;
            divisor  *= total_weight;
        } else {
            divisor *= nr_instances;   // 未设置权重时均等分配
        }
        instance->target = div_u64(dividend, divisor);
        thermal_cdev_update_nocheck(cdev);
    }
}

trip_levelget_trip_level() 计算,等于当前温度超过了多少个 trip 点的数量(第 18-42 行)。

5.5 user_space Governor

源码drivers/thermal/gov_user_space.c

user_space governor 将决策权完全交给用户空间。当温度穿越 trip point 时,通过 uevent 通知用户空间,由用户空间程序(如 thermald)读取温度并写入 cdev/cur_state 来控制冷却设备。

UEvent 发送(第 33-51 行):

static void user_space_trip_crossed(struct thermal_zone_device *tz,
                                    const struct thermal_trip *trip,
                                    bool upward)
{
    char *thermal_prop[5];

    thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", tz->type);
    thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", tz->temperature);
    thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=%d",
                                thermal_zone_trip_id(tz, trip));
    thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", tz->notify_event);
    thermal_prop[4] = NULL;
    kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop);
}

bind_to_tz() 回调(第 18-23 行)会打印一次提示,建议使用 Netlink 替代 UEvent:

static int user_space_bind(struct thermal_zone_device *tz)
{
    pr_info_once("Consider using thermal netlink events interface\n");
    return 0;
}

user_space governor 的使用场景:

  • thermald(Intel 热管理守护进程)
  • thermal-engine(Qualcomm 平台)
  • 自定义热管理脚本

用户空间程序通过轮询 /sys/class/thermal/thermal_zoneN/temp 读取温度,写入 /sys/class/thermal/cooling_deviceN/cur_state 直接控制冷却级别。


6. CPU Cooling Device

6.1 架构概览

源码drivers/thermal/cpufreq_cooling.c

CPU cooling device 通过限制 CPUFreq 最高频率实现降温。注册的设备名为 cpufreq-<cpu_name>

核心数据结构(第 68-79 行):

struct cpufreq_cooling_device {
    u32 last_load;                    // 最近一次获取的 CPU 负载(百分比)
    unsigned int cpufreq_state;       // 当前冷却状态
    unsigned int max_level;           // 最大冷却级别(频率档位数 - 1)
    struct em_perf_domain *em;        // 能量模型引用
    struct cpufreq_policy *policy;    // CPUFreq policy(一组相同频率的 CPU)
    struct thermal_cooling_device_ops cooling_ops;
#ifndef CONFIG_SMP
    struct time_in_idle *idle_time;   // UP 系统的空闲时间统计
#endif
    struct freq_qos_request qos_req;  // PM QoS 频率上限请求
};

6.2 冷却状态与频率的对应关系

关键设计(源码注释,第 29-40 行):

冷却状态(cooling state)与 CPUFreq 频率的对应关系:

最高冷却状态 = 最低可用频率

level 0  →  最高频率(最小冷却,最高性能)
level 1  →  第二高频率
level 2  →  第三高频率
...
level N  →  最低频率(最强冷却,最低性能)

冷却状态到频率的查找(get_state_freq() 第 389-420 行):

static unsigned int get_state_freq(struct cpufreq_cooling_device *cpufreq_cdev,
                                   unsigned long state)
{
#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR
    // 优先使用能量模型表
    if (cpufreq_cdev->em) {
        idx = cpufreq_cdev->max_level - state;  // 倒序索引
        freq = em_table[idx].frequency;
        return freq;
    }
#endif
    // 否则使用 CPUFreq 频率表(升序排列时同样倒序)
    if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING)
        idx = cpufreq_cdev->max_level - state;
    else
        idx = state;
    return policy->freq_table[idx].frequency;
}

6.3 频率设置机制

cpufreq_set_cur_state() 通过 PM QoS 限制频率上限(第 473-497 行):

static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
                                 unsigned long state)
{
    struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
    unsigned int frequency;
    int ret;

    if (state > cpufreq_cdev->max_level)
        return -EINVAL;

    if (cpufreq_cdev->cpufreq_state == state)
        return 0;

    frequency = get_state_freq(cpufreq_cdev, state);

    // 通过 freq_qos 更新最高频率约束
    ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency);
    if (ret >= 0) {
        cpufreq_cdev->cpufreq_state = state;
        ret = 0;
    }
    return ret;
}

这种设计的优点是:thermal 框架只通过 QoS 机制表达约束,CPUFreq 核心负责实际的频率切换,解耦合且安全。

6.4 CPU 负载与功率计算

当启用 CONFIG_THERMAL_GOV_POWER_ALLOCATOR 时,CPU cooling device 额外实现三个功率接口。

cpufreq_get_requested_power()(第 230-258 行):

static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev,
                                       u32 *power)
{
    unsigned long freq = cpufreq_quick_get(policy->cpu);
    u32 total_load = 0;

    // SMP 路径:使用调度器工具函数获取负载
    for_each_cpu(cpu, policy->related_cpus) {
        u32 load;
        if (cpu_online(cpu))
            // SMP: load = sched_cpu_util(cpu) * 100 / arch_scale_cpu_capacity(cpu)
            load = get_load(cpufreq_cdev, cpu, i);
        else
            load = 0;
        total_load += load;
    }

    cpufreq_cdev->last_load = total_load;
    *power = get_dynamic_power(cpufreq_cdev, freq);
    // 动态功率 = cpu_freq_to_power(freq) * last_load / 100
    return 0;
}

功率与状态转换(第 261-330 行):

  • cpufreq_state2power(state, &power):将冷却状态转为满负载功率(查能量模型表)
static int cpufreq_state2power(struct thermal_cooling_device *cdev,
                                unsigned long state, u32 *power)
{
    num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus);
    idx = cpufreq_cdev->max_level - state;
    freq = em_table[idx].frequency;
    *power = cpu_freq_to_power(cpufreq_cdev, freq) * num_cpus;
    return 0;
}
  • cpufreq_power2state(power, &state):将功率预算转为冷却状态(通过负载归一化)
static int cpufreq_power2state(struct thermal_cooling_device *cdev,
                               u32 power, unsigned long *state)
{
    u32 last_load = cpufreq_cdev->last_load ?: 1;
    u32 normalised_power = (power * 100) / last_load; // 归一化到 100% 负载
    u32 target_freq = cpu_power_to_freq(cpufreq_cdev, normalised_power);
    *state = get_level(cpufreq_cdev, target_freq);
    return 0;
}

6.5 注册流程

注册函数(第 514-611 行)__cpufreq_cooling_register() 的关键步骤:

  1. 统计 CPUFreq policy 中有效频率档位数 i
  2. 设置 max_level = i - 1
  3. 初始化基础 ops(get/set max/cur state)
  4. 若能量模型(EM)有效(em_is_sane() 检查),追加 IPA 功率 ops
  5. 通过 freq_qos_add_request() 注册 QoS 请求(初始无限制)
  6. 调用 thermal_of_cooling_device_register() 注册到 thermal 框架

两个公开 API:

  • cpufreq_cooling_register(policy):不带 DT 节点
  • of_cpufreq_cooling_register(policy):带 DT 节点,支持 EM 和 IPA

6.6 cpufreq cooling 与 CPUFreq 框架的关系

thermal_core
    |
    | set_cur_state(state=3)
    v
cpufreq_cooling
    |
    | freq_qos_update_request(qos_req, freq=1200MHz)
    v
PM QoS 框架(频率约束链)
    |
    | cpufreq_qos_notifier
    v
CPUFreq 核心
    |
    | -> policy->max = min(user_max, thermal_max)
    | -> __cpufreq_driver_target()
    v
CPUFreq 驱动(如 ACPI-cpufreq, cpufreq-dt)
    |
    | 写 MSR / MMIO 寄存器
    v
硬件:实际频率切换

这种分层设计的优势:当 thermal 解除限制时(设置 state=0),QoS 请求恢复为无限制,CPUFreq 可以立即恢复最高频率,而不需要 thermal 框架自己管理频率恢复。


7. devfreq Cooling Device

7.1 架构概览

源码drivers/thermal/devfreq_cooling.c

devfreq cooling 为使用 devfreq(动态频率调节框架)的设备提供热管理支持,主要用于 GPU、DDR 内存控制器、图像处理器等。

核心数据结构(第 50-62 行):

struct devfreq_cooling_device {
    struct thermal_cooling_device *cdev;
    struct thermal_cooling_device_ops cooling_ops;
    struct devfreq *devfreq;          // 关联的 devfreq 设备
    unsigned long cooling_state;      // 当前冷却状态
    u32 *freq_table;                  // 按冷却状态索引的频率表(降序)
    size_t max_state;                 // 最大状态数(OPP 数量 - 1)
    struct devfreq_cooling_power *power_ops; // 精确功率模型(可选)
    u32 res_util;                     // 资源利用率缩放因子(x100)
    int capped_state;                 // 动态功率预算下的冷却状态
    struct dev_pm_qos_request req_max_freq; // PM QoS 频率上限请求
    struct em_perf_domain *em_pd;     // 能量模型(可选)
};

res_util 的范围从 100(设备空闲)到 power × 100(满负载),乘以 100 是为了保留整数计算的精度(SCALE_ERROR_MITIGATION = 100,第 25 行)。

7.2 频率设置机制

devfreq_cooling_set_cur_state()(第 84-118 行):

static int devfreq_cooling_set_cur_state(struct thermal_cooling_device *cdev,
                                         unsigned long state)
{
    struct devfreq_cooling_device *dfc = cdev->devdata;
    unsigned long freq;

    if (dfc->em_pd) {
        perf_idx = dfc->max_state - state;  // 倒序:state 越大频率越低
        freq = em_table[perf_idx].frequency * 1000; // kHz -> Hz
    } else {
        freq = dfc->freq_table[state];      // 直接查频率表
    }

    // 通过 dev_pm_qos 限制设备最高频率
    dev_pm_qos_update_request(&dfc->req_max_freq,
                              DIV_ROUND_UP(freq, HZ_PER_KHZ));
    dfc->cooling_state = state;
    return 0;
}

7.3 功率计算

devfreq cooling 的功率计算考虑动态功耗模型 P = C × V² × f。电压通过 dev_pm_opp_get_voltage() 从 OPP 框架获取(get_voltage() 函数,第 148-174 行)。

static int devfreq_cooling_get_requested_power(...)
{
    // 获取 devfreq 设备的工作状态(busy_time / total_time)
    df->profile->get_dev_status(df->dev.parent, &status);
    _normalize_load(&status);  // 归一化到 1024

    if (dfc->power_ops && dfc->power_ops->get_real_power) {
        // 精确模型:驱动提供实际功率测量值
    } else if (dfc->em_pd) {
        // 能量模型:查表,按 busy_time 比例计算
        power = (em_table[perf_idx].power * status.busy_time)
                / (status.total_time * MICROWATT_PER_MILLIWATT);
    }
}

8. Intel 平台集成

8.1 x86 CPU 包温度传感器

源码drivers/thermal/intel/x86_pkg_temp_thermal.c

Intel CPU 提供了硬件温度传感器,通过 MSR 寄存器读取。x86_pkg_temp_thermal 驱动将这些传感器封装为 thermal zone。

关键特性(第 43-48 行):

/* MSR 只支持 2 个阈值中断 */
#define MAX_NUMBER_OF_TRIPS  2

/* 避免频繁通知的防抖延迟 */
#define PKG_TEMP_THERMAL_NOTIFY_DELAY  5000  // 5秒

核心数据结构(第 50-58 行):

struct zone_device {
    int cpu;
    bool work_scheduled;
    u32 msr_pkg_therm_low;   // IA32_PACKAGE_THERM_INTERRUPT 低阈值
    u32 msr_pkg_therm_high;  // IA32_PACKAGE_THERM_INTERRUPT 高阈值
    struct delayed_work work;
    struct thermal_zone_device *tzone;
    struct cpumask cpumask;
};

禁用 hwmon 接口(第 60-62 行):

static struct thermal_zone_params pkg_temp_tz_params = {
    .no_hwmon = true,  // 包温度不需要 hwmon 接口
};

温度读取基于Thermal Control Circuit(TCC)激活温度temperature = TCC_activation_temp - reading,通过 intel_tcc.c 提供的 API 读取。使用中断驱动模式(polling_delay = 0),硬件在温度越过 MSR 阈值时触发中断。

8.2 Intel Powerclamp(空闲注入冷却)

源码drivers/thermal/intel/intel_powerclamp.c

Powerclamp 通过**强制 CPU 进入 C 状态(idle injection)**来降低功耗和温度,而不是降低频率。这样可以在保持峰值频率的同时降温(适合突发性工作负载)。

设计参数(第 40-50 行):

#define MAX_TARGET_RATIO (100U)       // 最大空闲注入比例 100%
#define CONFIDENCE_OK (3)             // 校准置信度阈值
#define DEFAULT_DURATION_JIFFIES (6)  // 默认空闲注入持续时间(约 6ms)

冷却原理:

  • 冷却状态 N 表示将 N% 的 CPU 时间注入空闲(强制进入 C-state)
  • 通过 idle_inject 框架同步所有 CPU 进入空闲
  • 使用包级 C-state 计数器(MSR)监控实际效果,动态校准注入时间
  • CONFIDENCE_OK = 3 表示连续 3 次注入周期未被中断才认为校准有效

8.3 Intel PCH Thermal

源码drivers/thermal/intel/intel_pch_thermal.c

PCH(Platform Controller Hub)热传感器监测芯片组温度,通过 MMIO 寄存器读取。

8.4 Intel HFI(Hardware Feedback Interface)

源码drivers/thermal/intel/intel_hfi.c

Intel HFI 是 Alder Lake 及更新平台引入的硬件功能,CPU 通过 MMIO 共享内存区向 OS 报告每个 CPU 核心的**性能(performance)和能效(efficiency)**能力(0-255 的归一化值)。这些值随温度/功耗动态变化,供 Linux 调度器进行任务放置决策。

通过 Thermal Netlink 的 THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE 事件通知用户空间,THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCETHERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY 属性携带具体数值。

8.5 Intel RAPL(Running Average Power Limit)

RAPL 通过 MSR 接口(MSR_PKG_POWER_LIMITMSR_PP0_POWER_LIMIT 等)限制 CPU 包、核心、图形、内存子系统的功耗,是 Intel 平台功耗管理的重要组成部分。

RAPL 接口通过 PowerCap 框架(drivers/powercap/intel_rapl_common.c)暴露到用户空间(/sys/class/powercap/),虽然不直接使用 Thermal 框架,但与热管理密切相关:当 RAPL 限制功耗时,会直接影响 CPU 的发热量,是与 thermal framework 并行运行的功耗控制手段。


9. ACPI Thermal Zone 集成

9.1 架构概览

源码drivers/acpi/thermal.c

ACPI 规范定义了 Thermal Zone 对象,BIOS 通过 AML(ACPI Machine Language)描述热区配置。Linux 的 ACPI thermal 驱动将 ACPI 热区转换为内核 thermal zone,并将 ACPI 冷却设备(风扇、处理器)绑定为 cooling device。

9.2 核心数据结构(第 89-120 行)

struct acpi_thermal_trip {
    unsigned long temp_dk;          // 温度(十分之一开尔文)
    struct acpi_handle_list devices; // 关联的冷却设备列表
};

struct acpi_thermal_passive {
    struct acpi_thermal_trip trip;
    unsigned long tc1;   // 被动冷却时间常数 1(ACPI _TC1)
    unsigned long tc2;   // 被动冷却时间常数 2(ACPI _TC2)
    unsigned long delay; // 采样延迟(ACPI _TSP,单位毫秒)
};

struct acpi_thermal {
    struct acpi_device *device;
    unsigned long temp_dk;           // 当前温度(十分之一开尔文)
    unsigned long polling_frequency; // 轮询频率(ACPI _TZP,单位 1/10 秒)
    struct acpi_thermal_trips trips; // 所有 trip 点(passive + active x10)
    struct thermal_zone_device *thermal_zone; // 对应的内核 thermal zone
    int kelvin_offset;               // 开尔文转摄氏度偏移(毫摄氏度)
};

最多支持 10 个 active trip(_AC0_AC9),1 个 passive trip,1 个 hot trip,1 个 critical trip,共 ACPI_THERMAL_MAX_NR_TRIPS = 13 个(第 51 行)。

9.3 ACPI Thermal 方法映射

ACPI 规范定义了以下方法,驱动通过评估 AML 获取:

ACPI 方法 说明 对应内核概念
_TMP 当前温度(十分之一开尔文) ops.get_temp()
_CRT 临界温度(Critical) THERMAL_TRIP_CRITICAL
_HOT 热点温度(Hot Sleep/S4) THERMAL_TRIP_HOT
_PSV 被动冷却温度(Passive) THERMAL_TRIP_PASSIVE
_AC0~_AC9 主动冷却温度(Active fan) THERMAL_TRIP_ACTIVE
_PSL 被动冷却设备列表(处理器) cooling device 绑定
_AL0~_AL9 主动冷却设备列表(风扇) cooling device 绑定
_TZP 热区轮询频率(1/10 秒) polling_delay
_TC1, _TC2 被动冷却时间常数 传递给内核

9.4 通知机制

ACPI 热区支持三种通知(第 39-43 行):

#define ACPI_THERMAL_NOTIFY_TEMPERATURE  0x80  // 温度变化,触发重新评估
#define ACPI_THERMAL_NOTIFY_THRESHOLDS   0x81  // Trip 温度发生变化
#define ACPI_THERMAL_NOTIFY_DEVICES      0x82  // 冷却设备列表变化
// 厂商自定义通知(OEM-defined)
#define ACPI_THERMAL_NOTIFY_CRITICAL     0xF0
#define ACPI_THERMAL_NOTIFY_HOT          0xF1

9.5 ACPI 模块参数调整

ACPI thermal 驱动提供了多个内核模块参数(drivers/acpi/thermal.c 第 67-77 行):

static int act;
module_param(act, int, 0644);
MODULE_PARM_DESC(act, "Disable or override all lowest active trip points.");
// act=0: 禁用所有 active trip
// act>0: 覆盖所有 active trip 为此温度

static int crt;
module_param(crt, int, 0644);
MODULE_PARM_DESC(crt, "Disable or lower all critical trip points.");
// crt=-1: 禁用 critical trip(危险!)
// crt>0:  设置 critical trip 上限

static int tzp;
module_param(tzp, int, 0444);
MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.");
// 覆盖 _TZP 方法返回的轮询频率

这些参数允许系统管理员在不修改 BIOS 的情况下调整热管理行为,常用于诊断过热问题或规避 BIOS bug。

9.6 温度单位转换

ACPI 使用十分之一开尔文(decikelvin)作为温度单位,内核 thermal 框架使用毫摄氏度(milli-Celsius)。转换关系:

// ACPI 到毫摄氏度:
// kelvin_offset = 2731 * 100  (= 273.1 K * 1000 mK/K / 10 dK/K * 100)
// temp_mC = temp_dk * 100 - kelvin_offset
// 即:T(mC) = T(dK) * 100 - 273100

// 示例:
// 298.15 K = 25°C = 2981.5 dK ≈ 2982 dK (十分之一开尔文取整)
// 2982 * 100 - 273100 = 298200 - 273100 = 25100 mC = 25.1°C

10. IPA 智能功率分配算法

10.1 概述

源码drivers/thermal/gov_power_allocator.c

IPA(Intelligent Power Allocation,智能功率分配)是 ARM 贡献的 governor,也称为 power_allocator。它使用 PID 控制器计算总功率预算,然后按比例分配给各个冷却设备(power actor)。

IPA 的核心优势:

  • 基于功率(mW)而非离散状态(整数级别)进行控制,更物理化且精确
  • PID 控制提供更平滑的调节,避免 step_wise 可能出现的振荡
  • 支持多个冷却设备按权重公平共享功率预算
  • 适合 ARM 移动/嵌入式平台(大小核架构,多个 power domain)

10.2 核心数据结构(第 57-98 行)

// 每个 power actor 的功率信息
struct power_actor {
    u32 req_power;          // 实际请求功率(mW)
    u32 max_power;          // 最大可分配功率(当前 lower 冷却状态对应的功率)
    u32 granted_power;      // 分配到的功率(mW)
    u32 extra_actor_power;  // 额外可吸收的功率(max - granted)
    u32 weighted_req_power; // 加权后的请求功率(用于按比例分配)
};

// governor 私有参数
struct power_allocator_params {
    bool allocated_tzp;         // 是否动态分配了 tzp
    bool update_cdevs;          // 是否需要更新冷却设备
    s64 err_integral;           // PID 积分项累积值
    s32 prev_err;               // 上次误差(用于微分项)
    u32 sustainable_power;      // 热区可持续耗散功率(mW)
    const struct thermal_trip *trip_switch_on; // 第一个 passive trip(开关温度)
    const struct thermal_trip *trip_max;       // 最后一个 passive trip(目标控制温度)
    int total_weight;           // 所有 power actor 的权重之和
    unsigned int num_actors;    // 支持 IPA 接口的冷却设备数量
    unsigned int buffer_size;   // power 缓冲区大小
    struct power_actor *power;  // power actor 信息数组
};

10.3 PID 控制器(pid_controller(),第 239-299 行)

IPA 使用固定小数点算术(10 位精度,FRAC_BITS = 10)实现 PID 控制器,避免浮点运算:

#define FRAC_BITS 10
#define int_to_frac(x) ((x) << FRAC_BITS)
#define frac_to_int(x) ((x) >> FRAC_BITS)

static inline s64 mul_frac(s64 x, s64 y) { return (x * y) >> FRAC_BITS; }
static inline s64 div_frac(s64 x, s64 y) { return div_s64(x << FRAC_BITS, y); }

误差定义(第 252 行):

err = control_temp - tz->temperature;  // 目标温度 - 当前温度
// err > 0:温度低于目标,可以增加功率
// err < 0:超温,需要减少功率

PID 三项计算(第 255-286 行):

// P 项(比例):两个不同的比例系数区分超调/欠调
// k_po:err < 0(超调,温度超过目标)时的比例系数
// k_pu:err >= 0(欠调,温度低于目标)时的比例系数
p = mul_frac(err < 0 ? tz->tzp->k_po : tz->tzp->k_pu, err);

// I 项(积分):累积误差,仅在误差小于 integral_cutoff 时累积
// integral_cutoff 防止系统空闲时误累积正误差
i = mul_frac(tz->tzp->k_i, params->err_integral);
if (err < int_to_frac(tz->tzp->integral_cutoff)) {
    s64 i_next = i + mul_frac(tz->tzp->k_i, err);
    if (abs(i_next) < max_power_frac) {
        i = i_next;
        params->err_integral += err;
    }
}

// D 项(微分):误差变化率,除以采样周期(毫秒)使其时间无关
d = mul_frac(tz->tzp->k_d, err - params->prev_err);
d = div_frac(d, jiffies_to_msecs(tz->passive_delay_jiffies));
params->prev_err = err;

// 总功率预算 = 可持续功率(前馈)+ PID 修正
power_range = sustainable_power + frac_to_int(p + i + d);
power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power);

PID 参数自动估算(estimate_pid_constants() 第 149-184 行):

若驱动未配置 PID 参数,IPA 会根据 sustainable_power 和温度范围自动估算:

tz->tzp->k_po = int_to_frac(sustainable_power) / temperature_threshold;
tz->tzp->k_pu = int_to_frac(2 * sustainable_power) / temperature_threshold;
k_i = tz->tzp->k_pu / 10;
tz->tzp->k_i = k_i > 0 ? k_i : 1;
// k_d 和 integral_cutoff 默认为 0

10.4 功率分配算法(divvy_up_power(),第 350-403 行)

功率分配分两轮进行:

第一轮:按加权请求功率比例分配

granted[i] = power_range * weighted_req_power[i] / total_weighted_req_power

granted[i] > max_power[i],则限制为 max_power[i],并将超出部分记入 extra_power

第二轮:将超额功率重新分配给有余量的 actor

extra_range = extra_power * extra_actor_power[i] / total_extra_actor_power
granted[i] += extra_range

这确保了功率预算的充分利用,不会因为某个 actor 已满而浪费预算。

10.5 整体 allocate_power() 流程(第 406-483 行)

allocate_power(tz, control_temp)
    1. 遍历 trip_max 绑定的所有 cdev
       - get_requested_power()  →  pa->req_power(当前请求功率,mW)
       - state2power(lower)     →  pa->max_power(最大可分配功率,mW)
       - 计算 total_req_power, max_allocatable_power, total_weighted_req_power

    2. pid_controller(control_temp, max_allocatable_power)  →  power_range
       (PID 计算总功率预算)

    3. divvy_up_power(power, num_actors, total_weighted_req_power, power_range)
       (两轮分配:比例分配 + 超额重分配)

    4. 遍历所有 actor:
       power_actor_set_power(cdev, instance, pa->granted_power)
           cdev->ops->power2state(cdev, power, &state)
           instance->target = clamp(state, lower, upper)
           thermal_cdev_update_nocheck(cdev)
               cdev->ops->set_cur_state(cdev, target)

10.6 Trip Point 选择策略(get_governor_trips(),第 500-537 行)

// 存在多个 passive trip:
//   trip_switch_on = 第一个 passive trip(IPA 在此温度开始介入)
//   trip_max = 最后一个 passive trip(目标控制温度)
// 仅一个 passive trip:
//   trip_switch_on = NULL(IPA 始终开启)
//   trip_max = 该 passive trip
// 无 passive trip:使用最后一个 active trip 作为 trip_max

当温度低于 trip_switch_on 时,IPA 调用 allow_maximum_power() 让所有冷却设备以最大性能运行,PID 控制器重置(reset_pid_controller())。

10.7 IPA 的前提条件

要使用 IPA,冷却设备必须实现 power actor 接口(check_power_actors() 第 588-609 行):

  • get_requested_power():返回当前请求功率(mW)
  • state2power():将冷却状态转为功率(mW)
  • power2state():将功率转为冷却状态

不满足条件时,IPA 会打印警告:

dev_warn(&tz->device, "power_allocator: %s is not a power actor\n",
         instance->cdev->type);
return -EINVAL;

10.8 IPA 的 sustainable_power 配置

sustainable_power 是 IPA 工作的核心参数,表示热区在长期运行下可以持续散发的最大功率(单位 mW)。可通过以下途径配置:

  1. 设备树:在 thermal zone 节点中设置 sustainable-power 属性
  2. thermal_zone_params:驱动注册时直接传入 tzp->sustainable_power
  3. sysfs:通过 /sys/class/thermal/thermal_zoneN/sustainable_power 动态修改

若未配置,IPA 在 bind_to_tz() 时根据绑定的 cdev 自动估算(estimate_sustainable_power()):

// 自动估算:将所有 power actor 在 middle_state 的功率求和
for each power actor:
    state = (lower + upper) / 2       // 中间冷却状态
    state2power(cdev, state, &power)
    sustainable_power += power

11. hwmon 与 Thermal 的关系

11.1 双重接口设计

源码drivers/thermal/thermal_hwmon.c

Linux 提供了两套温度传感器的用户空间接口:

Thermal Subsystem          hwmon Subsystem
/sys/class/thermal/        /sys/class/hwmon/
thermal_zoneN/temp         hwmonN/temp1_input
                           hwmonN/temp1_crit

Thermal 框架通过 thermal_hwmon.c 桥接到 hwmon,让传统的 lm-sensors 工具也能读取 thermal zone 的温度。

11.2 核心数据结构(第 24-43 行)

// 每种 type 的 thermal zone 共享一个 hwmon 设备
struct thermal_hwmon_device {
    char type[THERMAL_NAME_LENGTH];  // thermal zone 的 type('-' 替换为 '_')
    struct device *device;           // hwmon 设备
    int count;                       // 共享此 hwmon 设备的 tz 数量
    struct list_head tz_list;        // 共享此 hwmon 的 tz 链表
    struct list_head node;           // 挂载到 thermal_hwmon_list
};

// 每个 thermal zone 的温度输入节点
struct thermal_hwmon_temp {
    struct list_head hwmon_node;
    struct thermal_zone_device *tz;
    struct thermal_hwmon_attr temp_input; // tempN_input(当前温度)
    struct thermal_hwmon_attr temp_crit;  // tempN_crit(临界温度,可选)
};

11.3 共享逻辑

同类型(相同 tz->type)的多个 thermal zone 共享同一个 hwmon 设备(thermal_hwmon_lookup_by_type() 第 91-109 行)。类型字符串中的 - 会被替换为 _(hwmon 命名约定要求使用下划线)。

11.4 禁用机制

驱动可以通过 thermal_zone_params.no_hwmon = true 禁用 hwmon 接口。例如:

// drivers/thermal/intel/x86_pkg_temp_thermal.c 第 60-62 行
static struct thermal_zone_params pkg_temp_tz_params = {
    .no_hwmon = true,
};

11.5 hwmon 属性

  • tempN_input:当前温度(毫摄氏度),通过 thermal_zone_get_temp() 读取
  • tempN_crit:临界温度(毫摄氏度),通过 tz->ops.get_crit_temp() 读取(仅当驱动实现该回调时暴露)

12. sysfs 接口与用户空间

12.1 Thermal Zone sysfs

源码drivers/thermal/thermal_sysfs.c

每个 thermal zone 在 /sys/class/thermal/thermal_zoneN/ 下暴露以下文件:

文件 权限 说明
type r Thermal zone 类型字符串
temp r 当前温度(毫摄氏度)
mode rw enabled / disabled
policy rw 当前 governor 名称
available_policies r 所有可用 governor 列表
trip_point_N_type r 第 N 个 trip 点类型
trip_point_N_temp rw* 第 N 个 trip 点温度(*可写表示支持动态修改)
trip_point_N_hyst rw* 第 N 个 trip 点迟滞值
emul_temp w 仿真温度(CONFIG_THERMAL_EMULATION)

温度读取(第 36-49 行):

static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
                         char *buf)
{
    struct thermal_zone_device *tz = to_thermal_zone(dev);
    int temperature, ret;

    ret = thermal_zone_get_temp(tz, &temperature);
    if (!ret)
        return sysfs_emit(buf, "%d\n", temperature);
    if (ret == -EAGAIN)
        return -ENODATA;  // 临时无数据,而非错误
    return ret;
}

Trip 温度动态修改(第 104-137 行):

trip_point_temp_store() 调用 thermal_zone_set_trip_temp() 并触发 __thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED),通知 governor 和用户空间。

修改 trip 温度的有效性检查:

// 防止整数溢出:温度不能 <= THERMAL_TEMP_INVALID + hysteresis
if (temp != THERMAL_TEMP_INVALID &&
    temp <= trip->hysteresis + THERMAL_TEMP_INVALID)
    return -EINVAL;

12.2 Cooling Device sysfs

每个 cooling device 在 /sys/class/thermal/cooling_deviceN/ 下暴露:

文件 权限 说明
type r 冷却设备类型字符串
max_state r 最大冷却状态
cur_state rw 当前冷却状态(governor 设定)
stats/time_in_state_ms r 各状态停留时间统计(调试)
stats/reset w 重置统计数据

12.3 Thermal Zone 与 Cooling Device 绑定的 sysfs

每个绑定关系(thermal_instance)在 thermal zone 的目录下创建(thermal_bind_cdev_to_trip() 第 864-888 行):

  • cdevN → symlink 到对应的 cooling device 设备目录
  • cdevN_trip_point → 该绑定对应的 trip 点索引(只读)
  • cdevN_weight → 该绑定的权重(读写,IPA 使用,默认 THERMAL_WEIGHT_DEFAULT = 0

12.4 Governor 切换

通过向 policy 文件写入 governor 名称切换(thermal_zone_device_set_policy() 函数,thermal_core.c 第 191-207 行):

echo "power_allocator" > /sys/class/thermal/thermal_zone0/policy
cat /sys/class/thermal/thermal_zone0/available_policies
# step_wise fair_share user_space power_allocator bang_bang

12.5 thermal_zone_device_set_mode 与模式控制

mode_store 函数(thermal_sysfs.c 第 66-83 行)处理 enabled/disabled 写入:

static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
                          const char *buf, size_t count)
{
    struct thermal_zone_device *tz = to_thermal_zone(dev);
    int result;

    if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
        result = thermal_zone_device_enable(tz);
    else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
        result = thermal_zone_device_disable(tz);
    else
        result = -EINVAL;

    if (result)
        return result;
    return count;
}

禁用 thermal zone 后:

  • 轮询定时器停止
  • 所有 cooling device 的 target 设为 THERMAL_NO_TARGET
  • Governor 不再调用
  • THERMAL_DEVICE_DOWN 事件发送给 governor

13. Netlink 事件通知

13.1 Generic Netlink 接口

源码drivers/thermal/thermal_netlink.c

Thermal 框架通过 Generic Netlink 向用户空间发送事件通知。Family 名称为 "thermal"THERMAL_GENL_FAMILY_NAMEinclude/uapi/linux/thermal.h 第 22 行),版本 0x02

两个多播组(第 23-25 行):

  • "sampling"THERMAL_GENL_SAMPLING_GROUP_NAME):周期性温度采样数据(高频)
  • "event"THERMAL_GENL_EVENT_GROUP_NAME):状态变化事件(低频)

13.2 主要事件类型(第 68-91 行)

enum thermal_genl_event {
    THERMAL_GENL_EVENT_TZ_CREATE,          // Thermal zone 创建
    THERMAL_GENL_EVENT_TZ_DELETE,          // Thermal zone 删除
    THERMAL_GENL_EVENT_TZ_DISABLE,         // Thermal zone 禁用
    THERMAL_GENL_EVENT_TZ_ENABLE,          // Thermal zone 启用
    THERMAL_GENL_EVENT_TZ_TRIP_UP,         // Trip 穿越(温度升高方向)
    THERMAL_GENL_EVENT_TZ_TRIP_DOWN,       // Trip 穿越(温度降低方向)
    THERMAL_GENL_EVENT_TZ_TRIP_CHANGE,     // Trip 参数变化
    THERMAL_GENL_EVENT_TZ_TRIP_ADD,        // Trip 点添加
    THERMAL_GENL_EVENT_TZ_TRIP_DELETE,     // Trip 点删除
    THERMAL_GENL_EVENT_CDEV_ADD,           // Cooling device 绑定
    THERMAL_GENL_EVENT_CDEV_DELETE,        // Cooling device 解绑
    THERMAL_GENL_EVENT_CDEV_STATE_UPDATE,  // Cooling device 状态更新
    THERMAL_GENL_EVENT_TZ_GOV_CHANGE,      // Governor 切换
    THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, // CPU 能力变化(HFI)
    THERMAL_GENL_EVENT_THRESHOLD_ADD,      // 用户阈值添加
    THERMAL_GENL_EVENT_THRESHOLD_DELETE,   // 用户阈值删除
    THERMAL_GENL_EVENT_THRESHOLD_FLUSH,    // 所有用户阈值清除
    THERMAL_GENL_EVENT_THRESHOLD_UP,       // 用户阈值被穿越(升温方向)
    THERMAL_GENL_EVENT_THRESHOLD_DOWN,     // 用户阈值被穿越(降温方向)
};

13.3 命令接口(第 93-108 行)

用户空间可以通过 Netlink 命令查询状态,而无需解析 sysfs 文件路径:

enum thermal_genl_cmd {
    THERMAL_GENL_CMD_TZ_GET_ID,        // 获取所有 thermal zone 的 ID 列表
    THERMAL_GENL_CMD_TZ_GET_TRIP,      // 获取 thermal zone 的 trip 点列表
    THERMAL_GENL_CMD_TZ_GET_TEMP,      // 获取温度
    THERMAL_GENL_CMD_TZ_GET_GOV,       // 获取 governor
    THERMAL_GENL_CMD_TZ_GET_MODE,      // 获取模式(enabled/disabled)
    THERMAL_GENL_CMD_CDEV_GET,         // 获取 cooling device 列表
    THERMAL_GENL_CMD_THRESHOLD_GET,    // 获取用户阈值列表
    THERMAL_GENL_CMD_THRESHOLD_ADD,    // 添加用户阈值
    THERMAL_GENL_CMD_THRESHOLD_DELETE, // 删除用户阈值
    THERMAL_GENL_CMD_THRESHOLD_FLUSH,  // 清除所有用户阈值
};

用户阈值(user_threshold)是较新引入的特性,允许用户空间无需轮询即可在特定温度变化时收到通知,由 drivers/thermal/thermal_thresholds.c 实现。

13.4 Netlink 属性策略(第 23-58 行)

thermal_genl_policy 定义了所有属性的类型约束:

static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
    [THERMAL_GENL_ATTR_TZ]          = { .type = NLA_NESTED },
    [THERMAL_GENL_ATTR_TZ_ID]       = { .type = NLA_U32 },
    [THERMAL_GENL_ATTR_TZ_TEMP]     = { .type = NLA_U32 },
    [THERMAL_GENL_ATTR_TZ_TRIP]     = { .type = NLA_NESTED },
    [THERMAL_GENL_ATTR_TZ_TRIP_ID]  = { .type = NLA_U32 },
    [THERMAL_GENL_ATTR_TZ_TRIP_TEMP]= { .type = NLA_U32 },
    [THERMAL_GENL_ATTR_TZ_TRIP_TYPE]= { .type = NLA_U32 },
    [THERMAL_GENL_ATTR_TZ_TRIP_HYST]= { .type = NLA_U32 },
    [THERMAL_GENL_ATTR_TZ_MODE]     = { .type = NLA_U32 },
    // CPU capability(HFI 使用)
    [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 },
    [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]  = { .type = NLA_U32 },
    // 用户阈值
    [THERMAL_GENL_ATTR_THRESHOLD_TEMP]      = { .type = NLA_U32 },
    [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 },
};

14. Device Tree 集成

14.1 DT Thermal 框架

源码drivers/thermal/thermal_of.c

设备树通过 thermal-zones 节点描述热区配置,devm_thermal_of_zone_register() 负责解析并注册 thermal zone。

Trip 类型字符串映射(第 27-32 行):

static const char * const trip_types[] = {
    [THERMAL_TRIP_ACTIVE]   = "active",
    [THERMAL_TRIP_PASSIVE]  = "passive",
    [THERMAL_TRIP_HOT]      = "hot",
    [THERMAL_TRIP_CRITICAL] = "critical",
};

DT 节点示例(ARM SoC 典型配置):

thermal-zones {
    cpu_thermal: cpu-thermal {
        polling-delay-passive = <250>;   /* passive 冷却轮询间隔,ms */
        polling-delay = <1000>;          /* 普通轮询间隔,ms */
        thermal-sensors = <&tsadc 0>;   /* 温度传感器 phandle + 通道 ID */

        trips {
            cpu_alert0: cpu-alert0 {
                temperature = <70000>;   /* 70°C,单位毫摄氏度 */
                hysteresis = <2000>;     /* 2°C 迟滞 */
                type = "passive";
            };
            cpu_crit: cpu-crit {
                temperature = <110000>;  /* 110°C */
                hysteresis = <0>;
                type = "critical";
            };
        };

        cooling-maps {
            map0 {
                trip = <&cpu_alert0>;
                cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
                contribution = <70>;     /* 权重(对应 weight 字段) */
            };
        };
    };
};

Trip 属性解析(thermal_of_populate_trip() 第 63-80 行):

ret = of_property_read_u32(np, "temperature", &prop);
trip->temperature = prop;

ret = of_property_read_u32(np, "hysteresis", &prop);
trip->hysteresis = prop;

thermal_of_get_trip_type(np, &trip->type);

14.2 DT 解析的完整流程(thermal_of.c

devm_thermal_of_zone_register(dev, sensor_id, data, ops)
    │
    ├── of_find_thermal_zone(sensor_np)
    │     遍历 thermal-zones 下的所有子节点
    │     查找 thermal-sensors = <&sensor_phandle sensor_id>
    │
    ├── thermal_of_trips_init(np)
    │     遍历 trips 子节点
    │     为每个 trip 子节点调用 thermal_of_populate_trip()
    │     返回 struct thermal_trip 数组
    │
    ├── thermal_of_monitor_init(np)
    │     读取 polling-delay-passive 和 polling-delay
    │
    ├── thermal_zone_device_register_with_trips()
    │     注册到 thermal 核心框架
    │
    └── thermal_of_bind()
          遍历 cooling-maps 子节点
          读取 trip phandle、cooling-device phandle
          读取 contribution(权重)
          读取 min/max 状态约束(THERMAL_NO_LIMIT 表示无约束)
          调用 thermal_zone_bind_cooling_device()

14.3 多传感器热区配置

一个热区可以引用多个温度传感器,取其最大温度:

thermal-zones {
    soc-thermal {
        thermal-sensors = <&tsens 1>, <&tsens 2>;  /* 多个传感器 */
        /* thermal 框架会取最高温度作为热区温度 */
    };
};

部分 SoC(如高通 Snapdragon)使用多个 TSENS(热传感器)通道,将多个核心的温度汇聚到一个热区进行统一管理。


15. 调试与追踪

15.1 Traceevent 追踪点

Thermal 框架定义了多个 traceevent 追踪点(drivers/thermal/thermal_trace.h):

追踪点 说明
thermal_temperature 温度更新事件
thermal_zone_trip Trip 点穿越事件
thermal_power_cpu_get_power_simple CPU 功率采样
thermal_power_allocator IPA 分配决策
thermal_power_allocator_pid IPA PID 计算详情(p, i, d, range)
thermal_power_actor 单个 power actor 的功率分配
thermal_power_cpu_limit CPU 频率限制决策

使用方法:

# 开启 thermal 相关 tracepoint
echo 1 > /sys/kernel/debug/tracing/events/thermal/enable
cat /sys/kernel/debug/tracing/trace_pipe
# 示例输出:
# thermald-123 [001] .... thermal_temperature: thermal_zone=cpu-thermal id=0 temp=65000
# kworker-456  [000] .... thermal_zone_trip:   thermal_zone=cpu-thermal id=0 trip=1 trip_type=passive

15.2 Debugfs 接口

若启用 CONFIG_THERMAL_DEBUGFSdrivers/thermal/thermal_debugfs.c),在 /sys/kernel/debug/thermal/ 下提供详细的调试信息:

  • 每个 thermal zone 的 trip 统计(在各 trip 处停留的时间)
  • Cooling device 的状态转换历史(transition 目录)
  • IPA 的功率分配历史

15.3 温度检查失败的重试机制

thermal_core.c 第 319-345 行实现了指数退避重试机制:

#define THERMAL_RECHECK_DELAY      msecs_to_jiffies(250)   // 初始重试延迟(250ms)
#define THERMAL_MAX_RECHECK_DELAY  (120 * HZ)              // 最大重试延迟(120秒)

static void thermal_zone_recheck(struct thermal_zone_device *tz, int error)
{
    if (error == -EAGAIN) {
        // 临时性错误,使用固定延迟
        thermal_zone_device_set_polling(tz, THERMAL_RECHECK_DELAY);
        return;
    }

    // 持续性错误:使用指数退避(每次延迟增加 50%)
    thermal_zone_device_set_polling(tz, tz->recheck_delay_jiffies);
    tz->recheck_delay_jiffies += max(tz->recheck_delay_jiffies >> 1, 1ULL);

    if (tz->recheck_delay_jiffies > THERMAL_MAX_RECHECK_DELAY) {
        thermal_zone_broken_disable(tz);  // 禁用 thermal zone
        tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; // 重置,方便恢复
    }
}

15.4 暂停/恢复处理

thermal_core.c 维护全局标志 thermal_pm_suspended(第 42 行),在系统挂起期间阻止温度轮询。恢复时通过 TZ_STATE_FLAG_RESUMING 标志和 THERMAL_TZ_RESUME 事件通知 governor 重新初始化状态。


16. 关键流程分析

16.1 Thermal Zone 注册流程

thermal_zone_device_register_with_trips()
    │
    ├── ida_alloc(&thermal_tz_ida)           // 分配全局唯一 ID
    ├── kzalloc(sizeof(*tz) + trips 柔性数组) // 一次分配
    ├── 初始化 trips_high, trips_reached, trips_invalid 链表
    ├── 为每个 trip 初始化 thermal_trip_desc
    │     ├── 有效温度 → move_to_trips_high()(threshold = temperature)
    │     └── 无效温度 → move_to_trips_invalid()(threshold = INT_MAX)
    ├── device_register(&tz->device)         // 注册设备模型,创建 sysfs
    ├── thermal_set_governor(tz, governor)   // 绑定 governor,调用 bind_to_tz()
    ├── thermal_add_hwmon_sysfs(tz)          // 注册 hwmon 接口(unless no_hwmon)
    ├── __thermal_zone_cdev_bind(tz, cdev)   // 与已注册的 cdev 自动绑定
    └── thermal_zone_device_enable(tz)      // 启用,触发首次温度检查

16.2 Cooling Device 注册与绑定流程

__thermal_cooling_device_register()
    │
    ├── ida_alloc(&thermal_cdev_ida)
    ├── kstrdup_const(type)
    ├── cdev->ops->get_max_state()          // 查询最大冷却状态,存入 cdev->max_state
    ├── thermal_cooling_device_setup_sysfs()
    ├── device_register(&cdev->device)      // 创建 /sys/class/thermal/cooling_deviceN/
    └── thermal_cooling_device_init_complete()
            ├── list_add(&cdev->node, &thermal_cdev_list)
            └── for each tz in thermal_tz_list:
                    thermal_zone_cdev_bind(tz, cdev)
                        └── __thermal_zone_cdev_bind()
                                for each td in tz->trips:
                                    tz->ops.should_bind(tz, trip, cdev, &spec)?
                                        └── YES: thermal_bind_cdev_to_trip()
                                                    创建 thermal_instance
                                                    创建 cdevN symlink
                                                    创建 cdevN_trip_point 属性
                                                    创建 cdevN_weight 属性

16.3 温度更新主循环

delayed_work 到期 → thermal_zone_device_check()
    │
    └── __thermal_zone_device_update(tz, THERMAL_EVENT_TEMP_SAMPLE)
            │
            ├── 检查 state == READY && mode == ENABLED
            ├── __thermal_zone_get_temp(tz, &temp)  // 调用 ops.get_temp()
            │     失败 → thermal_zone_recheck()(指数退避)
            ├── tz->last_temperature = tz->temperature
            ├── tz->temperature = temp
            ├── thermal_genl_sampling_temp()         // 发送 sampling Netlink 消息
            │
            ├── thermal_zone_handle_trips()          // 检测 trip 穿越
            │     遍历 trips_reached(降温,判断是否需要退出):
            │         threshold > temp → thermal_trip_crossed(false)
            │           PASSIVE → tz->passive--
            │           HOT/CRITICAL → (降温无需特殊处理)
            │           → thermal_notify_tz_trip_down()(Netlink + uevent)
            │     遍历 trips_high(升温,判断是否需要进入):
            │         threshold <= temp → thermal_trip_crossed(true)
            │           PASSIVE → tz->passive++
            │           HOT → tz->ops.hot(tz)
            │           CRITICAL → tz->ops.critical(tz)
            │                          → __hw_protection_trigger() → 关机!
            │           → thermal_notify_tz_trip_up()(Netlink + uevent)
            │
            ├── thermal_thresholds_handle()          // 处理用户空间自定义阈值
            ├── thermal_zone_set_trips(tz, low, high) // 通知驱动更新中断阈值
            │     ops.set_trips(tz, low, high)        // 硬件中断驱动模式
            ├── governor->manage(tz)                  // Governor 决策
            └── monitor_thermal_zone(tz)             // 调度下次轮询
                    passive > 0 → passive_delay_jiffies
                    else → polling_delay_jiffies

16.4 IPA 完整调用链

governor->manage(tz)  →  power_allocator_manage()
    │
    ├── [temperature < trip_switch_on]
    │       allow_maximum_power(tz)      // 所有 cdev 以最大性能运行
    │       reset_pid_controller()       // 清零积分项和上次误差
    │
    └── [temperature >= trip_switch_on]
            allocate_power(tz, trip_max->temperature)
                │
                ├── 遍历 trip_max 的 thermal_instances(power actors)
                │     get_requested_power() → pa->req_power(实际功耗 mW)
                │     state2power(lower)    → pa->max_power(最大可分配功率 mW)
                │
                ├── pid_controller(control_temp, max_allocatable_power)
                │     err = control_temp - tz->temperature
                │     p = k_po/k_pu × err(超调/欠调使用不同系数)
                │     i += k_i × err(积分限幅 + integral_cutoff 控制)
                │     d = k_d × Δerr / Δt(微分项时间归一化)
                │     power_range = sustainable_power + (p + i + d)
                │     power_range = clamp(0, max_allocatable_power)
                │
                ├── divvy_up_power()
                │     第一轮:granted[i] = power_range * weighted_req[i] / total_weighted_req
                │     第二轮:重分配超额功率给有余量的 actor
                │
                └── for each power actor:
                        power2state(pa->granted_power) → state
                        instance->target = clamp(state, lower, upper)
                        cdev->ops->set_cur_state(cdev, target)
                          └── freq_qos_update_request()(cpufreq cooling)
                           或 dev_pm_qos_update_request()(devfreq cooling)

17. 总结

Linux Thermal 子系统是一个高度模块化的框架,其设计体现了以下核心思想:

架构设计原则

  1. 关注点分离:温度感知(thermal zone)、冷却执行(cooling device)、控制策略(governor)三者完全解耦
  2. 可组合性:一个 thermal zone 可绑定多个 cooling device,一个 cooling device 可被多个 thermal zone 复用
  3. 策略可插拔:governor 以模块方式注册,运行时可通过 sysfs 动态切换
  4. 双模态驱动:同时支持轮询(polling)和中断驱动两种工作模式,中断驱动通过 ops.set_trips() 配置硬件阈值
  5. QoS 约束模型:冷却动作通过 PM QoS 约束表达,解耦 thermal 框架与具体驱动实现

各组件关系

thermal_zone_device
    ├── [1..N] thermal_trip_desc(trip 点描述符,嵌入柔性数组)
    │           └── [1..M] thermal_instance(绑定的 cdev 实例)
    │                       ├── lower/upper(冷却状态约束范围)
    │                       ├── weight(IPA 按权重分配功率)
    │                       └── target(governor 设定的目标冷却状态)
    ├── thermal_governor(step_wise/power_allocator/bang_bang 等)
    └── thermal_zone_params(sustainable_power, k_po, k_pu, k_i, k_d)

thermal_cooling_device
    ├── thermal_cooling_device_ops(get/set_cur_state, get_max_state)
    ├── [IPA] get_requested_power / state2power / power2state
    └── [1..M] thermal_instance(反向引用,用于 cdev 侧统一更新)

Governor 选型建议

Governor 适用场景 特点
step_wise 通用,大多数平台默认 简单可靠,逐步调节,无需能量模型
bang_bang 风扇控制 二值控制,零抖动(靠 hysteresis 保证)
power_allocator ARM 移动平台,多 power actor PID 精确控制,需要能量模型(EM)支持
fair_share 多 trip 共享一个 cdev 按超温比例公平分配冷却资源
user_space 需要完全自定义策略 策略由用户空间 thermald 实现

关键文件索引

文件 重要位置 内容
include/uapi/linux/thermal.h L14-19 thermal_trip_type 枚举
include/linux/thermal.h L72-78 thermal_trip 结构体
include/linux/thermal.h L114-121 thermal_cooling_device_ops
include/linux/thermal.h L123-139 thermal_cooling_device 结构体
include/linux/thermal.h L145-192 thermal_zone_params 结构体
drivers/thermal/thermal_core.h L31-37 thermal_trip_desc 结构体
drivers/thermal/thermal_core.h L53-64 thermal_governor 结构体
drivers/thermal/thermal_core.h L119-156 thermal_zone_device 结构体
drivers/thermal/thermal_core.c L33-35 全局链表定义
drivers/thermal/thermal_core.c L609-657 __thermal_zone_device_update()
drivers/thermal/thermal_core.c L824-910 thermal_bind_cdev_to_trip()
drivers/thermal/gov_step_wise.c L32-79 get_target_state()
drivers/thermal/gov_step_wise.c L117-152 step_wise_manage()
drivers/thermal/gov_bang_bang.c L63-78 bang_bang_trip_crossed()
drivers/thermal/gov_bang_bang.c L123-129 governor 结构体定义
drivers/thermal/gov_power_allocator.c L57-98 IPA 核心数据结构
drivers/thermal/gov_power_allocator.c L239-299 pid_controller()
drivers/thermal/gov_power_allocator.c L350-403 divvy_up_power()
drivers/thermal/gov_power_allocator.c L406-483 allocate_power()
drivers/thermal/cpufreq_cooling.c L68-79 cpufreq_cooling_device 结构体
drivers/thermal/cpufreq_cooling.c L389-420 get_state_freq()
drivers/thermal/cpufreq_cooling.c L473-497 cpufreq_set_cur_state()
drivers/thermal/cpufreq_cooling.c L514-611 __cpufreq_cooling_register()
drivers/thermal/devfreq_cooling.c L50-62 devfreq_cooling_device 结构体
drivers/thermal/devfreq_cooling.c L84-118 devfreq_cooling_set_cur_state()
drivers/thermal/thermal_hwmon.c L24-43 hwmon 桥接数据结构
drivers/thermal/thermal_sysfs.c L36-49 temp_show() 温度读取
drivers/thermal/thermal_of.c L27-32 DT trip 类型字符串映射
drivers/acpi/thermal.c L89-120 acpi_thermal 结构体
drivers/thermal/intel/x86_pkg_temp_thermal.c L50-62 Intel 包温度传感器结构体
drivers/thermal/intel/intel_powerclamp.c L61-68 Powerclamp 数据结构

18. 用户阈值机制

18.1 概述

源码drivers/thermal/thermal_thresholds.cdrivers/thermal/thermal_thresholds.h

用户阈值(User Threshold)是 Linux 6.x 引入的新特性,允许用户空间程序通过 Netlink 接口动态注册温度阈值,当温度穿越这些阈值时收到异步通知,无需轮询。这是比 UEvent 更高效的事件驱动机制。

18.2 数据结构

user_threshold 结构体(thermal_thresholds.h):

struct user_threshold {
    struct list_head list_node; // 有序链表节点(按温度升序排列)
    int temperature;            // 阈值温度(毫摄氏度)
    int direction;              // 触发方向:
                                //   THERMAL_THRESHOLD_WAY_UP   = 升温穿越
                                //   THERMAL_THRESHOLD_WAY_DOWN = 降温穿越
                                //   两者均可 OR 组合
};

用户阈值存储在 thermal_zone_device.user_thresholds 链表中,按温度升序排列(list_sort 维护)。

18.3 阈值处理逻辑(thermal_thresholds.c

添加阈值(第 168-200 行):

int thermal_thresholds_add(struct thermal_zone_device *tz,
                           int temperature, int direction)
{
    t = __thermal_thresholds_find(thresholds, temperature);
    if (t) {
        if (t->direction == direction)
            return -EEXIST;        // 完全相同的阈值已存在
        t->direction |= direction; // 追加方向
    } else {
        t = kmalloc_obj(*t);
        t->temperature = temperature;
        t->direction = direction;
        list_add(&t->list_node, thresholds);
        list_sort(NULL, thresholds, __thermal_thresholds_cmp); // 维护升序
    }
    thermal_notify_threshold_add(tz, temperature, direction);
    __thermal_zone_device_update(tz, THERMAL_TZ_ADD_THRESHOLD);
    return 0;
}

阈值穿越检测(第 72-166 行):

升温穿越检测逻辑:

static bool thermal_thresholds_handle_raising(struct list_head *thresholds,
                                              int temperature, int last_temperature)
{
    list_for_each_entry(t, thresholds, list_node) {
        if (!(t->direction & THERMAL_THRESHOLD_WAY_UP))
            continue;
        // 当前温度 >= 阈值 且 上次温度 < 阈值 → 穿越发生
        if (temperature >= t->temperature && last_temperature < t->temperature)
            return true;
    }
    return false;
}

降温穿越检测类似,但从链表尾部(高温)向头部(低温)反向遍历,以捕获最低被穿越的阈值。

边界计算(thermal_threshold_find_boundaries() 第 108-126 行):

此函数将用户阈值的边界合并到 low/high 中,用于配置硬件中断:

// 找到温度以上最近的升温阈值 → 更新 high
if (temperature < t->temperature && (t->direction & WAY_UP) && *high > t->temperature)
    *high = t->temperature;

// 找到温度以下最近的降温阈值 → 更新 low
if (temperature > t->temperature && (t->direction & WAY_DOWN) && *low < t->temperature)
    *low = t->temperature;

这样,当温度越出 [low, high] 范围时,硬件触发中断,内核随即检查是否有用户阈值被穿越。

18.4 用户空间使用方式

通过 Generic Netlink 命令操作用户阈值:

# Python 示例(使用 nlattr 库)
# 添加 60°C 升温阈值
cmd = THERMAL_GENL_CMD_THRESHOLD_ADD
attrs = {
    THERMAL_GENL_ATTR_TZ_ID: 0,           # thermal zone 0
    THERMAL_GENL_ATTR_THRESHOLD_TEMP: 60000,    # 60°C(毫摄氏度)
    THERMAL_GENL_ATTR_THRESHOLD_DIRECTION: THERMAL_THRESHOLD_WAY_UP,
}
# 发送命令到 "thermal" family

# 监听事件
# 当 thermal zone 0 温度从 <60°C 升至 >=60°C 时收到
# THERMAL_GENL_EVENT_THRESHOLD_UP 事件

19. 温度感知工作原理与驱动实现

19.1 温度读取接口

thermal_zone_device_ops 中的温度读取回调(include/linux/thermal.h):

struct thermal_zone_device_ops {
    int (*get_temp)(struct thermal_zone_device *tz, int *temp);
    // 必须实现。返回值单位:毫摄氏度
    // 返回 -EAGAIN 表示临时性无法获取(进入短暂重试)
    // 返回其他负值表示持续性错误(进入指数退避)

    int (*set_trips)(struct thermal_zone_device *tz, int low, int high);
    // 可选。中断驱动模式:设置温度窗口 [low, high]
    // 当温度超出此窗口时,驱动负责调用 thermal_zone_device_update()

    int (*get_trend)(struct thermal_zone_device *tz,
                     const struct thermal_trip *trip, enum thermal_trend *trend);
    // 可选。step_wise governor 使用。若未实现,则根据相邻温度差推断趋势

    void (*hot)(struct thermal_zone_device *tz);
    // 可选。温度超过 HOT trip 时回调(通常发送通知给 OSPM)

    void (*critical)(struct thermal_zone_device *tz);
    // 可选。若实现则替代默认关机行为。通常平台可在此触发硬件保护

    int (*change_mode)(struct thermal_zone_device *tz,
                       enum thermal_device_mode mode);
    // 可选。模式切换时回调(enabled/disabled 切换)
};

19.2 温度获取函数的安全性设计

__thermal_zone_get_temp()thermal_helpers.c 第 82-115 行)内置了仿真温度安全检查:

int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
    const struct thermal_trip_desc *td;
    int crit_temp = INT_MAX;
    int ret;

    ret = tz->ops.get_temp(tz, temp);

    if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
        // 查找临界温度
        for_each_trip_desc(tz, td) {
            if (td->trip.type == THERMAL_TRIP_CRITICAL) {
                crit_temp = td->trip.temperature;
                break;
            }
        }
        // 仿真温度只有在实际温度低于临界温度时才生效
        // 防止通过仿真温度掩盖真实的临界状态
        if (!ret && *temp < crit_temp)
            *temp = tz->emul_temperature;
    }
    return ret;
}

thermal_zone_get_temp() 的公开版本(第 127-144 行)增加了额外检查:

int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
    guard(thermal_zone)(tz);  // 获取 tz->lock

    ret = __thermal_zone_get_temp(tz, temp);
    // 额外检查:温度不能 <= THERMAL_TEMP_INVALID (-274000)
    // 该值表示温度尚未初始化或传感器硬件未就绪
    if (!ret && *temp <= THERMAL_TEMP_INVALID)
        return -ENODATA;
    return ret;
}

19.3 中断驱动模式(interrupt-driven mode)

当驱动实现 ops.set_trips() 时,系统工作在中断驱动模式,polling_delay = 0

温度窗口示意图:
                    low        current     high
                     |           |          |
  ----[---|-----------|-----------|---------|----|----]----
       cold         60°C       65°C      70°C   hot

硬件设置:[60000, 70000] 窗口
当温度变化导致超出 [60000, 70000] 时:
  → 硬件触发中断
  → IRQ handler 调用 thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED)
  → __thermal_zone_device_update() 执行完整的温度更新流程
  → 计算新窗口 [new_low, new_high]
  → 调用 ops.set_trips(tz, new_low, new_high) 更新硬件阈值

thermal_zone_set_trips() 函数(thermal_trip.c 第 54-80 行)会缓存上一次设置的阈值,若 low == prev_low && high == prev_high 则跳过硬件写入:

void thermal_zone_set_trips(struct thermal_zone_device *tz, int low, int high)
{
    if (tz->prev_low_trip == low && tz->prev_high_trip == high)
        return;  // 优化:阈值未变化,跳过硬件写入

    tz->prev_low_trip = low;
    tz->prev_high_trip = high;

    ret = tz->ops.set_trips(tz, low, high);
}

19.4 get_tz_trend 温度趋势判断

thermal_helpers.c 第 25-40 行:

int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip)
{
    enum thermal_trend trend;

    // 仿真温度激活时,或驱动未实现 get_trend,或 get_trend 失败
    if (tz->emul_temperature || !tz->ops.get_trend ||
        tz->ops.get_trend(tz, trip, &trend)) {
        // 基于历史温度简单推断趋势
        if (tz->temperature > tz->last_temperature)
            trend = THERMAL_TREND_RAISING;
        else if (tz->temperature < tz->last_temperature)
            trend = THERMAL_TREND_DROPPING;
        else
            trend = THERMAL_TREND_STABLE;
    }
    return trend;
}

20. 其他 Cooling Device 类型

20.1 cpuidle Cooling Device

源码drivers/thermal/cpuidle_cooling.c

通过 idle_inject 框架实现的 CPU 空闲冷却设备,比 Powerclamp 更通用(不依赖 Intel 特定 MSR)。

运行时间计算(第 54-60 行):

// 公式:running = (idle * 100) / state - idle
// 例如:
//   state=50%: running = (10ms * 100) / 50 - 10ms = 10ms
//   state=25%: running = (10ms * 100) / 25 - 10ms = 30ms
//   state=10%: running = (10ms * 100) / 10 - 10ms = 90ms
static unsigned int cpuidle_cooling_runtime(unsigned int idle_duration_us,
                                            unsigned long state)
{
    if (!state)
        return 0;
    return ((idle_duration_us * 100) / state) - idle_duration_us;
}

冷却状态与空闲注入比例:

state 0  → 不注入空闲(100% 运行)
state 10 → 注入 10% 空闲(90% 运行)
state 50 → 注入 50% 空闲(50% 运行)
state 100→ 注入 100% 空闲(0% 运行,仅用于紧急情况)

max_state = 100idle_inject_set_duration() 设置每个 idle 注入周期的持续时间(默认 10ms)。

20.2 PCIe Cooling Device

源码drivers/thermal/pcie_cooling.c

通过降低 PCIe 链路速度(Gen5 → Gen4 → Gen3 → Gen2 → Gen1)来减少 PCIe 设备的功耗和热量。

冷却状态与速度映射(第 20-55 行):

// 冷却状态 0 = 最高 PCIe 速度(最小冷却)
// max_state = port->subordinate->max_bus_speed - PCIE_SPEED_2_5GT
// 例如 Gen5 设备:max_state = PCIE_SPEED_32GT - PCIE_SPEED_2_5GT = 4

// 速度设置(第 40-48 行):
static int pcie_cooling_set_cur_level(struct thermal_cooling_device *cdev,
                                      unsigned long state)
{
    // 速度 = max_speed - state(state 越大速度越低)
    speed = (cdev->max_state - state) + PCIE_SPEED_2_5GT;
    return pcie_set_target_speed(port, speed, true);
}

这是 Intel 贡献的新特性(2023-2024 年),适用于温度敏感的高速 PCIe 设备(如 NVMe SSD、GPU)。

20.3 Thermal Generic ADC(模拟温度传感器)

源码drivers/thermal/thermal-generic-adc.c

为使用 ADC(模数转换器)测量温度的系统提供通用接口。通过设备树配置 ADC 通道和电压-温度转换表(V-T table),无需编写专用驱动。

DT 配置示例:

thermal-zones {
    battery-thermal {
        thermal-sensors = <&thermistor>;
    };
};

thermistor: adc-thermistor {
    compatible = "generic-adc-thermal";
    io-channels = <&adc 5>;          /* ADC 通道 5 */
    io-channel-names = "sensor-channel";
    /* 电压(mV) → 温度(mC) 映射表 */
    temperature-lookup-table = <(-30000) 1626  /* -30°C: 1626mV */
                                  25000  1002  /*  25°C: 1002mV */
                                  85000   502>;/*  85°C:  502mV */
};

20.4 MMIO Thermal(内存映射寄存器温度传感器)

源码drivers/thermal/thermal_mmio.c

为通过 MMIO 寄存器直接读取温度值的简单传感器提供通用封装,无需编写完整驱动。

struct thermal_mmio {
    void __iomem *mmio_base;     // 寄存器基地址
    u32 (*read_mmio)(void __iomem *mmio_base); // 读取函数
    u32 mask;                    // 有效位掩码
    int offset;                  // 温度偏移
    int clbr_factor;            // 校准因子(等比例缩放)
};

21. Debugfs 深度解析

21.1 目录结构

CONFIG_THERMAL_DEBUGFS=y 时,thermal_debug_init() 创建以下 debugfs 树(thermal_debugfs.c 第 178-189 行):

/sys/kernel/debug/thermal/
├── cooling_devices/
│   ├── 0/              # cooling_device0
│   │   ├── total_count         # 总状态转换次数
│   │   ├── transitions         # 状态转换矩阵(只显示发生过的转换)
│   │   └── time_in_state_ms    # 各状态停留时间
│   └── 1/
│       └── ...
└── thermal_zones/
    ├── 0/              # thermal_zone0
    │   ├── mitigations/        # 热缓解事件列表
    │   │   ├── 0/              # 第 0 次缓解事件
    │   │   │   ├── timestamp   # 开始时间
    │   │   │   ├── duration    # 持续时间
    │   │   │   └── max_temp    # 最高温度
    │   │   └── ...
    │   └── ...
    └── ...

21.2 Cooling Device 调试数据结构

cdev_debugfs(第 56-62 行):

struct cdev_debugfs {
    u32 total;              // 总转换次数
    int current_state;      // 当前状态
    ktime_t timestamp;      // 最后一次状态变化时间

    // 使用哈希表存储:避免稀疏矩阵的内存浪费
    // 哈希 key = state_id % CDEVSTATS_HASH_SIZE (16)
    struct list_head transitions[16]; // 状态转换记录(from_state -> to_state, count)
    struct list_head durations[16];   // 状态停留时间记录(state, residency_ktime)
};

设计原因(第 40-48 行注释)

冷却设备可能有大量状态(如 CPUFreq 有几十个频率档位),用矩阵表示转换(NxN)会浪费内存且大部分为零。使用哈希链表只记录实际发生过的转换,内存高效。

21.3 Thermal Zone 调试数据结构

tz_debugfs(第 150-155 行)记录缓解事件(mitigation episode):

struct tz_debugfs {
    struct list_head tz_episodes; // 所有缓解事件链表
    struct thermal_zone_device *tz;
    int *trips_crossed;           // 当前正在被穿越的 trip 点 ID 数组
    int nr_trips;                 // 当前穿越的 trip 点数量
};

一个 缓解事件(tz_episode) 从最低温度 trip 被穿越开始,到该 trip 降温解除结束:

struct tz_episode {
    ktime_t timestamp;       // 事件开始时间(最低 trip 被穿越时)
    ktime_t duration;        // 事件持续时间
    struct list_head node;
    int max_temp;            // 期间最高温度
    struct trip_stats trip_stats[]; // 每个 trip 的统计(柔性数组)
};

struct trip_stats {
    ktime_t timestamp;    // 该 trip 被穿越的时间
    ktime_t duration;     // 在该 trip 以上停留的总时间
    int trip_temp;        // 该 trip 的温度(记录时刻)
    int trip_hyst;        // 该 trip 的迟滞(记录时刻)
    int count;            // 被穿越次数
    int min;              // 期间最低温度(仍高于该 trip)
    int avg;              // 期间平均温度(加权)
};

21.4 实际调试示例

# 查看 cooling_device0 的状态转换统计
cat /sys/kernel/debug/thermal/cooling_devices/0/transitions
# 输出格式:
# transitions: state_from->state_to  count
#   0 -> 1 : 125
#   1 -> 2 : 43
#   2 -> 1 : 41
#   1 -> 0 : 82

# 查看 cooling_device0 各状态停留时间
cat /sys/kernel/debug/thermal/cooling_devices/0/time_in_state_ms
# 输出格式:
# state  ms
#   0    12453210
#   1    234500
#   2    56700

# 查看 thermal_zone0 最近的热缓解事件
ls /sys/kernel/debug/thermal/thermal_zones/0/mitigations/
# 输出:0  1  2  ...(最多保留 N 个事件,默认 32)

# 查看第 0 次热缓解事件详情
cat /sys/kernel/debug/thermal/thermal_zones/0/mitigations/0/duration
cat /sys/kernel/debug/thermal/thermal_zones/0/mitigations/0/max_temp

22. Thermal 子系统初始化序列

22.1 内核启动阶段初始化

thermal_core.c 中的 thermal_init() 函数(__init 标记):

static int __init thermal_init(void)
{
    int result;

    // 1. 注册 thermal 设备类(/sys/class/thermal/)
    result = class_register(&thermal_class);

    // 2. 注册所有内置 governor
    result = thermal_register_governors();

    // 3. 注册 PM 通知器(处理挂起/恢复事件)
    register_pm_notifier(&thermal_pm_nb);

    // 4. 初始化 debugfs 目录
    thermal_debug_init();

    // 5. 初始化 Generic Netlink
    result = thermal_netlink_init();

    return result;
}
fs_initcall(thermal_init);  // 比 device_initcall 早执行,确保在驱动注册前准备好

fs_initcall 级别确保 thermal 框架在所有平台驱动之前初始化完成,因此当具体的 thermal zone 驱动(如 Rockchip TSADC)在 device_initcall 阶段注册时,框架已经就绪。

22.2 Governor 注册顺序

static int __init thermal_register_governors(void)
{
    struct thermal_governor **governor;

    // 遍历 ELF section "__governor_thermal_table"
    for_each_governor_table(governor) {
        ret = thermal_register_governor(*governor);
        // 注册失败时回滚已注册的 governor
    }
}

由于 governor 存放在 ELF section 中,其注册顺序由链接顺序决定,通常为:

  1. step_wise(大多数配置的默认 governor)
  2. bang_bang
  3. fair_share
  4. user_space
  5. power_allocator(若启用)

首个注册成功的 governor 成为 def_governor(默认 governor)。

22.3 热区注册与 Cooling Device 的自动配对

当 thermal zone 和 cooling device 分属不同驱动模块时,注册顺序不确定。内核通过两个方向的自动绑定解决:

  • 热区先注册thermal_zone_device_register() 在注册结束时调用 __thermal_zone_cdev_bind() 扫描已有 cdev
  • Cooling Device 先注册thermal_cooling_device_register() 注册完成后调用 thermal_cooling_device_init_complete(),反向扫描已有 thermal zone

这种互相扫描的设计确保无论哪一方先注册,最终都能完成绑定。


23. 暂停与恢复处理

23.1 PM 通知器

thermal_core.c 中注册了 PM 通知器处理挂起/恢复:

static int thermal_pm_notify(struct notifier_block *nb,
                             unsigned long mode, void *_unused)
{
    switch (mode) {
    case PM_HIBERNATION_PREPARE:
    case PM_SUSPEND_PREPARE:
    case PM_RESTORE_PREPARE:
        // 设置全局标志,阻止轮询工作队列重新调度
        thermal_pm_suspended = true;
        break;

    case PM_POST_HIBERNATION:
    case PM_POST_SUSPEND:
    case PM_POST_RESTORE:
        thermal_pm_suspended = false;
        // 对所有 thermal zone 触发恢复更新
        list_for_each_entry(tz, &thermal_tz_list, node) {
            // 设置 RESUMING 标志
            tz->state |= TZ_STATE_FLAG_RESUMING;
            // 触发温度更新,通知 governor 重置状态
            thermal_zone_device_update(tz, THERMAL_TZ_RESUME);
        }
        break;
    }
}

23.2 Governor 的恢复处理

IPA governor 在收到 THERMAL_TZ_RESUME 事件时通过 update_tz() 回调重置 PID 状态:

// gov_power_allocator.c
static void power_allocator_update_tz(struct thermal_zone_device *tz,
                                      enum thermal_notify_event reason)
{
    if (reason == THERMAL_TZ_RESUME)
        reset_pid_controller(params);  // 清零积分项,防止恢复后的积分突变
}

step_wise governor 在恢复后的首次 manage() 调用中,所有 thermal_instance.initialized = false,因此会按照首次初始化逻辑处理(逐步增加冷却)。

23.3 恢复完成量(completion)

thermal_zone_device.resume 字段(completion)用于同步恢复过程:

// 等待 RESUMING 标志清除后再进行热区注销
// 防止注销时热区仍处于恢复状态
wait_for_completion(&tz->resume);

24. 并发与锁机制

24.1 锁的层次结构

thermal_governor_lock (mutex)
  └── 保护 thermal_governor_list 链表
      在 governor 注册/注销/查找时持有

thermal_list_lock (mutex)
  └── 保护 thermal_tz_list 和 thermal_cdev_list 链表
      在 thermal zone 和 cooling device 注册/注销时持有

tz->lock (mutex)  [per-zone 锁]
  └── 保护单个 thermal zone 的内部状态
      包括:temperature、trips 链表、governor_data、mode
      在温度更新、trip 修改、cdev 绑定时持有
      使用 guard(thermal_zone)(tz) 宏自动管理

cdev->lock (mutex)  [per-cdev 锁]
  └── 保护单个 cooling device 的内部状态
      包括:thermal_instances 链表、updated 标志
      使用 guard(cooling_dev)(cdev) 宏自动管理

24.2 lockdep 注解

关键函数使用 lockdep_assert_held() 验证锁持有状态:

// thermal_helpers.c 第 88 行
int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
    lockdep_assert_held(&tz->lock);  // 必须在 tz->lock 保护下调用
    ...
}

// thermal_thresholds.c 第 136 行
void thermal_thresholds_handle(struct thermal_zone_device *tz, ...)
{
    lockdep_assert_held(&tz->lock);  // 必须在 tz->lock 保护下调用
    ...
}

24.3 guard() 宏的使用

Linux 6.x 引入的 guard() 宏(基于 scoped_guard())实现了自动锁管理:

// 等价于:
// mutex_lock(&tz->lock);
// ...
// mutex_unlock(&tz->lock);  // 离开作用域时自动解锁
guard(thermal_zone)(tz);

// cooling device 锁
guard(cooling_dev)(cdev);

这种 RAII 风格的锁管理减少了忘记解锁的 bug 风险,特别是在函数有多个返回路径时。

24.4 cdev->updated 标志的作用

在一次温度更新中,可能有多个 thermal_instance 指向同一个 cdev(该 cdev 绑定到多个 trip 点)。updated 标志确保 cdev 只被更新一次:

void thermal_cdev_update(struct thermal_cooling_device *cdev)
{
    guard(cooling_dev)(cdev);

    if (!cdev->updated) {
        __thermal_cdev_update(cdev);  // 实际更新:找最大 target,调用 set_cur_state
        cdev->updated = true;          // 标记已更新
    }
}

在每次温度更新循环开始前,所有相关 cdev 的 updated 标志被清除。


25. 能量模型与 Thermal 的关系

25.1 能量模型(Energy Model)框架

源码drivers/cpufreq/cpufreq_cooling.cinclude/linux/energy_model.h

Linux 能量模型(EM)框架提供了 CPU、GPU 等设备在不同性能级别下的功耗/性能数据表。这是 IPA 正确工作的基础。

em_perf_domain 结构体(include/linux/energy_model.h):

struct em_perf_domain {
    struct em_perf_table __rcu *em_table;  // 性能-功耗对照表
    int nr_perf_states;                    // OPP 数量
    unsigned long flags;
    // ...
};

struct em_perf_state {
    unsigned long frequency;  // 频率(kHz)
    unsigned long power;      // 该频率下满负载功耗(微瓦,uW)
    unsigned long cost;       // 能量效率指标(power / frequency)
    unsigned long flags;
};

25.2 能量模型与 cpufreq_cooling 的联动

of_cpufreq_cooling_register() 注册时自动查找 EM:

struct em_perf_domain *em = em_cpu_get(policy->cpu);
if (em && !em_is_sane(em, policy))
    em = NULL;  // 能量模型无效时不使用 IPA 功率接口

em_is_sane() 检查 EM 表是否与 CPUFreq 频率表对应(避免 EM 和实际频率不匹配导致计算错误)。

25.3 通过 DT 提供能量模型数据

cpu0: cpu@0 {
    compatible = "arm,cortex-a55";
    operating-points-v2 = <&cpu_opp_table>;
};

cpu_opp_table: opp-table {
    compatible = "operating-points-v2";
    opp-shared;

    opp-1000000000 {
        opp-hz = /bits/ 64 <1000000000>;  /* 1GHz */
        opp-microvolt = <900000>;          /* 900mV */
        opp-microwatt = <200000>;          /* 200mW(可选,用于 EM) */
    };
    opp-1800000000 {
        opp-hz = /bits/ 64 <1800000000>;  /* 1.8GHz */
        opp-microvolt = <1100000>;         /* 1100mV */
        opp-microwatt = <600000>;          /* 600mW */
    };
};

若 OPP 表提供了 opp-microwatt,则内核自动构建 EM;否则 EM 通过 P = C * V² * f 公式估算(需要平台提供 C 参数)。


26. 实际平台案例:Rockchip TSADC

26.1 Rockchip TSADC 架构

源码drivers/thermal/rockchip_thermal.c

Rockchip SoC(如 RK3399、RK3588)集成了 TSADC(Thermal Sensor ADC),通过 ADC 读取热敏电阻阻值并转换为温度。

硬件关机模式(第 28-31 行):

enum tshut_mode {
    TSHUT_MODE_CRU = 0,  // 通过 CRU(Clock Reset Unit)复位整个芯片
    TSHUT_MODE_GPIO,     // 通过 GPIO 向 PMIC 发送关机信号
};

ADC 值排序模式(第 47-51 行):

enum adc_sort_mode {
    ADC_DECREMENT = 0,  // ADC 值随温度升高而减小(NTC 热敏电阻)
    ADC_INCREMENT,      // ADC 值随温度升高而增大(PTC 热敏电阻)
};

温度转换表(以 RK3399 为例):

温度与 ADC 值的对应关系通过查表法(二分查找)实现,支持线性插值:

static const struct tsadc_table rk3399_code_table[] = {
    { 0,    INVALID_TEMP },
    { 402,  -40000 },   /* ADC=402 → -40°C */
    { 490,  25000  },   /* ADC=490 → 25°C  */
    { 579,  125000 },   /* ADC=579 → 125°C */
    { 0,    INVALID_TEMP },
};

26.2 Rockchip TSADC 中断驱动模式

Rockchip TSADC 支持温度阈值中断,实现无轮询的热管理:

// rockchip_thermal.c 中的 ops 实现
static const struct thermal_zone_device_ops rockchip_of_thermal_ops = {
    .get_temp    = rockchip_thermal_get_temp,     // 读取 ADC 寄存器,查表转换
    .set_trips   = rockchip_thermal_set_trips,    // 写入阈值寄存器,配置中断
};

static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev)
{
    struct rockchip_thermal_data *thermal = dev;
    // 清除中断标志
    // 通知 thermal 核心进行温度更新
    thermal_zone_device_update(thermal->tzd, THERMAL_EVENT_UNSPECIFIED);
    return IRQ_HANDLED;
}

26.3 NVMEM 校准数据

许多 SoC 的温度传感器需要出厂校准数据(存储在 eFuse 中):

// rockchip_thermal.c:通过 NVMEM 框架读取校准数据
nvmem_cell_read(cell, &len);  // 读取 efuse 中的 trim 值

// trim 值用于修正 ADC 值到温度的转换:
// actual_temp = nominal_temp + trim * trim_slope

27. 温度线性补偿:slope 与 offset

27.1 原理

thermal_zone_params 中的 slopeoffset 参数用于对原始温度进行线性补偿,解决传感器精度不足或安装位置偏差导致的系统误差:

compensated_temp = (raw_temp * slope) / 1024 + offset

27.2 实现(thermal_helpers.c

int thermal_zone_get_slope(struct thermal_zone_device *tz)
{
    if (tz && tz->tzp)
        return tz->tzp->slope;
    return 1;          // 默认斜率 1(不补偿)
}

int thermal_zone_get_offset(struct thermal_zone_device *tz)
{
    if (tz && tz->tzp)
        return tz->tzp->offset;
    return 0;          // 默认偏移 0(不补偿)
}

驱动在 ops.get_temp() 中应用补偿:

static int example_get_temp(struct thermal_zone_device *tz, int *temp)
{
    int raw_temp = read_sensor_raw();
    // 线性补偿:
    *temp = (raw_temp * thermal_zone_get_slope(tz)) / 1024
            + thermal_zone_get_offset(tz);
    return 0;
}

27.3 配置方式

通过设备树:

&thermal_zone {
    thermal-zone-params {
        slope = <1024>;   /* 斜率 1.0(1024/1024) */
        offset = <2000>;  /* 加 2°C 偏移(2000 mC) */
    };
};

或通过驱动代码:

struct thermal_zone_params tzp = {
    .slope  = 1024,   // 斜率(固定小数点,1024 = 1.0)
    .offset = 2000,   // 偏移(毫摄氏度)
};

28. Thermal 子系统常见问题与调优

28.1 温度频繁振荡(Oscillation)

症状:CPU 频率在高低频之间频繁切换,性能不稳定。

原因

  1. trip point 的 hysteresis 过小(或为 0)
  2. 使用 step_wise governor 时 passive_delay 过短

解决方案

# 增大 hysteresis(迟滞)
echo 5000 > /sys/class/thermal/thermal_zone0/trip_point_0_hyst
# 5°C 迟滞,防止在临界温度附近振荡

# 切换到 power_allocator(PID 控制更平滑)
echo "power_allocator" > /sys/class/thermal/thermal_zone0/policy

28.2 过度降频(Over-throttling)

症状:CPU 在温度并不高时已经大幅降频。

诊断

# 查看当前温度
cat /sys/class/thermal/thermal_zone0/temp
# 查看当前冷却状态
cat /sys/class/thermal/cooling_device0/cur_state
cat /sys/class/thermal/cooling_device0/max_state
# 查看 trip point 配置
cat /sys/class/thermal/thermal_zone0/trip_point_0_temp
cat /sys/class/thermal/thermal_zone0/trip_point_0_type

可能原因

  1. Trip point 温度设置过低(BIOS/DT 配置问题)
  2. IPA 的 sustainable_power 设置过低
  3. 温度传感器安装位置不合理(如靠近热源)

28.3 系统意外关机(Critical Trip Triggered)

诊断

# 查看内核日志
dmesg | grep -i "critical temperature"
# 典型输出:
# thermal thermal_zone0: cpu-thermal: critical temperature reached, initiating shutdown

# 查看临界温度配置
cat /sys/class/thermal/thermal_zone0/trip_point_*_type
cat /sys/class/thermal/thermal_zone0/trip_point_*_temp

临时规避(不建议在生产环境使用)

# ACPI 系统:临时提高临界温度(仅调试用)
# echo 115000 > /sys/class/thermal/thermal_zone0/trip_point_N_temp
# 注意:必须确认 N 对应 critical 类型的 trip

28.4 IPA 调优

调整 IPA 的 PID 参数:

# 查看当前 sustainable_power
cat /sys/class/thermal/thermal_zone0/sustainable_power

# 设置可持续功率(mW)
echo 5000 > /sys/class/thermal/thermal_zone0/sustainable_power

# 通过 debugfs 查看 IPA PID 内部状态(需要 CONFIG_THERMAL_DEBUGFS)
# 注意:标准内核 debugfs 不直接暴露 PID 参数
# 建议使用 tracepoint 进行分析:
echo 1 > /sys/kernel/debug/tracing/events/thermal_power_allocator/enable
cat /sys/kernel/debug/tracing/trace_pipe

IPA tracepoint 输出示例:

kworker/0:0-12   [000] ... thermal_power_allocator_pid:
    thermal_zone=cpu-thermal id=0
    err=-3000 err_integral=-15000 prev_err=-2500
    p=-300 i=-1500 d=-50 power_range=4700

含义:

  • err = -3000:当前温度超目标温度 3°C
  • power_range = 4700:分配的总功率预算 4.7W
  • p = -300:比例项贡献 -300mW

28.5 thermal zone 被禁用(broken disable)

get_temp() 持续失败超过 120 秒阈值后,thermal_zone_broken_disable() 会禁用该 thermal zone:

# 查看 thermal zone 是否被禁用
cat /sys/class/thermal/thermal_zone0/mode
# 输出 "disabled" 表示被禁用

# 如果传感器恢复正常,可以重新启用:
echo enabled > /sys/class/thermal/thermal_zone0/mode

注意:若被禁用的 thermal zone 包含 CRITICAL trip point,内核会打印 dev_crit 级别的警告,提醒管理员注意。


29. 内核配置选项(Kconfig)全解析

29.1 核心配置

配置项 说明
CONFIG_THERMAL 热管理子系统基础(必选)
CONFIG_THERMAL_HWMON hwmon 桥接接口(lm-sensors 支持)
CONFIG_THERMAL_OF 设备树热区支持
CONFIG_THERMAL_WRITABLE_TRIPS 允许用户空间修改 trip 温度
CONFIG_THERMAL_EMULATION 启用温度仿真(emul_temp sysfs)
CONFIG_THERMAL_STATISTICS cooling device 状态统计
CONFIG_THERMAL_DEBUGFS debugfs 调试接口
CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS 紧急关机延迟(ms,默认 0=立即)

29.2 Governor 配置

配置项 说明
CONFIG_THERMAL_GOV_STEP_WISE step_wise governor
CONFIG_THERMAL_GOV_BANG_BANG bang_bang governor
CONFIG_THERMAL_GOV_USER_SPACE user_space governor
CONFIG_THERMAL_GOV_FAIR_SHARE fair_share governor
CONFIG_THERMAL_GOV_POWER_ALLOCATOR power_allocator(IPA)governor
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE 设置 step_wise 为默认
CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR 设置 IPA 为默认

29.3 Cooling Device 配置

配置项 说明
CONFIG_CPU_THERMAL CPU 频率冷却设备(cpufreq_cooling)
CONFIG_DEVFREQ_THERMAL devfreq 冷却设备
CONFIG_THERMAL_CPUFREQ_COOLING cpufreq 冷却(通用)
CONFIG_INTEL_POWERCLAMP Intel 空闲注入冷却
CONFIG_PCIE_THERMAL PCIe 链路速度冷却

29.4 平台驱动配置

配置项 说明
CONFIG_ACPI_THERMAL ACPI 热区支持
CONFIG_X86_PKG_TEMP_THERMAL Intel 包温度传感器
CONFIG_INTEL_HFI_THERMAL Intel HFI 硬件反馈接口
CONFIG_ROCKCHIP_THERMAL Rockchip TSADC 驱动
CONFIG_SUN8I_THERMAL Allwinner H3/H5 热传感器
CONFIG_IMX_THERMAL NXP i.MX 热传感器

29.5 CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS 的安全含义

这个配置项定义了系统在触发 CRITICAL trip 后的强制关机延迟(毫秒)。

  • 设置为 0(默认):温度超临界后立即触发关机
  • 设置为正值(如 5000):等待 5 秒后如果系统未正常关机才强制关机

这实际上是一个安全后备机制:通常 thermal_zone_device_critical() 调用 orderly_poweroff() 触发正常关机流程。但若正常关机因某种原因卡住,CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS 定义的超时后会触发 kernel_power_off() 强制关机,防止硬件损坏。


30. 完整数据流图

30.1 温度感知到冷却动作的完整数据流

                    +-----------------------+
                    |   硬件温度传感器       |
                    | (ADC/MSR/MMIO/I2C...) |
                    +-----------+-----------+
                                |
                    +-----------v-----------+
                    | ops.get_temp(tz, &t)  |
                    |   驱动层温度读取       |
                    +-----------+-----------+
                                |
                    +-----------v-----------+
                    | __thermal_zone_get_   |
                    | temp()                |
                    | 仿真温度安全检查       |
                    | THERMAL_TEMP_INVALID  |
                    | 检查                  |
                    +-----------+-----------+
                                |
                    +-----------v-----------+
                    | thermal_zone_handle_  |
                    | trips()               |
                    | 遍历 trips_high/      |
                    | trips_reached         |
                    | 检测 trip 穿越         |
                    +-----------+-----------+
                                |
            +-------------------+-------------------+
            |                   |                   |
    +-----------v----+  +-------v--------+  +------v-------+
    |  PASSIVE trip  |  |  HOT trip      |  | CRITICAL trip|
    |  tz->passive++ |  |  ops.hot(tz)   |  |  ops.critical|
    |  governor->    |  |  Netlink 通知   |  |  关机序列     |
    |  trip_crossed  |  +----------------+  +------+-------+
    +-------+--------+                             |
            |                             +--------v-------+
    +-------v--------+                   | orderly_poweroff|
    | governor->     |                   | kernel_power_off|
    | manage(tz)     |                   +----------------+
    +-------+--------+
            |
    +-------v--------+
    | 计算目标冷却状态 |
    | (step_wise:     |
    |  state +=/-= 1) |
    | (IPA: PID →    |
    |  power → state)|
    +-------+--------+
            |
    +-------v--------+
    | thermal_cdev_  |
    | update(cdev)   |
    | 取所有绑定      |
    | instance 的    |
    | 最大 target    |
    +-------+--------+
            |
    +-------v--------+
    | ops.set_cur_   |
    | state(cdev, t) |
    +-------+--------+
            |
    +-------v--------+
    | PM QoS 约束更新 |
    | freq_qos_update |
    | dev_pm_qos_...  |
    +-------+--------+
            |
    +-------v--------+
    | CPUFreq/devfreq |
    | 频率切换        |
    | 硬件执行降频    |
    +----------------+

30.2 IPA 功率分配数据流

  +-----------+    +-----------+    +-----------+
  |  CPU Big  |    |  CPU Lit  |    |  GPU      |
  |  cdev     |    |  cdev     |    |  cdev     |
  +-----+-----+    +-----+-----+    +-----+-----+
        |                |                |
  req=3W, max=5W   req=1W, max=2W   req=4W, max=6W
        |                |                |
        +----------------+----------------+
                         |
                +--------v--------+
                | 汇总:          |
                | total_req=8W    |
                | max_alloc=13W   |
                +--------+--------+
                         |
                +--------v--------+
                | PID 控制器       |
                | control=80°C    |
                | current=82°C    |
                | err=-2000mC     |
                | p=-200mW        |
                | i=-800mW        |
                | d=0             |
                | range=5000-1000 |
                |    =4000mW (4W) |
                +--------+--------+
                         |
                +--------v--------+
                | divvy_up_power  |
                | 第一轮:        |
                | cpu_big: 3/8*4  |
                |        = 1.5W   |
                | cpu_lit: 1/8*4  |
                |        = 0.5W   |
                | gpu:    4/8*4   |
                |        = 2.0W   |
                | 总=4W ✓        |
                +--------+--------+
                         |
        +----------------+----------------+
        |                |                |
  +-----v-----+    +-----v-----+    +-----v-----+
  | power2    |    | power2    |    | power2    |
  | state     |    | state     |    | state     |
  | 1.5W→     |    | 0.5W→     |    | 2.0W→     |
  | state=2   |    | state=1   |    | state=3   |
  +-----+-----+    +-----+-----+    +-----+-----+
        |                |                |
  set_cur_state(2)  set_cur_state(1)  set_cur_state(3)
        |                |                |
  1.2GHz 限制       800MHz 限制       400MHz 限制

30.3 各组件交互关系总图

+------------------------------------------------------------------+
|                    用户空间(User Space)                         |
|   thermald   thermal-engine   lm-sensors   自定义脚本            |
|      |              |              |            |               |
|   Netlink        sysfs          hwmon        sysfs             |
+------|-----------+--|---+---------+------------|-----------+----+
       |           |  |   |                      |           |
+------v-----------v--v---v-----------+  +-------v-----------v----+
|         Generic Netlink             |  |    /sys/class/thermal/  |
|  thermal_netlink.c                  |  |    thermal_sysfs.c      |
|  Family: "thermal"                  |  |    thermal_hwmon.c      |
|  Groups: "sampling" | "event"       |  +------------------------+
+-------------------+-----------------+           |
                    |                             |
+-------------------v-----------------------------v---------------+
|                   Thermal 核心框架                               |
|   thermal_core.c    thermal_helpers.c    thermal_trip.c        |
|                                                                 |
|   +------------------+     +--------------------+              |
|   | thermal_tz_list  |     | thermal_cdev_list  |              |
|   | (全局 mutex 保护)  |     | (全局 mutex 保护)   |             |
|   +--------+---------+     +----------+---------+              |
|            |                          |                        |
|   +--------v---------+     +----------v---------+             |
|   | thermal_zone_    |     | thermal_cooling_   |             |
|   | device           |     | device             |             |
|   | (per-zone mutex) |     | (per-cdev mutex)   |             |
|   |  +-----------+   |     |  +-----------+     |             |
|   |  | trips[]   |   |---->|  | instances |     |             |
|   |  | (柔性数组) |   |<----|  | 链表      |     |             |
|   |  +-----------+   |     |  +-----------+     |             |
|   +--------+---------+     +-------------------+              |
|            |                                                   |
|   +--------v---------+                                        |
|   | Governor 接口     |                                        |
|   | step_wise        |                                        |
|   | power_allocator  |                                        |
|   | bang_bang        |                                        |
|   | fair_share       |                                        |
|   | user_space       |                                        |
|   +------------------+                                        |
+------+--------------------+--------------------------------------+
       |                    |
+------v------+    +--------v---------+
| 温度传感器   |    | 冷却设备驱动      |
| 驱动层       |    |                  |
|              |    | cpufreq_cooling  |
| ACPI _TMP   |    | devfreq_cooling  |
| Intel MSR   |    | intel_powerclamp |
| Rockchip    |    | cpuidle_cooling  |
| TSADC       |    | pcie_cooling     |
| i.MX TEMPMON|    +--------+---------+
+------+------+             |
       |             +------v-------+
+------v------+      | PM QoS 框架  |
| ADC/MSR     |      | CPUFreq 核心 |
| MMIO 寄存器  |      | devfreq 核心 |
| I2C 设备    |      +------+-------+
+-------------+             |
                     +------v-------+
                     | 硬件执行      |
                     | 频率/电压变化 |
                     +-------------+

由 Claude Code 分析生成