- 框架概述
- 整体架构
- 核心数据结构
- Trip Point 机制
- Governor 算法详解
- CPU Cooling Device
- devfreq Cooling Device
- Intel 平台集成
- ACPI Thermal Zone 集成
- IPA 智能功率分配算法
- hwmon 与 Thermal 的关系
- sysfs 接口与用户空间
- Netlink 事件通知
- Device Tree 集成
- 调试与追踪
- 关键流程分析
- 总结
- 用户阈值机制(User Threshold)
- 温度感知工作原理与驱动实现
- 其他 Cooling Device 类型
- Debugfs 深度解析
- Thermal 子系统初始化序列
- 暂停与恢复处理
- 并发与锁机制
- 能量模型(Energy Model)与 Thermal 的关系
- 实际平台案例:Rockchip TSADC
- 温度线性补偿:slope 与 offset
- Thermal 子系统常见问题与调优
- 内核配置选项(Kconfig)全解析
- 完整数据流图
Linux Thermal 热管理子系统(Thermal Management Subsystem)自 2008 年由 Intel 工程师 Zhang Rui 引入,提供了一套通用的、与硬件无关的热管理框架。其核心目标是:在系统温度过高时,通过控制各类冷却设备(风扇、CPU 频率限制等)将温度维持在安全范围内,同时尽量保留系统性能。
源码入口文件:
drivers/thermal/thermal_core.c:核心框架,注册/注销 thermal zone 和 cooling deviceinclude/linux/thermal.h:公开 API 和数据结构定义include/uapi/linux/thermal.h:用户空间可见的数据结构和枚举drivers/thermal/thermal_core.h:内核内部结构体(thermal_zone_device、thermal_governor等)
框架的四个核心概念:
| 概念 | 说明 |
|---|---|
| Thermal Zone | 一个可监测温度的逻辑区域(如 CPU 包、GPU、电池等) |
| Cooling Device | 一个可降温的设备(如 CPU 频率限制、风扇) |
| Trip Point | 触发冷却动作的温度阈值 |
| Governor | 决定如何调节冷却设备的策略算法 |
+-----------------------------------------------------------+
| 用户空间 / 工具层 |
| 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_list和thermal_cdev_listthermal_governor_lock:保护thermal_governor_list
定义于 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定义于 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 表示最强冷却(最低性能)。
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_desc(drivers/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_high 和 trips_reached 的维护逻辑:
- 当温度低于 trip 的
temperature时,trip 在trips_high中,threshold = trip.temperature - 当温度高于等于 trip 的
temperature时,trip 移入trips_reached,threshold = trip.temperature - trip.hysteresis(利用迟滞防止频繁切换)
定义于 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;
};用于在注册 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; // 温度线性调整偏移
};连接 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 中使用)定义于 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 和调试系统记录触发原因。
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() |
温度更新函数 __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 行)。
临界温度处理(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);
}迟滞防止在 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);
}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_TEMP(thermal_of.c:89 行),允许用户运行时调整温度阈值,而不需要重新编译驱动。
对于临界 trip(CRITICAL 类型),驱动通常不设置 RW_TEMP 标志,防止用户随意提高临界温度导致硬件损坏。
trips_high 和 trips_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]
所有 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源码: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->temperature 与 tz->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
源码: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 作为标志,后续调用直接返回。
源码: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_level 由 get_trip_level() 计算,等于当前温度超过了多少个 trip 点的数量(第 18-42 行)。
源码: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 直接控制冷却级别。
源码: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 频率上限请求
};关键设计(源码注释,第 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;
}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 核心负责实际的频率切换,解耦合且安全。
当启用 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;
}注册函数(第 514-611 行)__cpufreq_cooling_register() 的关键步骤:
- 统计 CPUFreq policy 中有效频率档位数
i - 设置
max_level = i - 1 - 初始化基础 ops(get/set max/cur state)
- 若能量模型(EM)有效(
em_is_sane()检查),追加 IPA 功率 ops - 通过
freq_qos_add_request()注册 QoS 请求(初始无限制) - 调用
thermal_of_cooling_device_register()注册到 thermal 框架
两个公开 API:
cpufreq_cooling_register(policy):不带 DT 节点of_cpufreq_cooling_register(policy):带 DT 节点,支持 EM 和 IPA
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 框架自己管理频率恢复。
源码: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 行)。
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;
}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);
}
}源码: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 阈值时触发中断。
源码: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 次注入周期未被中断才认为校准有效
源码:drivers/thermal/intel/intel_pch_thermal.c
PCH(Platform Controller Hub)热传感器监测芯片组温度,通过 MMIO 寄存器读取。
源码: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_PERFORMANCE 和 THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY 属性携带具体数值。
RAPL 通过 MSR 接口(MSR_PKG_POWER_LIMIT、MSR_PP0_POWER_LIMIT 等)限制 CPU 包、核心、图形、内存子系统的功耗,是 Intel 平台功耗管理的重要组成部分。
RAPL 接口通过 PowerCap 框架(drivers/powercap/intel_rapl_common.c)暴露到用户空间(/sys/class/powercap/),虽然不直接使用 Thermal 框架,但与热管理密切相关:当 RAPL 限制功耗时,会直接影响 CPU 的发热量,是与 thermal framework 并行运行的功耗控制手段。
源码:drivers/acpi/thermal.c
ACPI 规范定义了 Thermal Zone 对象,BIOS 通过 AML(ACPI Machine Language)描述热区配置。Linux 的 ACPI thermal 驱动将 ACPI 热区转换为内核 thermal zone,并将 ACPI 冷却设备(风扇、处理器)绑定为 cooling device。
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 行)。
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 |
被动冷却时间常数 | 传递给内核 |
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 0xF1ACPI 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。
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源码: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)
// 每个 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 信息数组
};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功率分配分两轮进行:
第一轮:按加权请求功率比例分配
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 已满而浪费预算。
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)
// 存在多个 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())。
要使用 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;sustainable_power 是 IPA 工作的核心参数,表示热区在长期运行下可以持续散发的最大功率(单位 mW)。可通过以下途径配置:
- 设备树:在 thermal zone 节点中设置
sustainable-power属性 - thermal_zone_params:驱动注册时直接传入
tzp->sustainable_power - 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源码: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 的温度。
// 每种 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(临界温度,可选)
};同类型(相同 tz->type)的多个 thermal zone 共享同一个 hwmon 设备(thermal_hwmon_lookup_by_type() 第 91-109 行)。类型字符串中的 - 会被替换为 _(hwmon 命名约定要求使用下划线)。
驱动可以通过 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,
};tempN_input:当前温度(毫摄氏度),通过thermal_zone_get_temp()读取tempN_crit:临界温度(毫摄氏度),通过tz->ops.get_crit_temp()读取(仅当驱动实现该回调时暴露)
源码: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;每个 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 | 重置统计数据 |
每个绑定关系(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)
通过向 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_bangmode_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
源码:drivers/thermal/thermal_netlink.c
Thermal 框架通过 Generic Netlink 向用户空间发送事件通知。Family 名称为 "thermal"(THERMAL_GENL_FAMILY_NAME,include/uapi/linux/thermal.h 第 22 行),版本 0x02。
两个多播组(第 23-25 行):
"sampling"(THERMAL_GENL_SAMPLING_GROUP_NAME):周期性温度采样数据(高频)"event"(THERMAL_GENL_EVENT_GROUP_NAME):状态变化事件(低频)
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, // 用户阈值被穿越(降温方向)
};用户空间可以通过 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 实现。
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 },
};源码: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);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()
一个热区可以引用多个温度传感器,取其最大温度:
thermal-zones {
soc-thermal {
thermal-sensors = <&tsens 1>, <&tsens 2>; /* 多个传感器 */
/* thermal 框架会取最高温度作为热区温度 */
};
};
部分 SoC(如高通 Snapdragon)使用多个 TSENS(热传感器)通道,将多个核心的温度汇聚到一个热区进行统一管理。
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若启用 CONFIG_THERMAL_DEBUGFS(drivers/thermal/thermal_debugfs.c),在 /sys/kernel/debug/thermal/ 下提供详细的调试信息:
- 每个 thermal zone 的 trip 统计(在各 trip 处停留的时间)
- Cooling device 的状态转换历史(
transition目录) - IPA 的功率分配历史
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; // 重置,方便恢复
}
}thermal_core.c 维护全局标志 thermal_pm_suspended(第 42 行),在系统挂起期间阻止温度轮询。恢复时通过 TZ_STATE_FLAG_RESUMING 标志和 THERMAL_TZ_RESUME 事件通知 governor 重新初始化状态。
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) // 启用,触发首次温度检查
__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 属性
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
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)
Linux Thermal 子系统是一个高度模块化的框架,其设计体现了以下核心思想:
- 关注点分离:温度感知(thermal zone)、冷却执行(cooling device)、控制策略(governor)三者完全解耦
- 可组合性:一个 thermal zone 可绑定多个 cooling device,一个 cooling device 可被多个 thermal zone 复用
- 策略可插拔:governor 以模块方式注册,运行时可通过 sysfs 动态切换
- 双模态驱动:同时支持轮询(polling)和中断驱动两种工作模式,中断驱动通过
ops.set_trips()配置硬件阈值 - 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 | 适用场景 | 特点 |
|---|---|---|
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 数据结构 |
源码:drivers/thermal/thermal_thresholds.c,drivers/thermal/thermal_thresholds.h
用户阈值(User Threshold)是 Linux 6.x 引入的新特性,允许用户空间程序通过 Netlink 接口动态注册温度阈值,当温度穿越这些阈值时收到异步通知,无需轮询。这是比 UEvent 更高效的事件驱动机制。
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 维护)。
添加阈值(第 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] 范围时,硬件触发中断,内核随即检查是否有用户阈值被穿越。
通过 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 事件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 切换)
};__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;
}当驱动实现 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);
}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;
}源码: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 = 100,idle_inject_set_duration() 设置每个 idle 注入周期的持续时间(默认 10ms)。
源码: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)。
源码: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 */
};
源码: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; // 校准因子(等比例缩放)
};当 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 # 最高温度
│ │ └── ...
│ └── ...
└── ...
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)会浪费内存且大部分为零。使用哈希链表只记录实际发生过的转换,内存高效。
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; // 期间平均温度(加权)
};# 查看 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_tempthermal_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 阶段注册时,框架已经就绪。
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 中,其注册顺序由链接顺序决定,通常为:
step_wise(大多数配置的默认 governor)bang_bangfair_shareuser_spacepower_allocator(若启用)
首个注册成功的 governor 成为 def_governor(默认 governor)。
当 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
这种互相扫描的设计确保无论哪一方先注册,最终都能完成绑定。
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;
}
}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,因此会按照首次初始化逻辑处理(逐步增加冷却)。
thermal_zone_device.resume 字段(completion)用于同步恢复过程:
// 等待 RESUMING 标志清除后再进行热区注销
// 防止注销时热区仍处于恢复状态
wait_for_completion(&tz->resume);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) 宏自动管理
关键函数使用 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 保护下调用
...
}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 风险,特别是在函数有多个返回路径时。
在一次温度更新中,可能有多个 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 标志被清除。
源码:drivers/cpufreq/cpufreq_cooling.c,include/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;
};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 和实际频率不匹配导致计算错误)。
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 参数)。
源码: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 },
};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;
}许多 SoC 的温度传感器需要出厂校准数据(存储在 eFuse 中):
// rockchip_thermal.c:通过 NVMEM 框架读取校准数据
nvmem_cell_read(cell, &len); // 读取 efuse 中的 trim 值
// trim 值用于修正 ADC 值到温度的转换:
// actual_temp = nominal_temp + trim * trim_slopethermal_zone_params 中的 slope 和 offset 参数用于对原始温度进行线性补偿,解决传感器精度不足或安装位置偏差导致的系统误差:
compensated_temp = (raw_temp * slope) / 1024 + offset
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;
}通过设备树:
&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, // 偏移(毫摄氏度)
};症状:CPU 频率在高低频之间频繁切换,性能不稳定。
原因:
- trip point 的 hysteresis 过小(或为 0)
- 使用 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症状: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可能原因:
- Trip point 温度设置过低(BIOS/DT 配置问题)
- IPA 的
sustainable_power设置过低 - 温度传感器安装位置不合理(如靠近热源)
诊断:
# 查看内核日志
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调整 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_pipeIPA 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°Cpower_range = 4700:分配的总功率预算 4.7Wp = -300:比例项贡献 -300mW
当 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 级别的警告,提醒管理员注意。
| 配置项 | 说明 |
|---|---|
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=立即) |
| 配置项 | 说明 |
|---|---|
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 为默认 |
| 配置项 | 说明 |
|---|---|
CONFIG_CPU_THERMAL |
CPU 频率冷却设备(cpufreq_cooling) |
CONFIG_DEVFREQ_THERMAL |
devfreq 冷却设备 |
CONFIG_THERMAL_CPUFREQ_COOLING |
cpufreq 冷却(通用) |
CONFIG_INTEL_POWERCLAMP |
Intel 空闲注入冷却 |
CONFIG_PCIE_THERMAL |
PCIe 链路速度冷却 |
| 配置项 | 说明 |
|---|---|
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 热传感器 |
这个配置项定义了系统在触发 CRITICAL trip 后的强制关机延迟(毫秒)。
- 设置为 0(默认):温度超临界后立即触发关机
- 设置为正值(如 5000):等待 5 秒后如果系统未正常关机才强制关机
这实际上是一个安全后备机制:通常 thermal_zone_device_critical() 调用 orderly_poweroff() 触发正常关机流程。但若正常关机因某种原因卡住,CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS 定义的超时后会触发 kernel_power_off() 强制关机,防止硬件损坏。
+-----------------------+
| 硬件温度传感器 |
| (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 |
| 频率切换 |
| 硬件执行降频 |
+----------------+
+-----------+ +-----------+ +-----------+
| 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 限制
+------------------------------------------------------------------+
| 用户空间(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 分析生成