열 관리 (Thermal Management)
Linux 커널 Thermal 서브시스템은 CPU/GPU/SoC 센서 온도를 기반으로 thermal zone, trip point, cooling map을 구성하고 cooling device를 제어해 과열을 방지합니다. step_wise/fair_share/power_allocator governor의 제어 전략, cpufreq/devfreq·fan·throttle 연동, ACPI·Device Tree·hwmon 통합, 열 한계와 성능 사이의 정책 설계, 운영 환경에서의 throttling 원인 분석과 디버깅(Debugging) 절차를 상세히 다룹니다.
핵심 요약
- Thermal Zone — CPU, GPU 등 온도를 측정하는 하드웨어 영역. 온도 센서와 trip point를 포함합니다.
- Trip Point — 온도 임계값. active, passive, hot, critical 4가지 타입으로 cooling 정책을 다르게 트리거합니다.
- Cooling Device — 온도를 낮추는 장치. CPU 주파수 제한, 팬 속도 증가, GPU throttling 등.
- Thermal Governor — thermal zone과 cooling device를 연결하는 정책 알고리즘. step_wise, power_allocator 등.
- 제어 루프 — 온도 샘플링 → trip 비교 → governor 판단 → cooling 조정 → 온도 하강 → 반복.
단계별 이해
- Thermal Zone 확인
/sys/class/thermal/thermal_zone*/에서 현재 시스템의 온도 센서와 trip point를 확인합니다. - Cooling Device 파악
/sys/class/thermal/cooling_device*/에서 사용 가능한 cooling 수단과 상태 범위를 확인합니다. - Governor 정책 이해
step_wise, power_allocator 등 governor 알고리즘이 온도에 따라 cooling을 어떻게 조정하는지 학습합니다. - Tracing으로 동작 추적
thermal tracepoint를 활성화하여 온도 변화와 cooling 동작을 실시간(Real-time)으로 관찰합니다.
개요
Thermal Management 서브시스템은 하드웨어 과열을 방지하고 성능과 전력 소비를 균형있게 조절하는 커널의 핵심 컴포넌트입니다.
주요 구성 요소
- Thermal Zone — 온도를 측정할 수 있는 하드웨어 영역 (CPU 코어, GPU 다이, SoC 등)
- Cooling Device — 온도를 낮추기 위한 장치 (CPU 주파수 제한, 팬 속도 제어, GPU throttling)
- Thermal Governor — thermal zone과 cooling device 간의 정책 결정 알고리즘
- Trip Point — 온도 임계값. 특정 온도에 도달하면 cooling action 트리거
아키텍처
Thermal Zone
thermal_zone_device 구조체(Struct)
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
struct device device;
struct thermal_zone_device_ops *ops;
const struct thermal_zone_params *tzp;
struct thermal_governor *governor;
struct list_head thermal_instances; /* cooling device 바인딩 */
int temperature; /* 현재 온도 (milli-Celsius) */
int last_temperature; /* 이전 측정 온도 */
int polling_delay; /* 폴링 주기 (msec) */
int passive; /* passive cooling 활성화 */
bool forced_passive;
struct thermal_zone_device_ops *ops;
struct list_head node;
};
코드 설명
- id, typethermal zone의 고유 식별자와 이름입니다.
type은THERMAL_NAME_LENGTH(20) 이하의 문자열로, sysfs에서/sys/class/thermal/thermal_zone0/type으로 노출됩니다. - ops온도 읽기, trip point 조회, cooling device 바인딩 등 thermal zone이 지원하는 콜백 함수 테이블입니다.
include/linux/thermal.h에 정의됩니다. - governor이 thermal zone에 연결된 cooling 정책 governor입니다.
step_wise,power_allocator등이 할당됩니다. - thermal_instances이 zone에 바인딩된 cooling device 목록을 연결 리스트로 관리합니다.
drivers/thermal/thermal_core.c에서bind콜백 호출 시 추가됩니다. - temperature, last_temperature현재와 이전 측정 온도(milli-Celsius 단위)입니다. governor가 온도 변화 방향을 판단할 때 두 값을 비교합니다.
- polling_delay온도 폴링 주기(밀리초)입니다. 0이면 인터럽트 기반으로 동작하며,
passive상태에서는 별도의passive_delay가 적용됩니다.
thermal_zone_device_ops
struct thermal_zone_device_ops {
int (*bind)(struct thermal_zone_device *,
struct thermal_cooling_device *);
int (*unbind)(struct thermal_zone_device *,
struct thermal_cooling_device *);
int (*get_temp)(struct thermal_zone_device *, int *temp);
int (*set_trips)(struct thermal_zone_device *, int, int);
int (*get_mode)(struct thermal_zone_device *,
enum thermal_device_mode *);
int (*set_mode)(struct thermal_zone_device *,
enum thermal_device_mode);
int (*get_trip_type)(struct thermal_zone_device *, int,
enum thermal_trip_type *);
int (*get_trip_temp)(struct thermal_zone_device *, int, int *);
int (*get_crit_temp)(struct thermal_zone_device *, int *);
int (*notify)(struct thermal_zone_device *, int,
enum thermal_trip_type);
};
코드 설명
- bind / unbindthermal zone과 cooling device를 연결·해제하는 콜백입니다. zone 등록 시 커널이 모든 cooling device에 대해
bind를 호출하여 적합한 device를 연결합니다. - get_temp현재 온도를 milli-Celsius 단위로 읽어오는 콜백입니다. 센서 드라이버가 반드시 구현해야 하며,
drivers/thermal/thermal_core.c의 폴링 루프에서 주기적으로 호출됩니다. - set_trips하드웨어 인터럽트 기반 센서에서 상한·하한 온도를 설정합니다. 폴링 없이 온도가 범위를 벗어날 때 IRQ로 알림받는 방식에 사용됩니다.
- get_trip_type / get_trip_temptrip point의 유형(
THERMAL_TRIP_PASSIVE,THERMAL_TRIP_CRITICAL등)과 임계 온도를 조회합니다. governor가 cooling 판단 시 사용합니다. - get_crit_temp시스템 강제 종료 임계 온도를 반환합니다. 이 온도에 도달하면
orderly_poweroff()가 호출됩니다. - notifytrip point가 트리거될 때 호출되는 알림 콜백입니다. 드라이버가 추가 조치(로깅, 알람 등)를 수행할 수 있습니다.
Trip Points (온도 임계값)
Trip point는 특정 온도에 도달했을 때 cooling action을 트리거하는 임계값입니다.
| Trip Type | 설명 | 동작 |
|---|---|---|
THERMAL_TRIP_ACTIVE |
능동적 냉각 시작 | 팬 속도 증가, 주파수 감소 |
THERMAL_TRIP_PASSIVE |
수동적 냉각 시작 | CPU 주파수 제한, GPU throttling |
THERMAL_TRIP_HOT |
고온 경고 | 로그 기록, 경고 알림 |
THERMAL_TRIP_CRITICAL |
임계 온도 | 시스템 강제 종료 (orderly_poweroff) |
Thermal Zone 등록
struct thermal_zone_device *
thermal_zone_device_register(
const char *type,
int trips,
int mask,
void *devdata,
struct thermal_zone_device_ops *ops,
struct thermal_zone_params *tzp,
int passive_delay,
int polling_delay
);
/* 예: ACPI Thermal Zone 등록 */
struct thermal_zone_device *tz;
tz = thermal_zone_device_register("acpitz", 4, 0xf, tz_data,
&acpi_thermal_zone_ops,
&acpi_thermal_params,
10000, 0);
코드 설명
- typethermal zone의 이름 문자열입니다. 예시에서
"acpitz"는 ACPI thermal zone을 나타내며, sysfs의type파일로 확인할 수 있습니다. - trips이 zone이 가진 trip point의 개수입니다. 예시에서
4는 active, passive, hot, critical 4개의 trip을 의미합니다. - maskuserspace에서 수정 가능한 trip point를 비트마스크로 지정합니다.
0xf는 4개의 trip 모두 sysfs를 통해 온도 조정이 가능함을 뜻합니다. - ops, tzp온도 읽기·trip 관리 콜백 테이블과 governor 파라미터(PID 게인 등)를 전달합니다.
drivers/thermal/thermal_core.c에서 이 정보로 zone을 초기화합니다. - passive_delay, polling_delaypassive cooling 활성화 시와 일반 상태의 온도 폴링 주기(밀리초)입니다. 예시에서
10000은 10초마다 폴링,0은 일반 상태에서 폴링 비활성(인터럽트 기반)을 의미합니다.
Cooling Device
thermal_cooling_device 구조체
struct thermal_cooling_device {
int id;
char type[THERMAL_NAME_LENGTH];
struct device device;
struct thermal_cooling_device_ops *ops;
bool updated;
unsigned long max_state; /* 최대 cooling level */
unsigned long cur_state; /* 현재 cooling level */
struct list_head node;
};
코드 설명
- id, typecooling device의 고유 번호와 이름입니다.
type은 sysfs에서/sys/class/thermal/cooling_device0/type으로 확인할 수 있습니다 (예:"thermal-cpufreq-0","Fan"). - opscooling 상태를 조회·설정하는 콜백 함수 테이블입니다. governor가 이 ops를 통해 cooling level을 제어합니다.
- max_state, cur_statecooling device가 지원하는 최대 레벨과 현재 레벨입니다. 0이 냉각 없음,
max_state가 최대 냉각을 의미합니다. CPUFreq cooling의 경우 state가 높을수록 낮은 주파수에 대응합니다. - updatedcooling state가 변경되었는지 표시하는 플래그입니다. governor가 새 state를 설정한 후
true로 설정하며, 실제 적용 시 초기화됩니다.
thermal_cooling_device_ops
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);
int (*get_requested_power)(struct thermal_cooling_device *,
struct thermal_zone_device *, u32 *);
int (*state2power)(struct thermal_cooling_device *,
struct thermal_zone_device *,
unsigned long, u32 *);
int (*power2state)(struct thermal_cooling_device *,
struct thermal_zone_device *, u32,
unsigned long *);
};
코드 설명
- get_max_statecooling device가 지원하는 최대 cooling level을 반환합니다. CPUFreq cooling의 경우 가용 주파수 테이블의 단계 수에서 1을 뺀 값입니다.
- get_cur_state / set_cur_state현재 cooling level을 조회하거나 설정합니다. governor가 온도에 따라
set_cur_state를 호출하여 냉각 강도를 조절합니다.include/linux/thermal.h에 정의됩니다. - get_requested_powerIPA(Power Allocator) governor 전용 콜백으로, cooling device의 현재 소비 전력(mW)을 반환합니다. 이 값으로 전력 예산 분배를 계산합니다.
- state2power / power2statecooling state와 전력(mW) 간 양방향 변환 콜백입니다. IPA가 전력 예산을 cooling state로 변환할 때 사용합니다.
drivers/thermal/cpufreq_cooling.c에서 CPU 전력 모델 기반으로 구현됩니다.
Cooling Device 종류
- CPU Frequency Cooling (
cpufreq_cooling_register) — CPU 주파수를 낮춰 전력 소비 감소 - Fan Cooling — PWM 제어로 팬 속도 조절
- GPU Throttling — GPU 주파수/전압 제한
- Devfreq Cooling (
devfreq_cooling_register) — Memory Controller, GPU 등 devfreq 장치
Cooling Device 등록
/* CPUFreq Cooling Device 등록 */
struct thermal_cooling_device *cdev;
struct cpumask clip_cpus;
cpumask_set_cpu(0, &clip_cpus);
cdev = cpufreq_cooling_register(&clip_cpus);
/* Fan Cooling Device 등록 */
struct thermal_cooling_device *fan_cdev;
fan_cdev = thermal_cooling_device_register("Fan", fan_data,
&fan_cooling_ops);
Thermal Governor
Thermal Governor는 thermal zone의 온도에 따라 cooling device를 제어하는 정책 알고리즘입니다.
Governor 종류
| Governor | 설명 | 적용 대상 |
|---|---|---|
step_wise |
온도 변화에 따라 단계적으로 cooling state 조정 | 범용 (기본값) |
fair_share |
여러 cooling device에 cooling 부하를 공평하게 분배 | 다중 cooling device |
power_allocator |
전력 예산 기반 제어. PID 컨트롤러 사용 | 모바일 SoC, ARM big.LITTLE |
bang_bang |
On/Off 방식. 임계값 초과 시 full cooling | 단순 Fan 제어 |
user_space |
userspace 데몬이 cooling 제어 | 커스텀 열 정책 |
Step-Wise Governor (기본)
step_wise governor는 온도가 trip point를 초과하면 cooling state를 한 단계씩 증가시키고, 온도가 낮아지면 한 단계씩 감소시킵니다.
/* Step-Wise Governor 알고리즘 */
if (temperature > trip_temp) {
/* 온도 상승: cooling state 증가 */
new_state = cur_state + 1;
if (new_state > max_state)
new_state = max_state;
} else {
/* 온도 하강: cooling state 감소 */
if (cur_state > 0)
new_state = cur_state - 1;
}
set_cur_state(cdev, new_state);
Fair-Share Governor 알고리즘
/* Fair-Share: 각 cooling device에 가중치 비례로 cooling 분배 */
/* 온도 초과 비율 계산 */
percentage = (cur_temp - trip_temp) * 100 / (max_temp - trip_temp);
if (percentage > 100)
percentage = 100;
/* 각 cooling device에 가중치 기반 state 할당 */
for (i = 0; i < tz->cdevs_count; i++) {
weight = tz->cdevs_weight[i];
total_weight += weight;
}
for (i = 0; i < tz->cdevs_count; i++) {
cdev = tz->cdevs[i];
weight_pct = tz->cdevs_weight[i] * 100 / total_weight;
target_state = cdev->max_state * percentage * weight_pct / 10000;
set_cur_state(cdev, target_state);
}
Power Allocator Governor
ARM 모바일 SoC에서 널리 사용됩니다. PID(Proportional-Integral-Derivative) 컨트롤러를 사용해 전력 예산을 동적으로 할당합니다.
struct thermal_zone_params {
char governor_name[THERMAL_NAME_LENGTH];
bool no_hwmon;
int num_tbps;
struct thermal_bind_params *tbp;
/* Power Allocator 전용 */
s32 k_po; /* PID proportional gain (overshoot) */
s32 k_pu; /* PID proportional gain (undershoot) */
s32 k_i; /* PID integral gain */
s32 k_d; /* PID derivative gain */
s32 integral_cutoff;
int sustainable_power; /* 지속 가능 전력 (mW) */
};
Governor 선택
# sysfs로 governor 변경 */
# cat /sys/class/thermal/thermal_zone0/available_policies
step_wise fair_share power_allocator bang_bang user_space
# echo power_allocator > /sys/class/thermal/thermal_zone0/policy
IPA (Intelligent Power Allocation)
IPA(Power Allocator Governor)는 ARM에서 개발한 열 관리 알고리즘으로, PID 컨트롤러를 사용하여 sustainable_power 예산을 여러 cooling device에 동적으로 분배합니다. 온도를 목표값(control_temp)에 수렴시키면서 성능을 최대한 유지하는 것이 목표입니다.
IPA 알고리즘 핵심
/* drivers/thermal/gov_power_allocator.c */
static int power_allocator_throttle(
struct thermal_zone_device *tz, int trip)
{
s64 err, p, i, d, power_range;
int control_temp = tz->trips[trip].temperature;
int cur_temp;
thermal_zone_get_temp(tz, &cur_temp);
err = control_temp - cur_temp;
/* Proportional: overshoot → k_po, undershoot → k_pu */
if (err < 0)
p = mul_frac(tz->tzp->k_po, err);
else
p = mul_frac(tz->tzp->k_pu, err);
/* Integral: 누적 오차 (integral_cutoff으로 제한) */
if (abs(err) < tz->tzp->integral_cutoff)
params->err_integral += err;
i = mul_frac(tz->tzp->k_i, params->err_integral);
/* Derivative: 오차 변화율 */
d = mul_frac(tz->tzp->k_d, err - params->prev_err);
params->prev_err = err;
/* 총 power budget */
power_range = tz->tzp->sustainable_power + p + i + d;
if (power_range < 0)
power_range = 0;
/* cooling device들에 power 분배 */
divvy_up_power(tz, power_range);
return 0;
}
코드 설명
- err = control_temp - cur_temp목표 온도와 현재 온도의 차이(오차)를 계산합니다. 양수면 목표 미만(undershoot), 음수면 목표 초과(overshoot)입니다.
- k_po / k_pu 분기비례항(P)의 게인을 온도 상태에 따라 다르게 적용합니다. 목표 초과 시
k_po로 완만하게 전력을 줄이고, 목표 미만 시k_pu로 빠르게 전력을 늘립니다.drivers/thermal/gov_power_allocator.c에 구현되어 있습니다. - integral_cutoff적분항(I) 누적은 오차가
integral_cutoff이내일 때만 수행됩니다. 큰 오차에서 적분이 과도하게 누적되는 windup 현상을 방지합니다. - k_d, prev_err미분항(D)은 오차의 변화율을 반영합니다. 급격한 온도 변화에 선제적으로 대응하여 오버슈트를 억제합니다.
- sustainable_power + p + i + d기본 지속 가능 전력(
sustainable_power)에 PID 보정값을 더해 총 전력 예산을 산출합니다. 음수가 되면 0으로 클램핑합니다. - divvy_up_power산출된 전력 예산을 각 cooling device의
contribution가중치에 비례하여 분배합니다. 각 device는 할당받은 전력을power2state로 cooling state로 변환합니다.
thermal_power_actor 등록
/* Cooling device가 IPA와 연동하려면 power ops 필수 */
struct thermal_cooling_device_ops {
/* 기본 ops */
int (*get_max_state)(...);
int (*get_cur_state)(...);
int (*set_cur_state)(...);
/* Power Actor ops (IPA 필수) */
int (*get_requested_power)(..., u32 *power);
int (*state2power)(..., unsigned long state, u32 *power);
int (*power2state)(..., u32 power, unsigned long *state);
};
/* get_requested_power: 현재 소비 전력 조회 */
/* state2power: cooling state → mW 변환 */
/* power2state: mW → cooling state 역변환 */
IPA Device Tree 바인딩
/* IPA를 위한 thermal-zone DT 설정 */
cpu-thermal {
polling-delay-passive = <100>;
polling-delay = <1000>;
thermal-sensors = <&tmu 0>;
/* IPA 전용 속성 */
sustainable-power = <5000>; /* 5W (mW 단위) */
trips {
target: target-trip {
temperature = <75000>; /* IPA 목표 온도 */
hysteresis = <0>;
type = "passive";
};
};
cooling-maps {
cpu-map {
trip = <&target>;
cooling-device = <&cpu_cooling 0 10>;
contribution = <1024>;
};
gpu-map {
trip = <&target>;
cooling-device = <&gpu_cooling 0 5>;
contribution = <512>;
};
};
};
IPA sysfs 튜닝
# IPA 파라미터 확인/조정
TZ=/sys/class/thermal/thermal_zone0
# sustainable_power 조회/설정 (mW)
# cat $TZ/sustainable_power
5000
# echo 4000 > $TZ/sustainable_power # 4W로 낮추기
# PID 게인 확인 (자동 계산 또는 DT 지정)
# cat $TZ/k_po # proportional gain (overshoot)
# cat $TZ/k_pu # proportional gain (undershoot)
# cat $TZ/k_i # integral gain
# cat $TZ/k_d # derivative gain
# integral_cutoff: 이 온도차 이내에서만 적분항 누적
# cat $TZ/integral_cutoff
k_po / k_pu 자동 계산
IPA는 sustainable_power로부터 PID 게인을 자동 계산합니다:
k_po = sustainable_power / (control_temp - switch_on_temp)k_pu = 2 * sustainable_power / (control_temp - switch_on_temp)
예시: sustainable_power=5000, control_temp=75°C, switch_on=65°C인 경우:
k_po = 5000 / (75000 - 65000) = 0.5 mW/mCk_pu = 10000 / (75000 - 65000) = 1.0 mW/mC
overshoot 시 (온도 > target)에는 k_po로 완만하게 감소하고, undershoot 시 (온도 < target)에는 k_pu로 빠르게 증가합니다.
| 파라미터 | 설명 | 기본값 | 튜닝 방향 |
|---|---|---|---|
sustainable_power |
목표 온도에서 유지 가능한 전력 (mW) | DT 정의 | 높을수록 성능↑, 온도↑ |
k_po |
비례 게인 (overshoot) | 자동 계산 | 높으면 과열 시 빠른 제한 |
k_pu |
비례 게인 (undershoot) | 자동 계산 | 높으면 냉각 시 빠른 복원 |
k_i |
적분 게인 | 자동 계산 | 높으면 정상상태 오차 제거 빠름 |
k_d |
미분 게인 | 0 | 높으면 급격한 변화에 민감 |
integral_cutoff |
적분항 활성화 온도 범위 | 0 (항상) | 좁으면 목표 근처에서만 적분 |
| 항목 | IPA (power_allocator) | Step-Wise |
|---|---|---|
| 제어 방식 | PID + 전력 예산 분배 | 단순 단계 증감 |
| 다중 cooling device | 가중치 기반 최적 분배 | 개별 독립 제어 |
| 성능 유지 | 목표 온도에서 최대 성능 | 보수적 제한 |
| Power model 필요 | 필수 (power actor ops) | 불필요 |
| 적합 환경 | 모바일 SoC (ARM big.LITTLE) | 범용 (기본값) |
| 튜닝 복잡도 | 높음 (PID 파라미터) | 낮음 |
ACPI Thermal Zone
UEFI/ACPI 플랫폼에서는 ACPI 펌웨어(Firmware)가 thermal zone과 cooling method를 정의합니다.
ACPI Thermal Methods
| Method | 설명 |
|---|---|
_TMP |
현재 온도 반환 (1/10 Kelvin 단위) |
_CRT |
Critical 온도 임계값 |
_HOT |
Hot 온도 임계값 |
_PSV |
Passive 온도 임계값 |
_ACx |
Active cooling 임계값 (x = 0~9) |
_ALx |
Active cooling 대응 팬 리스트 |
_PSL |
Passive cooling 대상 프로세서 리스트 |
_TC1, _TC2, _TSP |
Passive cooling 상수 |
ACPI Thermal Zone 예시
/* ACPI DSDT에서 Thermal Zone 정의 예시 (ASL) */
ThermalZone (TZ00) {
Method (_TMP, 0) {
/* EC(Embedded Controller)에서 온도 읽기 */
Return (\_SB.PCI0.LPCB.EC0.TMP0) /* 0.1K 단위 */
}
Method (_CRT) { Return (3732) } /* 100°C (373.2K) */
Method (_PSV) { Return (3632) } /* 90°C */
Method (_AC0) { Return (3532) } /* 80°C */
Method (_AL0) { Return (Package(){\_SB.FAN0}) }
Name (_PSL, Package(){\_SB.CPU0, \_SB.CPU1}) /* Passive 대상 CPU */
}
Device Tree Thermal Binding
ARM/RISC-V 등 Device Tree 기반 플랫폼에서는 thermal zone, trip point, cooling map을 DT 노드로 정의합니다. 커널의 thermal_of.c가 DT를 파싱하여 thermal framework에 등록합니다.
완전한 DT 예시
/* arch/arm64/boot/dts/vendor/soc.dtsi */
thermal-zones {
cpu-thermal {
polling-delay-passive = <100>; /* passive cooling 시 100ms */
polling-delay = <1000>; /* 일반 1초 */
thermal-sensors = <&tmu 0>; /* 센서 phandle + ID */
trips {
cpu_alert0: cpu-alert0 {
temperature = <70000>; /* 70°C */
hysteresis = <2000>; /* 2°C */
type = "passive";
};
cpu_alert1: cpu-alert1 {
temperature = <85000>; /* 85°C */
hysteresis = <2000>;
type = "passive";
};
cpu_crit: cpu-crit {
temperature = <100000>; /* 100°C */
hysteresis = <0>;
type = "critical";
};
};
cooling-maps {
map0 {
trip = <&cpu_alert0>;
cooling-device =
<&cpu0_cooling THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
};
map1 {
trip = <&cpu_alert1>;
cooling-device =
<&cpu0_cooling 5 THERMAL_NO_LIMIT>;
/* state 5부터 max까지 */
};
};
};
};
센서 바인딩
/* 센서 드라이버에서 thermal zone 자동 등록 */
static int my_sensor_probe(struct platform_device *pdev)
{
struct my_sensor_data *data;
struct thermal_zone_device *tz;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
/* DT thermal-zones에서 이 센서를 참조하면 자동 연결 */
tz = devm_thermal_of_zone_register(
&pdev->dev, 0, /* sensor ID */
data, &my_sensor_ops);
if (IS_ERR(tz))
return PTR_ERR(tz);
data->tz = tz;
return 0;
}
static const struct thermal_zone_device_ops my_sensor_ops = {
.get_temp = my_sensor_get_temp,
.set_trips = my_sensor_set_trips, /* IRQ 기반 trip 설정 */
};
.dtsi에서 multi-zone 정의
/* 여러 thermal zone을 하나의 dtsi에서 정의 */
thermal-zones {
cpu-big-thermal {
thermal-sensors = <&tmu 0>; /* big 코어 센서 */
/* ... trips, cooling-maps ... */
};
cpu-little-thermal {
thermal-sensors = <&tmu 1>; /* LITTLE 코어 센서 */
};
gpu-thermal {
thermal-sensors = <&tmu 2>; /* GPU 센서 */
};
ddr-thermal {
thermal-sensors = <&tmu 3>; /* DDR 메모리 센서 */
};
};
복수 Cooling Device 바인딩
/* 하나의 trip에 여러 cooling device를 바인딩 */
cooling-maps {
cpu-passive {
trip = <&cpu_passive_trip>;
cooling-device = <&cpu_cooling 0 10>;
contribution = <1024>; /* IPA 가중치 */
};
gpu-passive {
trip = <&cpu_passive_trip>; /* 같은 trip 공유 */
cooling-device = <&gpu_cooling 0 5>;
contribution = <512>;
};
fan-active {
trip = <&cpu_active_trip>;
cooling-device = <&fan0 0 4>;
};
};
코드 설명
- trip = <&cpu_passive_trip>각 cooling map이 어떤 trip point에서 활성화되는지를 phandle로 참조합니다. 여러 cooling device가 동일한 trip을 공유할 수 있습니다.
- cooling-device = <&cpu_cooling 0 10>cooling device의 phandle과 사용 가능한 state 범위(최소 0, 최대 10)를 지정합니다. 커널의
drivers/thermal/thermal_of.c가 이 바인딩을 파싱합니다. - contributionIPA governor에서 전력 예산 분배 시 사용되는 가중치입니다. 예시에서 CPU(
1024)가 GPU(512)의 2배 비중을 가집니다.step_wisegovernor에서는 무시됩니다. - fan-active팬은 별도의 active trip에 바인딩됩니다. active trip은 passive보다 높은 온도에 설정되어, passive cooling으로 부족할 때 팬이 동작합니다.
| DT 속성 | 타입 | 설명 |
|---|---|---|
thermal-sensors |
phandle + args | 온도 센서 참조 (phandle + sensor ID) |
polling-delay |
u32 (ms) | 일반 폴링(Polling) 주기 |
polling-delay-passive |
u32 (ms) | passive cooling 시 폴링 주기 |
sustainable-power |
u32 (mW) | IPA용 지속 가능 전력 |
temperature |
s32 (mC) | trip point 온도 (milli-Celsius) |
hysteresis |
u32 (mC) | trip 히스테리시스 |
type |
string | "active", "passive", "hot", "critical" |
cooling-device |
phandle + min + max | cooling device phandle + state 범위 |
contribution |
u32 | IPA power allocation 가중치 |
#cooling-cells |
u32 | cooling device specifier 셀 수 (보통 2) |
| 항목 | Device Tree | ACPI |
|---|---|---|
| 플랫폼 | ARM, RISC-V, MIPS | x86, ARM ACPI |
| 정의 위치 | .dts / .dtsi 파일 | ACPI DSDT/SSDT (ASL) |
| 파싱 | thermal_of.c | drivers/acpi/thermal.c |
| 센서 바인딩 | thermal-sensors phandle | _TMP ACPI 메서드 |
| Trip point | trips { } 노드 | _CRT, _PSV, _HOT, _ACx |
| Cooling 바인딩 | cooling-maps { } 노드 | _PSL, _ALx |
| 유연성 | 높음 (사용자 정의) | 제한적 (펌웨어 의존) |
sysfs 인터페이스
Thermal Zone sysfs
Cooling Device sysfs
sysfs 사용 예시
# 모든 thermal zone 온도 확인
# for tz in /sys/class/thermal/thermal_zone*; do
echo "$tz: $(cat $tz/type) = $(cat $tz/temp | awk '{print $1/1000}')°C"
done
# Cooling device 상태 확인
# cat /sys/class/thermal/cooling_device0/type
Processor
# cat /sys/class/thermal/cooling_device0/cur_state
0
# cat /sys/class/thermal/cooling_device0/max_state
10
# Governor 변경
# echo power_allocator > /sys/class/thermal/thermal_zone0/policy
hwmon 통합
Thermal 서브시스템은 hwmon 센서와 통합되어 다양한 온도 센서를 thermal zone으로 노출할 수 있습니다.
/* hwmon thermal zone 자동 생성 (CONFIG_THERMAL_HWMON) */
struct thermal_hwmon_device {
struct device *device;
int count;
struct list_head tz_list;
struct list_head node;
};
/* /sys/class/hwmon/hwmon0/temp1_input과 연동 */
devm_thermal_of_zone_register() 자동 등록
hwmon 센서 드라이버에서 devm_thermal_of_zone_register()를 호출하면 Device Tree의 thermal-sensors phandle을 통해 thermal zone이 자동 생성됩니다.
/* hwmon 센서 드라이버에서 thermal zone 자동 등록 */
static int my_hwmon_get_temp(
struct thermal_zone_device *tz, int *temp)
{
struct my_hwmon_data *data = thermal_zone_device_priv(tz);
int raw = readl(data->regs + TEMP_REG);
*temp = raw_to_millicelsius(raw, data->cal);
return 0;
}
static const struct thermal_zone_device_ops my_tz_ops = {
.get_temp = my_hwmon_get_temp,
.set_trips = my_hwmon_set_trips, /* IRQ 기반 trip 지원 시 */
};
static int my_hwmon_probe(struct platform_device *pdev)
{
struct my_hwmon_data *data;
struct device *hwmon_dev;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
/* hwmon 등록 */
hwmon_dev = devm_hwmon_device_register_with_info(
&pdev->dev, "my_sensor", data,
&my_chip_info, NULL);
/* thermal zone 자동 등록 (DT thermal-sensors 참조 시) */
data->tz = devm_thermal_of_zone_register(
&pdev->dev, 0, data, &my_tz_ops);
if (IS_ERR(data->tz)) {
/* DT에 thermal-zones 없으면 무시 가능 */
if (PTR_ERR(data->tz) != -ENODEV)
return PTR_ERR(data->tz);
data->tz = NULL;
}
return 0;
}
CONFIG_THERMAL_HWMON=y이면 thermal zone이 등록될 때 자동으로 /sys/class/hwmon/hwmonN/temp1_input에 온도를 노출합니다. 반대로 hwmon 센서를 devm_thermal_of_zone_register()로 thermal zone에 연결하면 양방향 통합이 완성됩니다.
CPU Frequency Cooling
CPUFreq cooling device는 CPU 주파수를 제한하여 온도를 낮춥니다.
CPUFreq Cooling 등록
#include <linux/cpu_cooling.h>
struct thermal_cooling_device *cdev;
struct cpumask clip_cpus;
struct cpufreq_policy *policy;
policy = cpufreq_cpu_get(0);
cpumask_copy(&clip_cpus, policy->related_cpus);
cpufreq_cpu_put(policy);
/* Cooling device 등록 */
cdev = cpufreq_cooling_register(&clip_cpus);
if (IS_ERR(cdev))
return PTR_ERR(cdev);
/* OPP 기반 cooling (전력 인식) */
cdev = of_cpufreq_cooling_register(np, &clip_cpus);
Cooling State와 주파수 매핑(Mapping)
CPUFreq cooling device는 가용 주파수를 cooling state로 매핑합니다.
/* 예: max_state=10, 가용 주파수가 11개라면 */
State 0: 2400 MHz /* 최대 주파수 */
State 1: 2200 MHz
State 2: 2000 MHz
...
State 10: 400 MHz /* 최소 주파수 */
OPP → Cooling State 매핑 예시
| Cooling State | 주파수 (MHz) | 전압 (mV) | 예상 전력 (mW) |
|---|---|---|---|
| 0 (최대 성능) | 2400 | 1100 | ~4200 |
| 2 | 2000 | 1000 | ~2800 |
| 4 | 1600 | 900 | ~1800 |
| 6 | 1200 | 800 | ~1100 |
| 8 | 800 | 700 | ~550 |
| 10 (최대 절전) | 400 | 600 | ~200 |
Devfreq Cooling
GPU, Memory Controller 등 devfreq 장치를 cooling device로 등록합니다.
#include <linux/devfreq_cooling.h>
struct thermal_cooling_device *
devfreq_cooling_register(struct devfreq *df,
struct devfreq_cooling_power *dfc_power);
/* GPU devfreq cooling 등록 */
struct devfreq *gpu_devfreq;
struct thermal_cooling_device *gpu_cdev;
gpu_cdev = devfreq_cooling_register(gpu_devfreq, NULL);
Devfreq Power Callback 상세
IPA governor와 연동하려면 devfreq_cooling_power 구조체에 전력 콜백(Callback)을 등록해야 합니다.
struct devfreq_cooling_power {
/* 현재 주파수/전압에서 동적 전력 계산 */
int (*get_dynamic_power)(struct devfreq *df,
unsigned long freq,
unsigned long voltage,
unsigned long *power);
/* 정적(누설) 전력 - 온도 의존적 */
int (*get_static_power)(struct devfreq *df,
unsigned long voltage,
unsigned long *power);
/* 동적 전력 계수 (간이 모델용) */
unsigned long dyn_power_coeff;
};
/* 등록 시 power model 포함 */
static struct devfreq_cooling_power my_power = {
.get_dynamic_power = my_get_dyn_power,
.get_static_power = my_get_static_power,
.dyn_power_coeff = 300, /* mW 기준 */
};
cdev = devfreq_cooling_register(devfreq, &my_power);
/* NULL 전달 시 Energy Model 기반 자동 계산 */
팬 제어 (Fan Cooling Device)
PWM(Pulse Width Modulation) 기반 팬 제어는 thermal framework의 cooling device로 등록되어 governor가 직접 팬 속도를 조절합니다. thermal-fan 드라이버는 PWM 채널과 연동하여 cooling state에 따라 duty cycle을 변경합니다.
Fan Cooling Device 등록
/* drivers/hwmon/pwm-fan.c 기반 fan cooling device */
static int pwm_fan_set_cur_state(
struct thermal_cooling_device *cdev,
unsigned long state)
{
struct pwm_fan_ctx *ctx = cdev->devdata;
unsigned int pwm_value;
if (state > ctx->pwm_fan_max_state)
return -EINVAL;
/* cooling state → PWM duty cycle 매핑 */
pwm_value = ctx->pwm_fan_cooling_levels[state];
ctx->pwm_value = pwm_value;
return __set_pwm(ctx, pwm_value);
}
static struct thermal_cooling_device_ops pwm_fan_cooling_ops = {
.get_max_state = pwm_fan_get_max_state,
.get_cur_state = pwm_fan_get_cur_state,
.set_cur_state = pwm_fan_set_cur_state,
};
/* Cooling device 등록 */
cdev = devm_thermal_of_cooling_device_register(
dev, np, "pwm-fan", ctx, &pwm_fan_cooling_ops);
Fan Device Tree 바인딩
/* Device Tree: pwm-fan 노드 */
fan0: pwm-fan {
compatible = "pwm-fan";
pwms = <&pwm1 0 25000>; /* 25kHz PWM */
cooling-levels = <0 102 170 230 255>;
/* state 0: off, 1: 40%, 2: 67%, 3: 90%, 4: 100% */
#cooling-cells = <2>;
};
/* thermal-zones에서 fan 바인딩 */
thermal-zones {
cpu-thermal {
trips {
fan_alert: fan-alert {
temperature = <60000>;
hysteresis = <5000>;
type = "active";
};
};
cooling-maps {
map-fan {
trip = <&fan_alert>;
cooling-device = <&fan0 1 4>; /* state 1~4 */
};
};
};
};
hwmon Fan 속성
# hwmon에서 팬 정보 확인
# cat /sys/class/hwmon/hwmon2/fan1_input # 현재 RPM
2400
# cat /sys/class/hwmon/hwmon2/pwm1 # PWM duty (0~255)
170
# cat /sys/class/hwmon/hwmon2/pwm1_enable # 제어 모드
1 # 0=off, 1=manual, 2=auto(thermal)
# Cooling device로 확인
# cat /sys/class/thermal/cooling_device2/type
pwm-fan
# cat /sys/class/thermal/cooling_device2/max_state
4
# cat /sys/class/thermal/cooling_device2/cur_state
2
| Cooling State | PWM Duty | 예상 RPM | 소음 수준 |
|---|---|---|---|
| 0 (off) | 0% | 0 | 무소음 |
| 1 (low) | 40% | ~1200 | 저소음 |
| 2 (medium) | 67% | ~2000 | 보통 |
| 3 (high) | 90% | ~3200 | 높음 |
| 4 (full) | 100% | ~3800 | 최대 |
Thermal Pressure & 스케줄러(Scheduler) 연계
Thermal throttling이 발생하면 CPU의 실제 성능이 감소합니다. 커널 스케줄러(특히 EAS)는 arch_set_thermal_pressure()를 통해 이 정보를 반영하여 태스크(Task) 배치를 최적화합니다.
Thermal Pressure API
/* arch/arm64/kernel/topology.c */
void topology_update_thermal_pressure(
const struct cpumask *cpus,
unsigned long capped_freq)
{
unsigned long max_capacity, capacity;
u32 max_freq;
int cpu;
cpu = cpumask_first(cpus);
max_capacity = arch_scale_cpu_capacity(cpu);
max_freq = per_cpu(freq_factor, cpu);
/* 제한된 주파수에 비례하여 capacity 감소량 계산 */
capacity = mult_frac(max_capacity, capped_freq, max_freq);
arch_set_thermal_pressure(cpus, max_capacity - capacity);
}
CPUFreq Cooling에서 Pressure 업데이트
/* drivers/thermal/cpufreq_cooling.c */
static int cpufreq_set_cur_state(
struct thermal_cooling_device *cdev,
unsigned long state)
{
struct cpufreq_cooling_device *cd = cdev->devdata;
unsigned int clip_freq;
clip_freq = cd->freq_table[state].frequency;
/* freq_qos로 최대 주파수 제한 */
freq_qos_update_request(&cd->qos_req, clip_freq);
/* thermal pressure 업데이트 → 스케줄러 반영 */
topology_update_thermal_pressure(cd->policy->related_cpus,
clip_freq);
return 0;
}
Thermal Pressure 확인
# CPU별 thermal pressure 확인
# for cpu in /sys/devices/system/cpu/cpu*/topology; do
echo "$(dirname $cpu): capacity=$(cat $(dirname $cpu)/cpu_capacity)"
done
# trace에서 thermal pressure 변화 확인
# echo 1 > /sys/kernel/debug/tracing/events/thermal/thermal_pressure_update/enable
# cat /sys/kernel/debug/tracing/trace
# thermal pressure trace 출력 예시
kworker/0:1-123 thermal_pressure_update: cpu=4 thermal_pressure=512
kworker/0:1-123 thermal_pressure_update: cpu=5 thermal_pressure=512
# → big 코어(4,5)의 capacity가 1024에서 512로 감소
# → EAS가 heavy 태스크를 LITTLE 코어로 분산
| 스케줄러 | Thermal Pressure 반영 방식 | 효과 |
|---|---|---|
| EAS (Energy Aware) | capacity 감소 → energy model 재계산 | throttle된 코어 회피, 에너지 효율 유지 |
| CFS (Completely Fair) | capacity 감소 → load balancing 가중치 조정 | throttle된 코어의 runqueue 부하 감소 |
| RT (Real-Time) | 직접 반영 없음 (capacity aware 아님) | RT 태스크는 thermal pressure 무시 |
GPU/NPU Thermal Cooling
모바일 SoC에서 GPU와 NPU(Neural Processing Unit)는 집중 연산 시 발열이 매우 크며, CPU와 thermal budget을 공유합니다. IPA governor로 여러 cooling device에 전력을 분배할 때 GPU/NPU의 power model이 핵심입니다.
GPU Power Model
/* GPU devfreq cooling에 power model 등록 */
static int gpu_get_dynamic_power(
struct devfreq *df,
unsigned long freq,
unsigned long voltage,
unsigned long *power)
{
/* P_dynamic = C * V^2 * f */
/* C = effective capacitance (GPU별 상수) */
*power = (gpu_capacitance * voltage * voltage * freq) /
(1000000000ULL);
return 0;
}
static struct devfreq_cooling_power gpu_cooling_power = {
.get_dynamic_power = gpu_get_dynamic_power,
.get_static_power = gpu_get_static_power, /* 누설 전력 */
.dyn_power_coeff = 450, /* mW at max freq/voltage */
};
cdev = devfreq_cooling_em_register(gpu_devfreq, &gpu_cooling_power);
NPU Cooling 등록
/* NPU devfreq cooling - 사용자 정의 power model */
static struct devfreq_cooling_power npu_cooling_power = {
.get_dynamic_power = npu_get_dynamic_power,
.get_static_power = npu_get_static_power,
.dyn_power_coeff = 200,
};
/* NPU devfreq에 cooling 연결 */
npu_cdev = devfreq_cooling_em_register(npu_devfreq, &npu_cooling_power);
if (IS_ERR(npu_cdev))
dev_warn(dev, "NPU cooling registration failed\n");
Multi-zone Device Tree 구성
/* CPU + GPU + NPU가 하나의 thermal zone을 공유하는 DT 예시 */
thermal-zones {
soc-thermal {
polling-delay = <1000>;
polling-delay-passive = <100>;
sustainable-power = <5000>; /* 5W */
thermal-sensors = <&soc_temp_sensor 0>;
trips {
soc_target: soc-target {
temperature = <75000>;
hysteresis = <2000>;
type = "passive";
};
};
cooling-maps {
cpu-map {
trip = <&soc_target>;
cooling-device = <&cpu_cooling 0 10>;
contribution = <1024>; /* 가중치 */
};
gpu-map {
trip = <&soc_target>;
cooling-device = <&gpu_cooling 0 5>;
contribution = <512>;
};
npu-map {
trip = <&soc_target>;
cooling-device = <&npu_cooling 0 3>;
contribution = <256>;
};
};
};
};
| 특성 | CPU | GPU | NPU |
|---|---|---|---|
| 주요 워크로드 | 범용 연산 | 그래픽/컴퓨트 | AI 추론 |
| Cooling 방식 | cpufreq 제한 | devfreq 제한 | devfreq 제한 |
| 전력 소모 (최대) | ~4W (big 코어) | ~3W | ~2W |
| Throttle 영향 | 응답성 저하 | FPS 하락 | 추론 지연(Latency) 증가 |
| IPA 가중치 (일반적) | 높음 (1024) | 중간 (512) | 낮음 (256) |
contribution을 높이고, ML 추론 시에는 NPU contribution을 높여 워크로드에 맞게 thermal budget을 배분하세요.
Userspace Governor 활용
user_space governor를 선택하면 커널은 cooling 제어를 하지 않으며, userspace 데몬이 온도를 모니터링하고 cooling device를 직접 제어합니다. 복잡한 정책이나 ML 기반 예측 제어에 유용합니다.
thermald 설정 예시
<!-- /etc/thermald/thermal-conf.xml -->
<ThermalConfiguration>
<Platform>
<Name>Custom Thermal Policy</Name>
<ProductName>*</ProductName>
<ThermalZones>
<ThermalZone>
<Type>cpu-thermal</Type>
<TripPoints>
<TripPoint>
<SensorType>x86_pkg_temp</SensorType>
<Temperature>75000</Temperature>
<Type>passive</Type>
<CoolingDevice>
<Type>rapl_controller</Type>
<Influence>100</Influence>
</CoolingDevice>
</TripPoint>
<TripPoint>
<SensorType>x86_pkg_temp</SensorType>
<Temperature>85000</Temperature>
<Type>passive</Type>
<CoolingDevice>
<Type>Processor</Type>
<Influence>80</Influence>
</CoolingDevice>
</TripPoint>
</TripPoints>
</ThermalZone>
</ThermalZones>
</Platform>
</ThermalConfiguration>
Python 커스텀 Thermal 데몬
#!/usr/bin/env python3
# custom_thermal_daemon.py - Userspace 열 관리 데몬
import os, time, signal, sys
THERMAL_BASE = "/sys/class/thermal"
TARGET_TEMP = 75000 # 75°C (milli-Celsius)
POLL_INTERVAL = 2 # 초
def read_temp(zone_id):
with open(f"{THERMAL_BASE}/thermal_zone{zone_id}/temp") as f:
return int(f.read().strip())
def set_cooling_state(cdev_id, state):
with open(f"{THERMAL_BASE}/cooling_device{cdev_id}/cur_state", "w") as f:
f.write(str(state))
def get_max_state(cdev_id):
with open(f"{THERMAL_BASE}/cooling_device{cdev_id}/max_state") as f:
return int(f.read().strip())
def thermal_loop():
max_state = get_max_state(0)
cur_state = 0
while True:
temp = read_temp(0)
if temp > TARGET_TEMP + 5000:
cur_state = min(cur_state + 2, max_state)
elif temp > TARGET_TEMP:
cur_state = min(cur_state + 1, max_state)
elif temp < TARGET_TEMP - 3000:
cur_state = max(cur_state - 1, 0)
set_cooling_state(0, cur_state)
print(f"temp={temp/1000:.1f}°C state={cur_state}/{max_state}")
time.sleep(POLL_INTERVAL)
if __name__ == "__main__":
thermal_loop()
uevent 기반 모니터링
#!/bin/bash
# thermal_uevent_monitor.sh - uevent로 thermal 이벤트 감시
# udevadm으로 thermal uevent 실시간 수신
udevadm monitor --kernel --subsystem-match=thermal |
while read line; do
echo "[$(date +%H:%M:%S)] $line"
# 여기서 커스텀 액션 수행 가능
done
systemd 서비스 파일
# /etc/systemd/system/custom-thermald.service
[Unit]
Description=Custom Thermal Management Daemon
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/local/bin/custom_thermal_daemon.py
Restart=always
RestartSec=5
Nice=-10
CPUSchedulingPolicy=fifo
CPUSchedulingPriority=50
[Install]
WantedBy=multi-user.target
| 항목 | Kernel Governor | Userspace Governor |
|---|---|---|
| 반응 속도 | 빠름 (커널 컨텍스트) | 느림 (sysfs I/O 오버헤드(Overhead)) |
| 정책 복잡도 | 제한적 (고정 알고리즘) | 자유 (ML, 예측, 커스텀) |
| 안전성 | 높음 (critical은 커널 처리) | 데몬 크래시 시 위험 |
| 디버깅 | tracepoint 필요 | 일반 로그/printf 가능 |
| 적합 환경 | 임베디드, 모바일 | 서버, 데스크톱, R&D |
Thermal Interrupts
일부 하드웨어는 온도 임계값 도달 시 인터럽트(Interrupt)를 발생시킵니다.
Thermal IRQ 처리
static irqreturn_t thermal_zone_irq_handler(int irq, void *data)
{
struct thermal_zone_device *tz = data;
/* 온도 업데이트 트리거 */
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
return IRQ_HANDLED;
}
/* 인터럽트 등록 */
devm_request_threaded_irq(dev, irq, NULL,
thermal_zone_irq_handler,
IRQF_ONESHOT, "thermal", tz);
Thermal Netlink
커널 5.9+에서 thermal 이벤트를 netlink로 userspace에 전달합니다.
#include <linux/thermal.h>
/* Thermal Netlink 이벤트 */
enum thermal_notify_event {
THERMAL_EVENT_UNSPECIFIED,
THERMAL_EVENT_TEMP_SAMPLE, /* 온도 샘플링 */
THERMAL_TRIP_VIOLATED, /* trip point 초과 */
THERMAL_TRIP_CHANGED, /* trip point 변경 */
THERMAL_DEVICE_DOWN, /* thermal zone 비활성화 */
THERMAL_DEVICE_UP, /* thermal zone 활성화 */
THERMAL_DEVICE_NEW, /* 새 thermal zone 등록 */
THERMAL_DEVICE_DEL, /* thermal zone 제거 */
};
/* Netlink 그룹: THERMAL_GENL_SAMPLING_GROUP, THERMAL_GENL_EVENT_GROUP */
Thermal Netlink
커널 5.9+의 thermal netlink는 Generic Netlink(genl) 패밀리를 사용하여 userspace에 thermal 이벤트를 비동기로 전달합니다. polling 없이 실시간으로 온도 변화와 trip 이벤트를 수신할 수 있어 효율적입니다.
Netlink 이벤트 타입
| 이벤트 | Multicast 그룹 | 설명 |
|---|---|---|
THERMAL_GENL_EVENT_TZ_CREATE |
event | 새 thermal zone 등록 |
THERMAL_GENL_EVENT_TZ_DELETE |
event | thermal zone 삭제 |
THERMAL_GENL_EVENT_TZ_ENABLE |
event | thermal zone 활성화 |
THERMAL_GENL_EVENT_TZ_DISABLE |
event | thermal zone 비활성화 |
THERMAL_GENL_EVENT_TZ_TRIP_UP |
event | trip point 초과 (온도 상승) |
THERMAL_GENL_EVENT_TZ_TRIP_DOWN |
event | trip point 하회 (온도 하강) |
THERMAL_GENL_EVENT_TZ_TRIP_CHANGE |
event | trip point 온도값 변경 |
THERMAL_GENL_EVENT_CDEV_UPDATE |
event | cooling device 상태 변경 |
THERMAL_GENL_SAMPLING_TEMP |
sampling | 주기적 온도 샘플 데이터 |
Genl 이벤트 구조
/* include/uapi/linux/thermal.h - Netlink 속성 */
enum thermal_genl_attr {
THERMAL_GENL_ATTR_UNSPEC,
THERMAL_GENL_ATTR_TZ, /* nested: zone 정보 */
THERMAL_GENL_ATTR_TZ_ID, /* u32: zone ID */
THERMAL_GENL_ATTR_TZ_TEMP, /* s32: milli-Celsius */
THERMAL_GENL_ATTR_TZ_TRIP, /* nested: trip 정보 */
THERMAL_GENL_ATTR_TZ_TRIP_ID, /* u32: trip ID */
THERMAL_GENL_ATTR_TZ_TRIP_TYPE, /* u32: trip 유형 */
THERMAL_GENL_ATTR_TZ_TRIP_TEMP, /* s32: trip 온도 */
THERMAL_GENL_ATTR_TZ_TRIP_HYST, /* s32: 히스테리시스 */
THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT, /* u32: cooling 가중치 */
THERMAL_GENL_ATTR_CDEV, /* nested: cdev 정보 */
THERMAL_GENL_ATTR_CDEV_ID, /* u32: cdev ID */
THERMAL_GENL_ATTR_CDEV_CUR_STATE, /* u32: 현재 state */
THERMAL_GENL_ATTR_CDEV_MAX_STATE, /* u32: 최대 state */
THERMAL_GENL_ATTR_TZ_GOV, /* nested: governor 정보 */
THERMAL_GENL_ATTR_TZ_GOV_NAME, /* string: governor 이름 */
};
C 언어 Netlink Listener
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
static int thermal_event_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
struct genlmsghdr *ghdr = nlmsg_data(nlmsg_hdr(msg));
nla_parse(attrs, THERMAL_GENL_ATTR_MAX,
genlmsg_attrdata(ghdr, 0),
genlmsg_attrlen(ghdr, 0), NULL);
if (attrs[THERMAL_GENL_ATTR_TZ_ID]) {
int tz_id = nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]);
int temp = attrs[THERMAL_GENL_ATTR_TZ_TEMP] ?
nla_get_s32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]) : 0;
printf("[event] zone=%d temp=%d cmd=%d\n", tz_id, temp, ghdr->cmd);
}
return NL_OK;
}
int main(void)
{
struct nl_sock *sk = nl_socket_alloc();
int family, grp_event;
genl_connect(sk);
family = genl_ctrl_resolve(sk, "thermal");
grp_event = genl_ctrl_resolve_grp(sk, "thermal", "event");
nl_socket_add_membership(sk, grp_event);
nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM,
thermal_event_handler, NULL);
while (1)
nl_recvmsgs_default(sk);
return 0;
}
Python Netlink 모니터
# thermal_netlink_monitor.py
import socket, struct
# Generic Netlink으로 thermal 이벤트 수신
NETLINK_GENERIC = 16
def create_genl_socket():
sk = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, NETLINK_GENERIC)
sk.bind((0, 0))
return sk
def monitor_thermal_events():
# 실제 사용 시 pyroute2 라이브러리 권장
from pyroute2 import GenericNetlinkSocket
gnl = GenericNetlinkSocket()
gnl.bind("thermal", gnl.marshal)
print("Thermal Netlink 이벤트 대기 중...")
while True:
msg = gnl.get()
for m in msg:
print(f"Event: {m}")
if __name__ == "__main__":
monitor_thermal_events()
Netlink Batch 쿼리
# thermal netlink 쿼리 (iproute2 기반 도구 필요 시)
# 또는 커널 tools/thermal/ 도구 사용
# tmon - 커널 제공 thermal 모니터
# cd /usr/src/linux/tools/thermal/tmon
# make
# ./tmon
# Thermal zone 정보 일괄 수집 (debugfs)
# cat /sys/kernel/debug/thermal/thermal_zone*/temp
디버깅 & 모니터링
Tracing
# Thermal tracepoint 활성화
# cd /sys/kernel/debug/tracing
# echo 1 > events/thermal/enable
# 주요 tracepoint
thermal_temperature # 온도 샘플링
thermal_zone_trip # trip point 도달
cdev_update # cooling device 상태 변경
thermal_power_allocator # power allocator governor
# trace 출력
# cat trace
kworker/0:1-123 [000] .... 123.456: thermal_temperature: thermal_zone=acpitz temp=55000
실시간 모니터링
#!/bin/bash
# thermal_monitor.sh - 실시간 온도 모니터링
while true; do
clear
echo "===== Thermal Status ====="
for tz in /sys/class/thermal/thermal_zone*; do
type=$(cat $tz/type)
temp=$(cat $tz/temp)
policy=$(cat $tz/policy 2>/dev/null || echo "N/A")
printf "%-20s: %6.2f°C Governor: %s\\n" \
"$type" $(awk "BEGIN {print $temp/1000}") "$policy"
done
echo
echo "===== Cooling Devices ====="
for cdev in /sys/class/thermal/cooling_device*; do
type=$(cat $cdev/type)
cur=$(cat $cdev/cur_state)
max=$(cat $cdev/max_state)
printf "%-20s: %2d / %2d\\n" "$type" "$cur" "$max"
done
sleep 1
done
thermald (Thermal Daemon)
Intel thermald는 userspace thermal management 데몬입니다.
# thermald 설치 (Ubuntu/Debian)
# apt install thermald
# 서비스 상태 확인
# systemctl status thermald
# 설정 파일: /etc/thermald/thermal-conf.xml
# 로그: journalctl -u thermald
실전 디버깅 시나리오
Thermal 문제는 재현이 어렵고 여러 서브시스템에 걸쳐 발생합니다. 체계적인 디버깅 파이프라인(Pipeline)을 구축하면 빠르게 원인을 찾을 수 있습니다.
Tracepoint 기반 분석
# Thermal 전용 tracepoint 활성화
# cd /sys/kernel/debug/tracing
# echo 0 > tracing_on
# echo > trace
# 핵심 thermal 이벤트만 활성화
# echo 1 > events/thermal/thermal_temperature/enable
# echo 1 > events/thermal/thermal_zone_trip/enable
# echo 1 > events/thermal/cdev_update/enable
# echo 1 > events/thermal/thermal_power_allocator/enable
# 타임스탬프 포함 수집 시작
# echo 1 > tracing_on
# 부하 실행 후 수집
# stress-ng --cpu 4 --timeout 30s
# echo 0 > tracing_on
# cat trace > /tmp/thermal_trace.txt
bpftrace 스크립트
/* thermal_watch.bt - 온도 변화 실시간 추적 */
tracepoint:thermal:thermal_temperature
{
printf("%-16s zone=%-12s temp=%d (trip=%d)\n",
comm, str(args->thermal_zone),
args->temp, args->temp_prev);
}
tracepoint:thermal:cdev_update
{
printf("%-16s cdev=%-12s target=%d\n",
comm, str(args->type), args->target);
}
tracepoint:thermal:thermal_zone_trip
{
printf("*** TRIP *** zone=%s trip=%d type=%s temp=%d\n",
str(args->thermal_zone), args->trip,
str(args->trip_type), args->temp);
}
Thermal 상태 덤프(Dump) 스크립트
#!/bin/bash
# thermal_dump.sh - 현재 thermal 서브시스템 전체 상태 덤프
echo "=== Thermal Zones ==="
for tz in /sys/class/thermal/thermal_zone*; do
name=$(basename $tz)
type=$(cat $tz/type)
temp=$(cat $tz/temp)
policy=$(cat $tz/policy 2>/dev/null || echo "N/A")
mode=$(cat $tz/mode 2>/dev/null || echo "N/A")
printf " %-20s type=%-16s temp=%6d policy=%-16s mode=%s\n" \
"$name" "$type" "$temp" "$policy" "$mode"
# Trip point 나열
for tp in $tz/trip_point_*_temp; do
[ -f "$tp" ] || continue
idx=$(basename $tp | sed 's/trip_point_\(.*\)_temp/\1/')
trip_type=$(cat $tz/trip_point_${idx}_type 2>/dev/null)
trip_temp=$(cat $tp)
printf " trip_%s: type=%-10s temp=%d\n" "$idx" "$trip_type" "$trip_temp"
done
done
echo
echo "=== Cooling Devices ==="
for cd in /sys/class/thermal/cooling_device*; do
name=$(basename $cd)
type=$(cat $cd/type)
cur=$(cat $cd/cur_state)
max=$(cat $cd/max_state)
printf " %-20s type=%-16s state=%d/%d\n" \
"$name" "$type" "$cur" "$max"
done
echo
echo "=== dmesg thermal 메시지 ==="
dmesg | grep -iE "thermal|trip|cooling|throttl" | tail -20
perf를 이용한 thermal 이벤트 분석
# perf로 thermal tracepoint 수집
# perf record -e thermal:thermal_temperature \
-e thermal:cdev_update \
-e thermal:thermal_zone_trip \
-a -- sleep 60
# 결과 분석
# perf script | head -50
# perf stat -e thermal:thermal_temperature -a -- sleep 10
| 증상 | 확인 항목 | 해결 방법 |
|---|---|---|
| 갑작스런 shutdown | dmesg | grep critical |
critical trip point 확인, 팬/히트싱크 점검 |
| CPU throttling 지속 | cpufreq cur_freq + thermal trip_point |
trip point 온도 조정, IPA sustainable_power 증가 |
| 온도 0 표시 | cat thermal_zone*/temp |
센서 드라이버 로딩 확인, get_temp() 반환값 검증 |
| Cooling 미동작 | cat cdev*/cur_state |
바인딩 확인 (cdev*_trip_point), governor 정책 점검 |
| 온도 진동 (oscillation) | tracepoint thermal_temperature |
hysteresis 값 증가, polling_delay 조정 |
SoC별 Thermal 드라이버 비교
각 SoC 벤더는 자체 thermal sensor IP와 드라이버를 제공합니다. 레지스터(Register) 접근 모델, 인터럽트 구조, 캘리브레이션 방식이 크게 다릅니다.
Qualcomm TSENS 드라이버
/* drivers/thermal/qcom/tsens.c - Qualcomm Temperature Sensor */
static int tsens_get_temp(struct tsens_sensor *s, int *temp)
{
struct tsens_priv *priv = s->priv;
int last_temp, ret;
/* MMIO 레지스터에서 ADC 값 읽기 */
ret = regmap_field_read(priv->rf[LAST_TEMP_0 + s->hw_id], &last_temp);
if (ret)
return ret;
/* ADC → milli-Celsius 변환 (slope, offset 적용) */
*temp = tsens_hw_to_mC(s, last_temp);
return 0;
}
/* eFuse 캘리브레이션 데이터 읽기 */
static int tsens_read_calibration(struct tsens_priv *priv)
{
u32 *qfprom_cdata;
int mode;
qfprom_cdata = (u32 *)qfprom_read(priv->dev, "calib");
mode = (qfprom_cdata[0] >> TSENS_CAL_SEL_SHIFT) & TSENS_CAL_SEL_MASK;
/* mode: 0=no cal, 1=one-point, 2=two-point */
return tsens_calibrate_sensors(priv, mode, qfprom_cdata);
}
Samsung Exynos TMU
/* drivers/thermal/samsung/exynos_tmu.c */
static int exynos_tmu_read(struct exynos_tmu_data *data)
{
int temp_code, temp;
/* TRIMINFO 레지스터로 센서 캘리브레이션 */
temp_code = readl(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
/* 1-point 또는 2-point 캘리브레이션 적용 */
switch (data->cal_type) {
case TYPE_ONE_POINT_TRIMMING:
temp = temp_code - data->trim25 + 25;
break;
case TYPE_TWO_POINT_TRIMMING:
temp = (temp_code - data->trim25) *
(85 - 25) / (data->trim85 - data->trim25) + 25;
break;
}
return temp;
}
Intel INT340X (DPTF)
/* drivers/thermal/intel/int340x_thermal/ */
/* ACPI INT3403 센서 읽기 */
static int int3403_get_temp(struct thermal_zone_device *tz, int *temp)
{
struct int34x_thermal_zone *d = thermal_zone_device_priv(tz);
unsigned long long tmp;
acpi_status status;
/* _TMP ACPI 메서드 호출 */
status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status))
return -EIO;
/* 1/10 Kelvin → milli-Celsius 변환 */
*temp = deci_kelvin_to_millicelsius(tmp);
return 0;
}
Rockchip TSADC
/* drivers/thermal/rockchip_thermal.c */
/* ADC 기반 온도 읽기 + 2-point 캘리브레이션 */
static int rk_tsadcv2_get_temp(const struct chip_tsadc_table *table,
int chn, void __iomem *regs,
int *out_temp)
{
u32 val;
val = readl_relaxed(regs + TSADCV2_DATA(chn));
return rk_tsadcv2_code_to_temp(table, val, out_temp);
}
| 항목 | Qualcomm TSENS | Samsung TMU | MediaTek | Intel DPTF | Rockchip |
|---|---|---|---|---|---|
| 센서 방식 | BJT ADC | BJT ADC | auxadc | PECI/MSR | TSADC |
| 캘리브레이션 | eFuse 1/2pt | TRIMINFO 1/2pt | OTP | 펌웨어 | eFuse 2pt |
| 인터럽트 | upper/lower IRQ | level IRQ | polling | SCI/GPE | level IRQ |
| 레지스터 접근 | MMIO regmap | MMIO direct | MMIO auxadc | ACPI AML | MMIO direct |
| 센서 수 (최대) | 16개 | 5개 | 가변 | ACPI 정의 | 2~3개 |
| 소스 경로 | qcom/tsens.c |
samsung/exynos_tmu.c |
mediatek/ |
intel/int340x_thermal/ |
rockchip_thermal.c |
| 캘리브레이션 방식 | 정확도 | 설명 |
|---|---|---|
| No calibration | ±10°C | 공장 캘리브레이션 없음, ADC raw 값 직접 사용 |
| 1-point | ±5°C | 25°C 기준점 하나로 오프셋(Offset) 보정 |
| 2-point | ±2°C | 25°C + 85°C 두 기준점으로 slope + offset 보정 |
| Firmware-based | ±1°C | Intel PECI/펌웨어가 내부적으로 보정 완료 |
Thermal 에뮬레이션 & 테스트
CONFIG_THERMAL_EMULATION을 활성화하면 실제 하드웨어 없이도 thermal zone에 가상 온도를 주입하여 governor 동작, trip point 반응, cooling device 제어를 테스트할 수 있습니다.
기본 에뮬레이션 테스트
#!/bin/bash
# thermal_emul_test.sh - Thermal Emulation 기본 테스트
TZ="/sys/class/thermal/thermal_zone0"
# 에뮬레이션 가능 여부 확인
if [ ! -f "$TZ/emul_temp" ]; then
echo "ERROR: CONFIG_THERMAL_EMULATION not enabled"
exit 1
fi
# 현재 상태 저장
REAL_TEMP=$(cat $TZ/temp)
echo "현재 실제 온도: ${REAL_TEMP}mC"
# 에뮬레이션: 85°C (passive trip 테스트)
echo 85000 > $TZ/emul_temp
sleep 1
NEW_TEMP=$(cat $TZ/temp)
echo "에뮬레이션 온도: ${NEW_TEMP}mC"
# Cooling device 반응 확인
for cd in /sys/class/thermal/cooling_device*; do
echo " $(cat $cd/type): state=$(cat $cd/cur_state)/$(cat $cd/max_state)"
done
# 에뮬레이션 해제 (0 쓰기)
echo 0 > $TZ/emul_temp
echo "에뮬레이션 해제. 실제 온도 복원."
Trip Point 자동 검증
#!/bin/bash
# trip_point_test.sh - 모든 trip point에 대해 cooling 반응 검증
TZ="/sys/class/thermal/thermal_zone0"
PASS=0; FAIL=0
for tp in $TZ/trip_point_*_temp; do
[ -f "$tp" ] || continue
idx=$(basename $tp | sed 's/trip_point_\(.*\)_temp/\1/')
trip_temp=$(cat $tp)
trip_type=$(cat $TZ/trip_point_${idx}_type)
# trip 온도 + 1°C로 에뮬레이션
emul_temp=$(( trip_temp + 1000 ))
echo $emul_temp > $TZ/emul_temp
sleep 2
# Cooling device가 활성화되었는지 확인
any_active=0
for cd in /sys/class/thermal/cooling_device*; do
state=$(cat $cd/cur_state)
[ "$state" -gt 0 ] && any_active=1
done
if [ $any_active -eq 1 ]; then
echo "[PASS] trip_${idx} (${trip_type}): ${trip_temp}mC → cooling 활성화"
PASS=$(( PASS + 1 ))
else
echo "[FAIL] trip_${idx} (${trip_type}): ${trip_temp}mC → cooling 미동작"
FAIL=$(( FAIL + 1 ))
fi
done
echo 0 > $TZ/emul_temp
echo "결과: PASS=$PASS, FAIL=$FAIL"
Python 테스트 프레임워크
# thermal_test.py - Thermal emulation 자동 테스트
import os, time, unittest
class ThermalEmulationTest(unittest.TestCase):
TZ_PATH = "/sys/class/thermal/thermal_zone0"
def setUp(self):
# 에뮬레이션 가능 확인
self.assertTrue(
os.path.exists(f"{self.TZ_PATH}/emul_temp"),
"CONFIG_THERMAL_EMULATION 비활성")
def tearDown(self):
# 에뮬레이션 해제
self._write_emul(0)
def _write_emul(self, temp_mc):
with open(f"{self.TZ_PATH}/emul_temp", "w") as f:
f.write(str(temp_mc))
def _read_temp(self):
with open(f"{self.TZ_PATH}/temp") as f:
return int(f.read().strip())
def test_emulation_sets_temp(self):
self._write_emul(75000)
time.sleep(0.5)
self.assertEqual(self._read_temp(), 75000)
def test_cooling_activates_above_trip(self):
# trip_point_0_temp 초과 시 cooling 활성화
with open(f"{self.TZ_PATH}/trip_point_0_temp") as f:
trip = int(f.read().strip())
self._write_emul(trip + 5000)
time.sleep(2)
# 최소 하나의 cooling device가 활성화
active = False
for cd in os.listdir("/sys/class/thermal"):
if cd.startswith("cooling_device"):
with open(f"/sys/class/thermal/{cd}/cur_state") as f:
if int(f.read().strip()) > 0:
active = True
self.assertTrue(active, "cooling device가 활성화되지 않음")
if __name__ == "__main__":
unittest.main()
KUnit Thermal 테스트
/* tools/testing/selftests/thermal/ 기반 테스트 예시 */
#include <kunit/test.h>
#include <linux/thermal.h>
static void thermal_emul_test(struct kunit *test)
{
struct thermal_zone_device *tz;
int temp;
tz = thermal_zone_get_zone_by_name("cpu-thermal");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, tz);
/* 에뮬레이션 온도 설정 */
tz->emul_temperature = 85000;
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
thermal_zone_get_temp(tz, &temp);
KUNIT_EXPECT_EQ(test, temp, 85000);
}
| 테스트 시나리오 | 에뮬레이션 온도 | 기대 결과 |
|---|---|---|
| 정상 동작 | 40000 (40°C) | 모든 cooling device state = 0 |
| Passive trip 도달 | trip_passive + 1000 | CPU freq 제한 시작 |
| Active trip 도달 | trip_active + 1000 | Fan cooling 활성화 |
| Critical trip 도달 | trip_critical + 1000 | 시스템 shutdown 호출 |
| 히스테리시스 검증 | trip - hysteresis - 1000 | cooling 해제 확인 |
| 에뮬레이션 해제 | 0 | 실제 센서 온도 복원 |
성능 고려사항
Polling Delay 조정
온도 폴링 주기는 응답 속도와 CPU 오버헤드 사이의 트레이드오프입니다.
- passive_delay — passive cooling 활성화 시 폴링 주기 (msec, 짧게)
- polling_delay — 일반 폴링 주기 (msec, 길게)
/* 폴링 주기 예시 */
passive_delay = 10000; /* 10초 (passive cooling 시) */
polling_delay = 0; /* 0 = 인터럽트 기반 */
Hysteresis (히스테리시스)
온도가 임계값 근처에서 진동할 때 cooling device가 계속 on/off되는 것을 방지합니다.
struct thermal_trip {
int temperature; /* trip 온도 */
int hysteresis; /* 히스테리시스 (milli-Celsius) */
enum thermal_trip_type type;
};
/* 예: trip_temp=80°C, hysteresis=5°C */
/* → 85°C 초과 시 cooling 시작 */
/* → 75°C 이하로 떨어지면 cooling 중단 */
코드 설명
- temperaturetrip point의 임계 온도(milli-Celsius)입니다. 이 온도를 초과하면 해당 trip에 바인딩된 cooling device가 활성화됩니다.
include/linux/thermal.h에 정의됩니다. - hysteresis온도 진동 방지를 위한 히스테리시스 값입니다. cooling 시작 온도(
temperature)와 중단 온도(temperature - hysteresis)에 차이를 두어, 임계값 근처에서 cooling device가 반복적으로 켜지고 꺼지는 것을 방지합니다. - type
THERMAL_TRIP_ACTIVE,THERMAL_TRIP_PASSIVE,THERMAL_TRIP_HOT,THERMAL_TRIP_CRITICAL중 하나입니다.CRITICAL은orderly_poweroff()를 트리거하며,PASSIVE는 주파수 제한 등 소프트웨어 기반 cooling을 활성화합니다. - 예시 (80°C, 5°C)trip_temp가 80000(80°C)이고 hysteresis가 5000(5°C)이면, 온도가 80°C를 초과할 때 cooling이 시작되고 75°C 아래로 내려가야 cooling이 중단됩니다. 이 5°C 간격이 불필요한 on/off 반복을 방지합니다.
Polling vs Interrupt 성능 비교
| 항목 | Polling 방식 | Interrupt 방식 |
|---|---|---|
| CPU 오버헤드 | polling_delay마다 wakeup | 이벤트 시에만 처리 |
| 반응 지연 | 최대 polling_delay만큼 | 즉시 (IRQ latency) |
| 전력 소비 | 주기적 wakeup으로 idle 깨짐 | 유휴 시 전력 소비 없음 |
| 구현 복잡도 | 낮음 (get_temp만 구현) | 높음 (set_trips + IRQ 핸들러(Handler)) |
| 설정 | polling_delay > 0 |
polling_delay = 0 |
| 권장 환경 | 데스크톱/서버 | 모바일/임베디드 (배터리) |
polling_delay=0 (interrupt 기반) + set_trips() 콜백을 구현하여 CPU wakeup을 최소화하세요. 하드웨어가 interrupt를 지원하지 않으면 polling_delay_passive=100 (100ms), polling_delay=2000 (2초)가 적절한 시작점입니다.
커널 설정
CONFIG_THERMAL=y # Thermal 서브시스템
CONFIG_THERMAL_HWMON=y # hwmon 통합
CONFIG_THERMAL_GOV_STEP_WISE=y # Step-Wise Governor
CONFIG_THERMAL_GOV_FAIR_SHARE=y # Fair-Share Governor
CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y # Power Allocator Governor
CONFIG_THERMAL_GOV_BANG_BANG=y # Bang-Bang Governor
CONFIG_THERMAL_GOV_USER_SPACE=y # Userspace Governor
CONFIG_CPU_THERMAL=y # CPUFreq Cooling
CONFIG_DEVFREQ_THERMAL=y # Devfreq Cooling
CONFIG_THERMAL_EMULATION=y # 온도 에뮬레이션 (테스트용)
CONFIG_THERMAL_STATISTICS=y # Cooling 통계
# 플랫폼 특화
CONFIG_ACPI_THERMAL=y # ACPI Thermal Zone
CONFIG_INTEL_POWERCLAMP=y # Intel Idle Injection
CONFIG_X86_PKG_TEMP_THERMAL=y # Intel Package Temperature
CONFIG_INT340X_THERMAL=y # Intel INT3400/INT3403 DPTF
임베디드 최소 Thermal 설정
# 임베디드 ARM SoC 최소 thermal 설정
CONFIG_THERMAL=y
CONFIG_THERMAL_OF=y # Device Tree thermal 지원
CONFIG_THERMAL_GOV_STEP_WISE=y # 기본 governor
CONFIG_CPU_THERMAL=y # CPUFreq Cooling
CONFIG_THERMAL_EMULATION=y # 테스트용 (개발 빌드만)
# IPA 사용 시 추가
CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y
CONFIG_DEVFREQ_THERMAL=y # GPU devfreq cooling
CONFIG_ENERGY_MODEL=y # Energy Model (IPA 필수)
# 불필요한 항목 제거 (Flash 절약)
# CONFIG_THERMAL_HWMON is not set # hwmon 불필요 시
# CONFIG_THERMAL_STATISTICS is not set # 통계 불필요 시
# CONFIG_ACPI_THERMAL is not set # ARM이므로 ACPI 없음
Common Issues
- Critical 온도 미설정 —
_CRT메서드 누락 시 시스템이 과열로 손상될 수 있음 - Passive Cooling 무한 루프 — 너무 짧은
passive_delay로 CPU 부하 증가 - Cooling Device 미바인딩 — thermal zone과 cooling device가 연결되지 않아 cooling 동작 안 함
- ACPI Thermal Zone 초기화 실패 — BIOS 버그로
_TMP메서드 실패
Troubleshooting
# Thermal zone 정보 확인
# cat /sys/class/thermal/thermal_zone0/type
# cat /sys/class/thermal/thermal_zone0/temp
# cat /sys/class/thermal/thermal_zone0/mode
# Cooling device 바인딩 확인
# ls -l /sys/class/thermal/thermal_zone0/cdev*
# ACPI thermal zone 디버깅
# dmesg | grep -i thermal
# dmesg | grep -i acpi
# Thermal emulation (테스트용)
# echo 85000 > /sys/class/thermal/thermal_zone0/emul_temp
참고 자료
커널 공식 문서
- Linux Thermal Framework — thermal 서브시스템 전체 문서
- Thermal sysfs API — thermal zone/cooling device sysfs 인터페이스
- Power Allocator Governor (IPA) — Intelligent Power Allocation 알고리즘
- CPU Cooling API — CPUFreq cooling device 등록
- Exynos Thermal — Samsung Exynos TMU 드라이버 예시
- Device Tree: thermal-zones Binding — DT thermal zone 바인딩 스키마
- ACPI Thermal Zone — ACPI _TMP, _PSV, _CRT 메서드
LWN.net 기사
- Thermal management in the kernel — thermal 프레임워크 초기 설계
- Intelligent Power Allocation (IPA) — PID 제어 기반 전력 배분
- Thermal pressure in the scheduler — thermal throttling의 스케줄러 반영
- Thermal notifications via netlink — genetlink 기반 thermal 이벤트
- Device tree thermal bindings — DT 기반 thermal zone 정의
도구 및 유틸리티
- Intel Thermal Daemon (thermald) — Intel 플랫폼 thermal 관리 데몬
- Linux Thermal Monitor — thermal zone 실시간 모니터링
커널 소스
drivers/thermal/thermal_core.c— Thermal Core 프레임워크 구현drivers/thermal/thermal_trip.c— trip point 관리drivers/thermal/gov_step_wise.c— Step-wise governordrivers/thermal/gov_power_allocator.c— IPA (Power Allocator) governordrivers/thermal/gov_bang_bang.c— Bang-bang governor (팬 제어용)drivers/thermal/cpu_cooling.c— CPUFreq cooling devicedrivers/thermal/devfreq_cooling.c— GPU/devfreq cooling devicedrivers/acpi/thermal.c— ACPI Thermal Zone 드라이버drivers/thermal/thermal_netlink.c— Thermal genetlink 인터페이스drivers/thermal/thermal_of.c— Device Tree thermal 파싱
- RAPL & Powercap — Intel 전력 제한과 thermal 연계
- CPUFreq — CPU 주파수 스케일링(Frequency Scaling)과 thermal cooling 통합
- hwmon (Hardware Monitoring) — 온도 센서 드라이버
- 전원 관리 — DVFS와 thermal 통합
- ACPI — ACPI Thermal Zone
- CPU 토폴로지 — 코어별 온도 관리