전원 관리 프레임워크 (Power Management)
Linux 커널 PM 서브시스템 아키텍처, 시스템 절전(s2idle/S3/hibernate), cpuidle, 런타임 PM, dev_pm_ops, PM 도메인, PM QoS, Energy Model, powercap/RAPL, Regulator 종합 가이드.
핵심 요약
- Suspend(S3) — RAM에 상태를 유지한 채 CPU/디바이스 전원을 끄는 절전 모드입니다.
- Hibernate(S4) — RAM 내용을 디스크에 저장하고 완전 전원 차단. 복원 시 디스크에서 복구합니다.
- 런타임 PM — 개별 디바이스를 사용하지 않을 때 자동으로 저전력 상태로 전환합니다.
- cpuidle — idle CPU를 C-state(C0→C1→C3...)로 전환하여 전력을 절약합니다.
- dev_pm_ops — 드라이버가 구현하는 PM 콜백 구조체. suspend/resume 등의 동작을 정의합니다.
단계별 이해
- 시스템 절전 체험 —
systemctl suspend로 시스템을 S3 절전에 진입시킵니다.전원 버튼이나 키보드로 깨우면 모든 상태가 복원됩니다.
- 절전 상태 확인 —
cat /sys/power/state로 지원되는 절전 상태를 확인합니다.freeze(s2idle),mem(S3),disk(hibernate) 등이 표시됩니다. - 런타임 PM 확인 —
/sys/bus/pci/devices/*/power/runtime_status로 각 PCI 디바이스의 PM 상태를 볼 수 있습니다.active,suspended,unsupported중 하나입니다. - 드라이버 관점 — 드라이버는
dev_pm_ops의.suspend/.resume콜백을 구현하여 절전/복원 동작을 정의합니다.하드웨어 레지스터 저장/복원, DMA 중단, 인터럽트 비활성화 등을 수행합니다.
PM 서브시스템 아키텍처 개요
Linux 커널의 전원 관리(PM) 코드는 여러 디렉토리에 분산되어 있으며, 시스템 레벨과 디바이스 레벨의 두 축으로 구성됩니다.
소스 트리 구조
| 경로 | 역할 |
|---|---|
kernel/power/ | 시스템 절전(suspend/hibernate), PM 코어, wakeup |
drivers/base/power/ | 디바이스 PM 인프라: dev_pm_ops, 런타임 PM, 클럭 PM |
drivers/cpuidle/ | cpuidle 프레임워크, 거버너, 드라이버 |
drivers/opp/ | Operating Performance Points(OPP) 프레임워크 |
drivers/powercap/ | powercap 프레임워크, Intel/AMD RAPL |
drivers/regulator/ | Regulator(전압/전류) 프레임워크 |
drivers/cpufreq/ | CPU 주파수 스케일링 (→ ktime/Clock) |
drivers/thermal/ | 열 관리 (→ ACPI) |
핵심 구조체
/* include/linux/pm.h */
struct dev_pm_ops {
int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze_late)(struct device *dev);
int (*thaw_early)(struct device *dev);
int (*poweroff_late)(struct device *dev);
int (*restore_early)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
};
/* include/linux/suspend.h */
struct platform_suspend_ops {
int (*valid)(suspend_state_t state);
int (*begin)(suspend_state_t state);
int (*prepare)(void);
int (*enter)(suspend_state_t state);
void (*wake)(void);
void (*finish)(void);
void (*end)(void);
};
PM 서브시스템 계층 다이어그램
시스템 절전 상태 (System Sleep)
Linux 커널은 세 가지 시스템 절전 상태를 지원하며, /sys/power/state를 통해 진입합니다.
절전 상태 매핑
/sys/power/state | 내부 상태 | ACPI 매핑 | 설명 |
|---|---|---|---|
freeze | PM_SUSPEND_TO_IDLE (s2idle) | S0 (S0ix) | 프로세스 동결 + 디바이스 저전력 + CPU idle. 가장 빠른 진입/복귀 |
mem | PM_SUSPEND_MEM (S2RAM) | S3 | RAM 자체 리프레시 유지, 나머지 전원 차단. mem_sleep_default로 s2idle 대체 가능 |
disk | PM_SUSPEND_DISK (hibernate) | S4 | 메모리 이미지를 swap에 기록 후 전원 차단. resume= 파라미터로 복원 |
/sys/power/mem_sleep에서 현재 설정을 확인할 수 있습니다.Suspend 진입 흐름
Resume 경로는 역순으로 진행됩니다: resume_noirq → resume_early → resume → complete. 각 단계에서 에러 발생 시 해당 지점부터 rollback이 이루어집니다.
Hibernate 흐름
/* Hibernate 진입 */
echo disk > /sys/power/state
→ hibernate()
→ freeze_processes()
→ dpm_suspend_start(PMSG_FREEZE) /* 디바이스 freeze */
→ create_image() /* 메모리 스냅샷 생성 */
→ dpm_resume_end(PMSG_THAW) /* 디바이스 복원 (이미지 쓰기용) */
→ swsusp_write() /* swap 파티션에 이미지 기록 */
→ dpm_suspend_start(PMSG_POWEROFF) /* 디바이스 poweroff */
→ hibernation_platform_enter() /* ACPI S4 진입 */
/* Hibernate 복원 (부팅 시) */
커널 부팅 → resume=/dev/sdaX 파라미터 감지
→ swsusp_read() /* swap에서 이미지 로드 */
→ restore_image() /* 메모리 복원 + 점프 */
주요 /sys/power/ 인터페이스
| 파일 | 설명 |
|---|---|
/sys/power/state | 절전 상태 진입 (freeze, mem, disk) |
/sys/power/mem_sleep | mem 상태의 실제 모드 (s2idle, [deep]) |
/sys/power/disk | hibernate 모드 (platform, shutdown, reboot, suspend, test_resume) |
/sys/power/image_size | hibernate 이미지 목표 크기 (기본 RAM의 2/5) |
/sys/power/wakeup_count | wakeup 이벤트 카운터 (spurious wakeup 방지) |
/sys/power/pm_test | suspend 테스트 모드 (none, core, processors, platform, devices, freezer) |
/sys/power/suspend_stats | suspend 성공/실패 통계 |
cpuidle 프레임워크
CPU가 실행할 작업이 없을 때, 커널은 cpuidle 프레임워크를 통해 적절한 저전력 상태(C-state)로 진입합니다. 깊은 C-state일수록 전력 절감이 크지만 복귀 지연(exit latency)도 증가합니다.
핵심 구조체
/* include/linux/cpuidle.h */
struct cpuidle_state {
char name[16]; /* C-state 이름 (예: "C1", "C6S") */
char desc[32]; /* 설명 */
s64 exit_latency_ns; /* 복귀 지연 (나노초) */
s64 target_residency_ns; /* 최소 체류 시간 */
unsigned int flags; /* CPUIDLE_FLAG_* */
int (*enter)(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index);
};
struct cpuidle_driver {
const char *name;
struct cpuidle_state states[CPUIDLE_STATE_MAX];
int state_count;
int safe_state_index; /* BM-safe fallback state */
};
거버너 비교
| 거버너 | 알고리즘 | 적합 환경 |
|---|---|---|
| menu | 예상 idle 시간 + 보정 계수 + PM QoS 제약 | 범용 (기본값) |
| TEO (Timer Events Oriented) | 최근 타이머 이벤트 패턴 분석, 깊은 상태 적극 선택 | 서버, 주기적 워크로드 |
| ladder | C-state를 단계적으로 승격/강등 | 틱 기반(주로 레거시) |
| haltpoll | 짧은 busy-poll 후 halt, VM 게스트 최적화 | KVM 게스트 |
cpuidle 드라이버
| 드라이버 | 플랫폼 | C-state 소스 |
|---|---|---|
intel_idle | Intel 프로세서 | 하드코딩된 MWAIT 힌트 테이블 (CPUID 기반) |
acpi_idle | ACPI 지원 시스템 | ACPI _CST 메서드 (FADT C-state 정보) |
psci_idle (ARM) | ARM64 플랫폼 | PSCI cpu_suspend + DT idle-states |
cpuidle 아키텍처 다이어그램
sysfs 인터페이스
# CPU별 cpuidle 정보
/sys/devices/system/cpu/cpu0/cpuidle/state0/
├── name # C-state 이름
├── desc # 설명
├── latency # 복귀 지연 (μs)
├── residency # 최소 체류 시간 (μs)
├── usage # 진입 횟수
├── time # 총 체류 시간 (μs)
├── disable # 0/1 (비활성화)
└── above/below # 이 상태보다 깊은/얕은 선택 횟수
# 현재 거버너
/sys/devices/system/cpu/cpuidle/current_governor
런타임 PM
런타임 PM은 시스템이 동작 중일 때 개별 디바이스의 전원을 동적으로 관리합니다. 사용하지 않는 디바이스를 자동으로 suspend하여 전력을 절감합니다.
상태 전환 다이어그램
pm_runtime_* API
| 함수 | 동작 |
|---|---|
pm_runtime_enable(dev) | 런타임 PM 활성화 (초기 usage_count=1이므로 필수) |
pm_runtime_get_sync(dev) | usage_count++ 후 동기 resume (에러 시 put 필요) |
pm_runtime_put_sync(dev) | usage_count-- 후 즉시 idle 검사 → suspend |
pm_runtime_put_autosuspend(dev) | usage_count-- 후 autosuspend 타이머 시작 |
pm_runtime_set_autosuspend_delay(dev, ms) | autosuspend 지연 시간 설정 |
pm_runtime_use_autosuspend(dev) | autosuspend 모드 활성화 |
pm_runtime_mark_last_busy(dev) | 마지막 사용 시각 갱신 (autosuspend 타이머 리셋) |
pm_runtime_get_noresume(dev) | usage_count++만 (resume 안 함) |
pm_runtime_put_noidle(dev) | usage_count--만 (idle 검사 안 함) |
pm_runtime_forbid(dev) | 런타임 suspend 금지 |
pm_runtime_allow(dev) | 런타임 suspend 허용 |
Autosuspend 패턴
/* 디바이스 사용 시 (예: I/O 요청) */
pm_runtime_get_sync(dev); /* resume + refcount++ */
/* ... 실제 작업 ... */
pm_runtime_mark_last_busy(dev); /* 타이머 리셋 */
pm_runtime_put_autosuspend(dev); /* refcount--, delay 후 suspend */
드라이버 구현 예제
static int my_runtime_suspend(struct device *dev)
{
struct my_device *priv = dev_get_drvdata(dev);
clk_disable_unprepare(priv->clk);
return 0;
}
static int my_runtime_resume(struct device *dev)
{
struct my_device *priv = dev_get_drvdata(dev);
return clk_prepare_enable(priv->clk);
}
static int my_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
/* ... 초기화 ... */
pm_runtime_set_autosuspend_delay(dev, 200); /* 200ms */
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
}
static void my_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
}
static const struct dev_pm_ops my_pm_ops = {
RUNTIME_PM_OPS(my_runtime_suspend, my_runtime_resume, NULL)
};
런타임 PM sysfs
/sys/devices/.../power/
├── runtime_status # active / suspended / suspending / resuming
├── runtime_usage # usage_count
├── runtime_active_time
├── runtime_suspended_time
├── autosuspend_delay_ms
└── control # auto / on (on = 런타임 PM 강제 활성)
디바이스 PM (dev_pm_ops)
struct dev_pm_ops는 디바이스가 시스템 절전과 런타임 PM 이벤트에 응답하기 위한 22개 콜백을 정의합니다. 실제 드라이버에서는 매크로를 사용하여 간결하게 작성합니다.
콜백 전체 매핑
| 단계 | Suspend | Hibernate (freeze) | Hibernate (restore) |
|---|---|---|---|
| prepare | prepare | prepare | prepare |
| 메인 | suspend | freeze | restore |
| late | suspend_late | freeze_late | restore_early |
| noirq | suspend_noirq | freeze_noirq | restore_noirq |
| ← 하드웨어 절전/복원 → | |||
| noirq | resume_noirq | thaw_noirq | — |
| early | resume_early | thaw_early | — |
| 메인 | resume | thaw | — |
| complete | complete | complete | complete |
편의 매크로
/* 시스템 PM 전용 (suspend = resume 콜백 재사용) */
DEFINE_SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn)
/* 런타임 PM + 시스템 PM (시스템 PM이 런타임 PM 콜백 재사용) */
DEFINE_RUNTIME_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn)
/* pm_sleep_ptr() — CONFIG_PM_SLEEP 미설정 시 NULL로 최적화 */
static const struct dev_pm_ops my_pm_ops = {
SYSTEM_SLEEP_PM_OPS(my_suspend, my_resume)
RUNTIME_PM_OPS(my_rt_suspend, my_rt_resume, my_rt_idle)
};
static struct platform_driver my_drv = {
.driver = {
.name = "my-device",
.pm = pm_sleep_ptr(&my_pm_ops),
},
};
DPM 리스트 순서
디바이스 PM(DPM)은 디바이스 등록 순서를 기반으로 리스트를 관리합니다. Suspend 시에는 역순(자식 → 부모), Resume 시에는 정순(부모 → 자식)으로 콜백을 호출하여 의존성을 보장합니다.
noirq 콜백은 모든 IRQ가 비활성화된 상태에서 실행됩니다. 이 단계에서는 인터럽트를 사용하는 I/O(예: DMA 완료 대기)를 수행할 수 없습니다. PCI 디바이스의 경우 pci_save_state()/pci_restore_state()를 이 단계에서 호출합니다.PM 도메인 (genpd)
Generic PM Domain(genpd)은 하나의 전력 도메인(power domain)에 속한 디바이스 그룹을 관리합니다. SoC에서 특정 하드웨어 블록의 전원을 독립적으로 켜고 끌 수 있으며, 도메인 간 계층 관계를 지원합니다.
핵심 구조체
/* include/linux/pm_domain.h */
struct generic_pm_domain {
struct device dev;
const char *name;
unsigned int flags; /* GENPD_FLAG_* */
int (*power_on)(struct generic_pm_domain *domain);
int (*power_off)(struct generic_pm_domain *domain);
struct list_head parent_links; /* 부모 도메인 링크 */
struct list_head child_links; /* 자식 도메인 링크 */
struct list_head dev_list; /* 소속 디바이스 리스트 */
};
PM 도메인 계층 다이어그램 (SoC 예시)
genpd API
/* Provider 등록 */
pm_genpd_init(genpd, gov, is_off); /* 도메인 초기화 */
pm_genpd_add_subdomain(parent, child); /* 계층 관계 설정 */
of_genpd_add_provider_onecell(np, data); /* DT provider 등록 */
/* Consumer 연결 (DT 기반 자동) */
dev_pm_domain_attach(dev, true); /* 디바이스를 도메인에 연결 */
dev_pm_domain_detach(dev, true); /* 디바이스를 도메인에서 분리 */
Device Tree 바인딩
/* Provider (SoC DTSI) */
power_domains: power-controller@10000 {
compatible = "vendor,soc-power-domains";
#power-domain-cells = <1>;
};
/* Consumer (디바이스 노드) */
usb@20000 {
compatible = "vendor,usb-controller";
power-domains = <&power_domains 3>; /* domain index 3 */
};
PM QoS (Quality of Service)
PM QoS는 전원 관리 결정에 성능 제약 조건을 부과하는 프레임워크입니다. 디바이스나 서브시스템이 최소 응답 시간을 요구하면, cpuidle 거버너가 이 제약을 존중하여 깊은 C-state 진입을 제한합니다.
QoS 유형
| 유형 | 인터페이스 | 영향 |
|---|---|---|
| CPU latency QoS | /dev/cpu_dma_latency | 시스템 전체 최대 허용 C-state exit latency 제한 |
| 디바이스 PM QoS (latency) | per-device sysfs | 개별 디바이스의 resume latency 제한 |
| 디바이스 PM QoS (flags) | per-device sysfs | NO_POWER_OFF 등 디바이스별 PM 정책 |
| Frequency QoS | cpufreq 내부 | 최소/최대 CPU 주파수 제한 |
API
/* CPU latency QoS (글로벌) */
struct pm_qos_request req;
cpu_latency_qos_add_request(&req, 50); /* 최대 50μs latency 요구 */
cpu_latency_qos_update_request(&req, 100);
cpu_latency_qos_remove_request(&req);
/* 디바이스 PM QoS */
dev_pm_qos_add_request(dev, &req, DEV_PM_QOS_RESUME_LATENCY, 100);
dev_pm_qos_update_request(&req, 200);
dev_pm_qos_remove_request(&req);
cpuidle 연동
cpuidle 거버너(menu/TEO)는 매 idle 진입 시 cpuidle_governor_latency_req()를 호출하여 현재 유효한 최소 latency 제약을 확인합니다. exit_latency가 이 제약을 초과하는 C-state는 선택 후보에서 제외됩니다.
# 사용자 공간에서 latency 제약 설정
# /dev/cpu_dma_latency에 4바이트 LE 값 쓰기
# fd를 열고 있는 동안만 제약 유효
exec 3> /dev/cpu_dma_latency
printf '\x32\x00\x00\x00' >&3 # 50μs 제약
# ... 작업 수행 ...
exec 3>&- # fd 닫기 → 제약 해제
Wakeup Sources
Wakeup source는 시스템을 절전 상태에서 깨울 수 있는 이벤트 소스를 추적합니다. 진행 중인 wakeup 이벤트가 있으면 suspend 진입을 중단(abort)합니다.
wakeup_source API
/* 디바이스에 wakeup capability 설정 */
device_init_wakeup(dev, true); /* wakeup source 등록 + capable 설정 */
device_set_wakeup_enable(dev, true);
/* Wakeup 이벤트 보고 */
pm_wakeup_event(dev, msec); /* msec 동안 suspend 방지 */
pm_stay_awake(dev); /* suspend 방지 시작 */
pm_relax(dev); /* suspend 방지 해제 */
__pm_wakeup_event(ws, msec); /* wakeup_source 직접 사용 */
wakeup_count 메커니즘
Suspend 진입 시 wakeup_count를 사용하면 race condition을 방지할 수 있습니다:
# 1. 현재 wakeup count 읽기
count=$(cat /sys/power/wakeup_count)
# 2. 같은 값을 다시 쓰기 (이 사이에 wakeup이 발생했으면 실패)
echo $count > /sys/power/wakeup_count || exit 1
# 3. suspend 진입 (wakeup 발생 시 즉시 abort)
echo mem > /sys/power/state
sysfs 인터페이스
# 디바이스별 wakeup 정보
/sys/devices/.../power/
├── wakeup # enabled / disabled
└── wakeup_count # 디바이스의 wakeup 이벤트 횟수
# 전역 wakeup source 목록
/sys/class/wakeup/wakeup*/
├── name # wakeup source 이름
├── active_count # 활성화 횟수
├── event_count # 총 이벤트 수
├── wakeup_count # 실제 시스템 wakeup 횟수
├── expire_count # 타임아웃 만료 횟수
├── active_time_ms # 총 활성 시간
├── total_time_ms # 총 시간
├── max_time_ms # 최대 단일 활성 시간
├── last_change_ms # 마지막 상태 변경
└── prevent_suspend_time_ms
/sys/power/autosleep에 mem을 쓰면 시스템은 wakeup 이벤트가 없을 때 자동으로 suspend에 진입합니다. Android에서 널리 사용됩니다.Android 전원 관리
Android의 전원 관리는 커널의 wakeup source/autosleep 메커니즘을 핵심으로 사용한다. 초기 Android의 wakelocks는 out-of-tree 패치였으나, 커널 3.5에서 PM wakeup sources로 통합되었다.
| Android 용어 | 커널 메커니즘 | 역사 |
|---|---|---|
| WakeLock (Java API) | wakeup_source + autosleep | wakelocks → PM wakeup sources (3.5+) |
| Doze 모드 | alarm_timer + suspend | Android 6.0+, 유휴 시 앱 활동 제한 |
| App Standby Buckets | cgroup freezer + cpuset | Android 9+, 앱 사용 빈도별 차등 제한 |
/* Android WakeLock → 커널 wakeup_source 매핑 */
# /sys/power/wake_lock에 쓰기 → 유저스페이스 wakeup_source 생성
# PowerManager.WakeLock.acquire() → 최종적으로 여기에 도달
echo my_wakelock > /sys/power/wake_lock # suspend 방지
echo my_wakelock > /sys/power/wake_unlock # suspend 허용
/* UCLAMP: Android의 Power HAL이 태스크별 util 힌트를 설정 */
# foreground 앱: 높은 uclamp.min으로 성능 보장
# background 앱: 낮은 uclamp.max로 전력 절약
# EAS(Energy Aware Scheduling)가 util 힌트를 기반으로 에너지 효율적 코어 선택
Android의 Power HAL, UCLAMP, cgroup 기반 에너지 관리 등 심화 내용은 Android 커널 — 프로세스 모델을 참고하라.
Energy Model & EAS
Energy Model(EM)은 CPU의 주파수-전력 관계를 모델링하고, EAS(Energy Aware Scheduling)는 이 정보를 활용하여 태스크를 에너지 효율적인 CPU에 배치합니다.
핵심 구조체
/* include/linux/energy_model.h */
struct em_perf_state {
unsigned long frequency; /* kHz */
unsigned long power; /* mW (또는 추상 단위) */
unsigned long cost; /* 에너지 비용 (내부 계산) */
unsigned long flags;
};
struct em_perf_domain {
struct em_perf_state *table;
int nr_perf_states;
unsigned long cpus[]; /* cpumask */
};
EAS 활성화 조건
- 비대칭 CPU 토폴로지: big.LITTLE 또는 Intel Hybrid(P-core/E-core) (
SD_ASYM_CPUCAPACITY) - Energy Model 등록: cpufreq 드라이버가
em_dev_register_perf_domain()호출 - schedutil 거버너: cpufreq 거버너가 schedutil이어야 함
- 오버유틸 아님: root domain의 총 utilization이 capacity의 80% 미만
EAS 태스크 배치 흐름
find_energy_efficient_cpu(p, prev_cpu, sd_flag)
→ 각 performance domain에 대해:
→ 각 CPU에 태스크를 가상 배치
→ em_cpu_energy()로 에너지 소비 계산
→ compute_energy(): Σ(OPP_power × utilization / capacity)
→ 에너지가 최소인 CPU 선택
→ 이전 CPU 대비 6% 이상 절감 시에만 마이그레이션
EM + EAS 연동 다이어그램
OPP 프레임워크
OPP(Operating Performance Points)는 디바이스가 동작할 수 있는 전압-주파수 조합을 관리합니다. cpufreq, devfreq, Energy Model 등이 OPP 테이블을 참조하여 동적 전압/주파수 스케일링(DVFS)을 수행합니다.
dev_pm_opp API
| 함수 | 설명 |
|---|---|
dev_pm_opp_add(dev, freq, u_volt) | 동적 OPP 추가 |
dev_pm_opp_remove(dev, freq) | OPP 제거 |
dev_pm_opp_find_freq_ceil(dev, &freq) | freq 이상인 최소 OPP |
dev_pm_opp_find_freq_floor(dev, &freq) | freq 이하인 최대 OPP |
dev_pm_opp_get_voltage(opp) | OPP의 전압 반환 |
dev_pm_opp_get_freq(opp) | OPP의 주파수 반환 |
dev_pm_opp_set_rate(dev, freq) | 주파수 설정 (전압 자동 조정) |
dev_pm_opp_of_add_table(dev) | DT에서 OPP 테이블 로드 |
Device Tree OPP 테이블
cpu_opp_table: opp-table {
compatible = "operating-points-v2";
opp-shared;
opp-600000000 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <900000>;
opp-supported-hw = <0x3>;
};
opp-1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1100000 1050000 1150000>; /* target min max */
};
opp-1800000000 {
opp-hz = /bits/ 64 <1800000000>;
opp-microvolt = <1250000>;
turbo-mode; /* 부스트 전용 */
};
};
Thermal Throttling 연동
열 관리 서브시스템이 dev_pm_opp_put_clkname()과 cooling device를 통해 최대 OPP를 동적으로 제한합니다. 과열 시 상위 OPP가 비활성화되어 주파수가 자동으로 하향됩니다.
powercap & RAPL
powercap 프레임워크는 전력 상한(power capping)을 설정하는 통합 인터페이스를 제공합니다. 가장 대표적인 백엔드는 Intel/AMD의 RAPL(Running Average Power Limit)로, 프로세서 패키지·코어·메모리 등 전력 도메인별 에너지 소비를 모니터링하고 제한할 수 있습니다.
powercap/RAPL 아키텍처
RAPL은 하드웨어 MSR(또는 TPMI)에서 에너지 카운터와 전력 제한을 읽고 쓰는 커널 드라이버를 통해 powercap 프레임워크에 등록됩니다. 사용자 공간은 sysfs 또는 perf 이벤트를 통해 접근합니다.
RAPL 도메인
RAPL은 프로세서 내부를 여러 전력 도메인(power domain)으로 분리하여 도메인별 에너지 측정과 전력 제한을 제공합니다. 지원 도메인은 프로세서 세대에 따라 다릅니다.
| 도메인 | 설명 | Intel | AMD |
|---|---|---|---|
| package | 소켓 전체 (CPU + 언코어 + iGPU) | Sandy Bridge+ (2세대+) | Zen (Family 17h+) |
| core (PP0) | CPU 코어만 | Sandy Bridge+ | Zen+ |
| uncore (PP1) | iGPU / 언코어 | 클라이언트 전용 (Sandy Bridge+) | — |
| dram | 메모리 컨트롤러/DRAM | 서버 (Sandy Bridge-EP+), 클라이언트 (Haswell+) | — |
| psys | 플랫폼 전체 (SoC + PCH) | Skylake+ (모바일/NUC) | — |
psys 도메인은 CPU뿐 아니라 PCH, 온보드 디바이스를 포함한 플랫폼 전체 전력을 측정합니다. 노트북/NUC 등 배터리 기반 시스템에서 전체 시스템 전력 예산(power budget) 관리에 유용합니다.PL1/PL2/PL3/PL4 전력 제한 계층
RAPL은 최대 4단계의 전력 제한(Power Limit)을 제공합니다. 각 단계는 허용 전력과 시간 윈도우가 다르며, 하드웨어가 자율적으로 스로틀링을 적용합니다.
| 레벨 | 명칭 | 시간 윈도우 | 동작 방식 | MSR |
|---|---|---|---|---|
| PL1 | Long-Term (장기) | 1~128초 (설정 가능) | 지속 가능한 TDP. 윈도우 내 평균 전력이 PL1 이하 유지 | MSR_PKG_POWER_LIMIT [14:0] |
| PL2 | Short-Term (단기) | 1~10ms 수준 | Turbo Boost 시 허용하는 순간 최대 전력. PL1의 1.2~1.5배 | MSR_PKG_POWER_LIMIT [46:32] |
| PL3 | Peak Power (피크) | ~10ms | 배터리 기반 시스템의 순간 피크 제한 (Kaby Lake+) | MSR_PL3_CONTROL |
| PL4 | Max Power (최대) | 즉시 | 하드웨어 즉시 차단 — 전류/전력 한계 초과 시 주파수 급강하 | MSR_VR_CURRENT_CONFIG |
/* powercap sysfs에서 PL1/PL2 확인 및 설정 */
# PL1 (constraint_0) 확인
cat /sys/class/powercap/intel-rapl:0/constraint_0_name # "long_term"
cat /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw # 현재 PL1 (μW)
cat /sys/class/powercap/intel-rapl:0/constraint_0_time_window_us # 시간 윈도우 (μs)
# PL2 (constraint_1) 확인
cat /sys/class/powercap/intel-rapl:0/constraint_1_name # "short_term"
cat /sys/class/powercap/intel-rapl:0/constraint_1_power_limit_uw
# PL1을 15W로 설정 (root 권한 필요)
echo 15000000 > /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw
powercap sysfs 인터페이스
/sys/class/powercap/intel-rapl:0/ # package-0
├── name # "package-0"
├── energy_uj # 누적 에너지 (μJ, 읽기 전용)
├── max_energy_range_uj # 오버플로 범위
├── enabled # 도메인 활성화 여부
├── constraint_0_power_limit_uw # PL1 (장기 전력 제한, μW)
├── constraint_0_time_window_us # PL1 시간 윈도우
├── constraint_0_name # "long_term"
├── constraint_0_max_power_uw # PL1 최대 허용값
├── constraint_1_power_limit_uw # PL2 (단기 전력 제한)
├── constraint_1_time_window_us
├── constraint_1_name # "short_term"
└── constraint_1_max_power_uw
intel-rapl:0:0/ # core 서브도메인 (PP0)
├── name # "core"
└── energy_uj # 코어 에너지 (읽기 전용)
intel-rapl:0:1/ # uncore 서브도메인 (PP1)
intel-rapl:0:2/ # dram 서브도메인 (있는 경우)
RAPL 커널 드라이버 구조
커널의 RAPL 지원은 공통 로직과 하드웨어 백엔드로 분리된 모듈러 구조입니다.
| 모듈 | 경로 | 역할 |
|---|---|---|
| intel_rapl_common | drivers/powercap/intel_rapl_common.c | RAPL 도메인 관리, powercap zone 등록, 에너지 단위 변환, PL 읽기/쓰기 공통 로직 |
| intel_rapl_msr | drivers/powercap/intel_rapl_msr.c | MSR 기반 백엔드 — 대부분의 Intel/AMD CPU. rdmsr/wrmsr로 RAPL MSR 접근 |
| intel_rapl_tpmi | drivers/powercap/intel_rapl_tpmi.c | TPMI(Topology Aware Register and PM Capsule Interface) 백엔드 — Meteor Lake+ MMIO 기반 |
| amd_energy | drivers/hwmon/amd_energy.c | AMD Zen hwmon 드라이버 (powercap 미사용, hwmon으로 에너지 노출) |
/* intel_rapl_common.c — 핵심 구조체 */
struct rapl_domain {
const char *name;
enum rapl_domain_type id; /* RAPL_DOMAIN_PACKAGE, PP0, PP1, DRAM, PLATFORM */
u64 energy_unit; /* 에너지 단위 (MSR_RAPL_POWER_UNIT에서 파생) */
u64 power_unit; /* 전력 단위 */
struct powercap_zone power_zone; /* powercap 프레임워크 zone */
struct rapl_package *rp; /* 소속 패키지 */
};
struct rapl_package {
unsigned int id; /* 패키지(소켓) ID */
unsigned int nr_domains; /* 이 패키지의 도메인 수 */
struct rapl_domain *domains; /* 도메인 배열 */
struct rapl_if_priv *priv; /* 백엔드별 private 데이터 */
};
# RAPL 모듈 로드 확인
lsmod | grep rapl
# intel_rapl_msr 20480 0
# intel_rapl_common 36864 1 intel_rapl_msr
# powercap 16384 1 intel_rapl_common
# 에너지 단위 확인 (MSR_RAPL_POWER_UNIT, 606h)
rdmsr 0x606
# 결과 해석: bits[12:8]=에너지 단위, bits[3:0]=전력 단위, bits[19:16]=시간 단위
TPMI (Topology Aware Register and PM Capsule Interface)
TPMI는 Intel Meteor Lake(클라이언트, 2023) 및 Sapphire Rapids(서버, 4세대 Xeon) 이후 도입된 MMIO 기반 PM 레지스터 인터페이스입니다. PCIe 표준 기반의 아키텍처 모델로 설계되어, 기존 MSR의 모델별 코드 유지보수 부담을 줄이고 멀티 다이/칩렛 아키텍처에서 다이별(per-die) 세밀한 전력 관리를 지원합니다. Granite Rapids(Xeon 6 P-core), Sierra Forest(Xeon 6 E-core), Clearwater Forest 등 후속 세대에서 전면 채택되었습니다.
rdmsr/wrmsr) 직렬 접근이지만, TPMI는 PCI MMIO BAR를 통해 토폴로지 인식(topology-aware) 병렬 접근이 가능합니다. RAPL뿐 아니라 SST, Uncore 주파수 제어, 성능 제한 사유(PLR) 등 다양한 PM 기능이 TPMI를 통해 통합 제공됩니다.TPMI vs MSR 비교
| 항목 | MSR 방식 | TPMI 방식 |
|---|---|---|
| 접근 방식 | rdmsr/wrmsr (CPU 명령어) | MMIO (readq/writeq, PCI BAR 매핑) |
| 토폴로지 | CPU별 (per-CPU), IPI로 원격 CPU 접근 | 다이별 (per-die), 직접 MMIO 매핑 |
| 확장성 | 다이 수 증가 시 MSR 주소 충돌 우려 | PCI VSEC + PFS로 동적 열거, 다이별 독립 주소 공간 |
| 병렬 접근 | MSR 접근은 직렬화 (serialize) | 서로 다른 다이의 MMIO에 동시 접근 가능 |
| 기능 발견 | CPUID + MSR 존재 여부 수동 확인 | PFS(PM Feature Structure)로 자동 열거 |
| 디바이스 모델 | 직접 MSR 접근, 플랫폼 디바이스 없음 | PCI auxiliary 디바이스로 등록, 표준 드라이버 모델 |
| 유지보수 | CPU 모델별 MSR 테이블 유지 필요 | 아키텍처 인터페이스 — 세대 간 호환 유지 |
| 토폴로지 계층 | 소켓 → CPU (flat) | 소켓 → 다이 → 전력 도메인 → 패브릭 클러스터 |
| 지원 범위 | Sandy Bridge (2011) 이후 전 세대 | Meteor Lake/Sapphire Rapids (2023) 이후 |
TPMI 아키텍처
TPMI는 PCI VSEC(Vendor Specific Extended Capability, ID=66)를 통해 발견됩니다. Intel VSEC 드라이버(intel_vsec.c)가 OOB(Out-of-Band) PCI 디바이스의 VSEC/DVSEC 구조를 스캔하여 TPMI를 감지하면, auxiliary 버스에 TPMI 코어 디바이스를 생성합니다. TPMI 코어는 VSEC 헤더의 tBIR(BAR Indicator Register) 필드로 대상 PCI BAR를 식별하고, Address 필드로 BAR 내 PFS 시작 오프셋을 계산합니다.
PCI VSEC 발견 및 PFS 파싱
TPMI는 PCIe 설정 공간의 VSEC(Vendor Specific Extended Capability)를 통해 발견됩니다. Intel VSEC 드라이버(intel_vsec.c)가 TPMI 타입의 VSEC를 감지하면 TPMI 코어 드라이버를 로드하고, TPMI 코어는 MMIO에 매핑된 PFS(PM Feature Structure) 테이블을 파싱합니다.
/* PFS (PM Feature Structure) 엔트리 — 하드웨어 비트필드 레이아웃 */
/* drivers/platform/x86/intel/vsec_tpmi.c (커널 6.13+, 이전: tpmi.c) */
struct intel_tpmi_pfs_entry {
u64 tpmi_id:8; /* PM 기능 식별자 (0=RAPL, 2=UFS, 5=SST ...) */
u64 num_entries:8; /* 인스턴스 수 (전력 도메인/다이 수와 대응) */
u64 entry_size:16; /* 인스턴스 MMIO 블록 크기 (32비트 워드 단위) */
u64 cap_offset:16; /* PM_Features 베이스로부터 오프셋 (KB 단위) */
u64 attribute:2; /* 속성 플래그 (읽기 전용, 잠금 등) */
u64 reserved:14;
} __packed;
/* TPMI 코어의 PFS 파싱 흐름 */
tpmi_pfs_init(tpmi_info):
/* 1. PCI BAR0에서 PFS 헤더 읽기 */
pfs_start = devm_ioremap_resource(dev, &pci_resource);
/* 2. PFS 엔트리 순회하며 기능 열거 */
for (i = 0; i < pfs_count; i++) {
read_pfs_entry(&pfs[i]);
/* 3. 기능별 MMIO 영역 매핑 */
feature_mem = pfs_start + pfs[i].cap_offset;
/* 4. auxiliary 디바이스 생성 (intel_rapl_tpmi, isst_tpmi 등) */
intel_vsec_add_aux(dev, NULL, &feature_info, name);
}
TPMI Feature ID 매핑
각 PM 기능은 고유한 TPMI ID를 가지며, PFS 테이블에서 이 ID를 통해 발견됩니다. 커널은 ID에 따라 해당 feature 드라이버의 auxiliary 디바이스를 생성합니다.
| ID | 이름 | 커널 드라이버 | 설명 |
|---|---|---|---|
| 0 | RAPL | intel_rapl_tpmi | MMIO 기반 RAPL 도메인 — 에너지 측정 및 전력 제한 |
| 1 | PEM | (커널 내부) | Performance and Energy Monitoring — 성능/에너지 모니터링 |
| 2 | UFS | uncore-freq-tpmi | Uncore Frequency Scaling — 다이별 언코어 주파수 제어 |
| 5 | SST | isst_tpmi | Intel Speed Select Technology — PP/BF/TF/CP 프로파일 |
| 8 | PLR | tpmi_plr | Performance Limit Reasons — 스로틀링 원인 보고 (debugfs) |
| 10 | UMS | uncore-freq-tpmi | Uncore Mesh/SoC 주파수 제어 |
| 12 | PWR_DMN | tpmi_power_domains | 전력 도메인 파티셔닝 |
| 0x81 | INFO | (TPMI 코어 내부) | CPU↔PCI 토폴로지 매핑 (패키지 ID, PCI BDF). 실제 PM 기능이 아닌 메타 정보 |
TPMI_INFO_ID(0x81)는 CPU 소켓과 PCI 디바이스 간의 매핑 정보를 제공하는 특수 pseudo-feature입니다. TPMI 코어가 이 정보를 읽어 intel_tpmi_plat_info의 package_id와 PCI BDF를 채웁니다.TPMI 코어 구조체
/* include/linux/intel_tpmi.h — TPMI API 핵심 구조체 */
/* Feature 드라이버가 TPMI 코어에서 받는 플랫폼 정보 */
struct intel_tpmi_plat_info {
u8 package_id; /* CPU 소켓(패키지) ID */
u8 bus_number; /* PCI 버스 번호 */
u8 device_number; /* PCI 디바이스 번호 */
u8 function_number; /* PCI 펑션 번호 */
u8 segment; /* PCI 세그먼트 ID */
u8 partition; /* 패키지 파티션 ID */
u16 cdie_mask; /* 현재 파티션의 컴퓨트 다이 비트맵 */
};
/* TPMI 코어 내부 — 디바이스별 기능 관리 */
struct intel_tpmi_info {
struct auxiliary_device *vsec_dev; /* 부모 VSEC auxiliary 디바이스 */
int feature_count; /* 발견된 PM 기능 수 */
struct intel_tpmi_pm_feature *tpmi_features; /* PM 기능 배열 */
};
/* PM 기능 인스턴스 정보 */
struct intel_tpmi_pm_feature {
struct intel_tpmi_pfs_entry pfs_header; /* 하드웨어 PFS 헤더 */
unsigned int vsec_offset; /* MMIO 시작 주소 (바이트) */
/* vsec_offset = VSEC Address + cap_offset(KB) * 1024 */
};
/* CPU↔PCI 토폴로지 매핑 헤더 (TPMI_INFO_ID=0x81에서 읽음) */
struct tpmi_info_header {
u64 fn:3; /* PCI Function 번호 */
u64 dev:5; /* PCI Device 번호 */
u64 bus:8; /* PCI Bus 번호 */
u64 pkg:8; /* CPU Package ID */
u64 reserved:40;
} __packed;
/* Feature 드라이버가 사용하는 TPMI API (include/linux/intel_tpmi.h) */
struct intel_tpmi_plat_info *tpmi_get_platform_data(
struct auxiliary_device *adev); /* 플랫폼 정보 반환 */
int tpmi_get_resource_count(
struct auxiliary_device *adev); /* MMIO 인스턴스 수 반환 */
struct resource *tpmi_get_resource_at_index(
struct auxiliary_device *adev,
int index); /* 인덱스별 MMIO resource 반환 */
int tpmi_get_feature_status(
struct auxiliary_device *adev,
int feature_id,
bool *read_blocked,
bool *write_blocked); /* 기능 잠금/비활성 상태 확인 */
RAPL over TPMI (intel_rapl_tpmi.c)
Meteor Lake+에서 RAPL 레지스터는 MSR 대신 TPMI MMIO를 통해 접근합니다. intel_rapl_tpmi 드라이버는 TPMI 코어에서 RAPL 기능(ID=0)의 MMIO 영역을 받아, 기존 intel_rapl_common의 powercap 프레임워크에 MMIO 백엔드로 등록합니다. 각 RAPL 도메인의 MMIO 블록 크기는 128바이트(TPMI_RAPL_DOMAIN_SIZE)이며, 각 Power Limit이 개별 레지스터를 가집니다(기존 MSR은 PL1+PL2가 하나의 64비트 MSR에 패킹).
등록 흐름: ① TPMI 코어가 PFS에서 TPMI_ID_RAPL=0 발견 → ② auxiliary 디바이스 intel_vsec.tpmi-rapl.N 생성 → ③ intel_rapl_tpmi probe → ④ tpmi_get_resource_at_index()로 MMIO 매핑 → ⑤ rapl_add_package()로 powercap zone 등록 → ⑥ /sys/class/powercap/intel-rapl:* 노출
/* intel_rapl_tpmi.c — MMIO 기반 RAPL 백엔드 */
/* TPMI RAPL MMIO 레지스터 오프셋 (MSR 주소 대신 MMIO 오프셋 사용) */
#define TPMI_RAPL_REG_HEADER 0x00
#define TPMI_RAPL_REG_UNIT 0x08 /* 에너지/전력/시간 단위 */
#define TPMI_RAPL_REG_PL1 0x10 /* Package Power Limit 1 */
#define TPMI_RAPL_REG_PL2 0x18 /* Package Power Limit 2 */
#define TPMI_RAPL_REG_PL4 0x20 /* Package Power Limit 4 */
#define TPMI_RAPL_REG_ENERGY_STATUS 0x28 /* 에너지 카운터 */
#define TPMI_RAPL_REG_PERF_STATUS 0x30 /* 성능 스로틀 상태 */
#define TPMI_RAPL_REG_POWER_INFO 0x38 /* TDP, min/max 전력 */
/* 디바이스별 RAPL TPMI 패키지 구조체 */
struct tpmi_rapl_package {
struct rapl_if_priv priv; /* RAPL 인터페이스 private */
struct intel_tpmi_plat_info *tpmi_info; /* TPMI 플랫폼 정보 */
struct rapl_package *rp; /* RAPL 패키지 포인터 */
void __iomem *base; /* MMIO 베이스 주소 */
};
/* MMIO 기반 읽기 — IPI 없이 직접 접근 */
static int tpmi_rapl_read_raw(int id, struct reg_action *ra)
{
ra->value = readq(base + ra->reg.mmio_offset);
ra->value &= ra->mask; /* 마스크 적용 */
return 0;
}
/* MMIO 기반 쓰기 — read-modify-write 패턴 */
static int tpmi_rapl_write_raw(int id, struct reg_action *ra)
{
u64 val;
val = readq(base + ra->reg.mmio_offset);
val &= ~ra->mask; /* 대상 비트 클리어 */
val |= ra->value & ra->mask; /* 새 값 삽입 */
writeq(val, base + ra->reg.mmio_offset);
return 0;
}
/* rapl_if_priv — intel_rapl_common에 TPMI 백엔드 등록 */
static struct rapl_if_priv tpmi_rapl_priv = {
.type = RAPL_IF_TPMI,
.read_raw = tpmi_rapl_read_raw,
.write_raw = tpmi_rapl_write_raw,
.control_type = NULL, /* powercap에서 설정 */
.reg_unit = { .mmio_offset = TPMI_RAPL_REG_UNIT },
.regs = { /* 도메인별 레지스터 매핑 */
[RAPL_DOMAIN_PACKAGE] = {
.pl1 = { .mmio_offset = TPMI_RAPL_REG_PL1 },
.pl2 = { .mmio_offset = TPMI_RAPL_REG_PL2 },
.pl4 = { .mmio_offset = TPMI_RAPL_REG_PL4 },
.energy = { .mmio_offset = TPMI_RAPL_REG_ENERGY_STATUS },
.info = { .mmio_offset = TPMI_RAPL_REG_POWER_INFO },
},
},
};
intel_rapl_common 프레임워크를 사용하므로, 사용자 공간에서는 기존과 동일한 /sys/class/powercap/intel-rapl:* 경로로 접근합니다. MSR→TPMI 전환은 커널 내부에서 투명하게 처리됩니다.SST (Speed Select Technology) over TPMI
Intel Speed Select Technology(SST)는 동일 CPU 내에서 코어별 차등 성능 프로파일을 제공합니다. Meteor Lake+에서 SST는 TPMI(Feature ID=5)를 통해 접근합니다.
| SST 기능 | 설명 | 사용 사례 |
|---|---|---|
| SST-PP | Performance Profile — 미리 정의된 주파수/TDP 프로파일 전환 | 워크로드에 따라 코어 수 vs 주파수 트레이드오프 |
| SST-BF | Base Frequency — 특정 코어의 기본 주파수를 상향 | 지연시간에 민감한 코어에 높은 보장 주파수 할당 |
| SST-TF | Turbo Frequency — 특정 코어에 터보 주파수 우선 배분 | 고성능 요구 스레드에 터보 주파수 집중 |
| SST-CP | Core Power — 코어별 전력 우선순위 설정 | 중요 코어에 전력 예산 우선 배정 |
# intel-speed-select CLI 도구로 SST 상태 확인
intel-speed-select --info
intel-speed-select perf-profile info # SST-PP 프로파일 정보
intel-speed-select base-freq info # SST-BF 지원 코어 확인
intel-speed-select turbo-freq info # SST-TF 상태
intel-speed-select core-power info # SST-CP 우선순위
# SST-CP로 특정 코어에 높은 우선순위 부여
intel-speed-select core-power assoc --clos 0 --core 0,1,2,3 # 고성능 그룹
intel-speed-select core-power assoc --clos 3 --core 4,5,6,7 # 저전력 그룹
Uncore 주파수 제어 over TPMI
TPMI 이전에는 Uncore 주파수가 패키지/다이 단위로만 제어 가능했습니다. TPMI(Feature ID=2)는 패브릭 클러스터(fabric cluster) 단위의 세밀한 Uncore 주파수 제어를 지원합니다.
# Uncore 주파수 토폴로지 계층 (TPMI 기반)
# Package → Die → Power Domain → Fabric Cluster
# 기존 per-die 인터페이스 (TPMI 이전에도 동일)
/sys/devices/system/cpu/intel_uncore_frequency/
package_00_die_00/
├── initial_max_freq_khz # 초기 최대 주파수
├── initial_min_freq_khz # 초기 최소 주파수
├── max_freq_khz # 현재 최대 주파수 (쓰기 가능)
└── min_freq_khz # 현재 최소 주파수 (쓰기 가능)
# TPMI 추가: 클러스터 레벨 제어
uncore00/
├── package_id # 소켓 ID
├── domain_id # 전력 도메인 ID
├── fabric_cluster_id # 패브릭 클러스터 ID
├── current_freq_khz # 현재 실제 주파수 (읽기 전용)
├── max_freq_khz
└── min_freq_khz
PLR (Performance Limit Reasons) over TPMI
PLR(Feature ID=8)은 CPU 성능이 제한(스로틀링)되는 원인을 실시간으로 보고합니다. TPMI를 통해 debugfs에 노출되며, 성능 문제 진단에 활용됩니다.
# PLR 상태 확인 (debugfs)
ls /sys/kernel/debug/tpmi-plr/
# die0/ die1/ ...
cat /sys/kernel/debug/tpmi-plr/die0/status
# 각 비트가 스로틀링 원인을 나타냄:
# - thermal: 열 제한
# - power_limit_pl1: PL1(장기 전력) 초과
# - power_limit_pl2: PL2(단기 전력) 초과
# - electrical_design_point: 전류 한계 도달
# - turbo_transition_attenuation: 터보 전환 감쇠
# - prochot: 외부 PROCHOT# 신호
# PLR 스티키 비트 클리어 (해당 파일에 0 쓰기)
echo 0 > /sys/kernel/debug/tpmi-plr/die0/status
TPMI 커널 설정
# TPMI 관련 Kconfig 옵션
CONFIG_INTEL_VSEC=m # Intel Vendor Specific Extended Capabilities (필수)
CONFIG_INTEL_TPMI=m # TPMI 코어 드라이버
CONFIG_INTEL_RAPL_TPMI=m # RAPL over TPMI 백엔드
CONFIG_INTEL_UNCORE_FREQ_TPMI=m # Uncore 주파수 제어 over TPMI
CONFIG_INTEL_SPEED_SELECT_INTERFACE=m # SST over TPMI
# 로드된 TPMI 모듈 확인
lsmod | grep tpmi
# intel_rapl_tpmi 16384 0
# intel_uncore_freq_tpmi 16384 0
# isst_tpmi 20480 0
# intel_tpmi 24576 3 intel_rapl_tpmi,intel_uncore_freq_tpmi,isst_tpmi
# intel_vsec 16384 1 intel_tpmi
# TPMI 디바이스 확인 (PCI VSEC)
lspci -d 8086: -vvv | grep -A5 "Vendor Specific"
TPMI 소스 트리 구조
| 파일 | 역할 |
|---|---|
include/linux/intel_tpmi.h | TPMI API 헤더 — Feature ID 상수, 플랫폼 정보 구조체, 헬퍼 함수 |
drivers/platform/x86/intel/vsec.c | PCI VSEC 드라이버 — TPMI 타입 감지 및 코어 디바이스 생성 |
drivers/platform/x86/intel/tpmi.c | TPMI 코어 — PFS 파싱, MMIO 매핑, auxiliary 디바이스 생성, debugfs (커널 6.13+에서 vsec_tpmi.c로 개명) |
drivers/powercap/intel_rapl_tpmi.c | RAPL TPMI 백엔드 — MMIO 기반 에너지/전력 레지스터 접근 |
drivers/platform/x86/intel/uncore-frequency/ | Uncore 주파수 제어 (TPMI + MSR 백엔드) |
drivers/platform/x86/intel/speed_select_if/ | SST 인터페이스 (TPMI + MMIO + MSR 백엔드) |
TPMI debugfs 인터페이스
TPMI 코어 드라이버는 debugfs를 통해 PFS 테이블 덤프와 Feature별 MMIO 원시 읽기/쓰기를 제공합니다. 주로 디버깅과 펌웨어 설정 확인에 사용됩니다.
# TPMI debugfs 구조
/sys/kernel/debug/tpmi-<N>/
├── pfs_dump # PFS 테이블 전체 엔트리 출력
├── tpmi-id-<feature_id>/
│ ├── mem_dump # Feature MMIO 영역 원시 덤프 (읽기 전용)
│ └── mem_write # 특정 오프셋에 MMIO 쓰기 (디버그용)
└── ...
# PFS 엔트리 확인 (어떤 PM 기능이 지원되는지)
cat /sys/kernel/debug/tpmi-0/pfs_dump
# RAPL MMIO 레지스터 원시 값 확인
cat /sys/kernel/debug/tpmi-0/tpmi-id-0/mem_dump
TPMI 커널 머지 타임라인
| 커널 버전 | TPMI 마일스톤 |
|---|---|
| 6.3 | TPMI 기본 열거 드라이버 병합 (tpmi.c) |
| 6.4 | RAPL TPMI 드라이버 (intel_rapl_tpmi.c) 병합 |
| 6.5 | Uncore 주파수 TPMI 지원, SST via TPMI |
| 6.5~6.6 | TPMI debugfs, 기능 잠금 상태 인터페이스 |
| 6.6+ | CPU 토폴로지/도메인 매핑, 클러스터 레벨 제어 |
| 6.13 | 드라이버 tpmi.c → vsec_tpmi.c 개명, Clearwater Forest ID 추가 |
AMD RAPL 지원
AMD는 Zen 아키텍처(Family 17h, Ryzen 1000/EPYC 7001) 이후로 RAPL 호환 MSR을 제공합니다. 커널 intel_rapl_msr 드라이버가 AMD CPU도 지원하여 동일한 powercap sysfs 인터페이스로 접근 가능합니다.
| 항목 | Intel RAPL | AMD RAPL |
|---|---|---|
| 지원 시작 | Sandy Bridge (2세대, 2011) | Zen (Family 17h, 2017) |
| 도메인 | package, core, uncore, dram, psys | package, core (Zen 2+에서 확장) |
| MSR 주소 | 606h, 610h, 611h, 619h, 641h 등 | C001_0299h (core), C001_029Ah (pkg) 등 |
| 전력 제한 쓰기 | PL1/PL2 쓰기 가능 | 읽기 전용 (에너지 측정만, 전력 제한 설정 불가) |
| 커널 드라이버 | intel_rapl_msr / intel_rapl_tpmi | intel_rapl_msr (AMD 지원 포함) |
| perf 이벤트 | power/energy-pkg,cores,ram/ | power/energy-pkg,cores/ |
amd_pstate 드라이버의 EPP(Energy Performance Preference)를 통해 관리합니다.perf 전력 이벤트 및 모니터링 도구
# RAPL 전력 측정 (perf) — 5초간 에너지 소비 측정
perf stat -e power/energy-pkg/,power/energy-cores/,power/energy-ram/ sleep 5
# 특정 프로세스의 전력 소비 프로파일링
perf stat -e power/energy-pkg/,power/energy-cores/ -- make -j$(nproc)
# turbostat — 실시간 전력/주파수/온도 모니터링
turbostat --show Package,Core,CPU,Avg_MHz,Busy%,PkgWatt,CorWatt,RAMWatt --interval 1
# powertop — 전력 소비 분석 + 절전 추천
powertop --auto-tune # 자동 절전 최적화 적용
powertop --csv=report.csv --time=30 # 30초 측정 후 CSV 리포트
# powercap 에너지 직접 읽기 (커스텀 스크립트용)
E1=$(cat /sys/class/powercap/intel-rapl:0/energy_uj)
sleep 5
E2=$(cat /sys/class/powercap/intel-rapl:0/energy_uj)
echo "5초간 소비: $(( (E2 - E1) / 1000000 )) J, 평균: $(( (E2 - E1) / 5000000 )) W"
RAPL 정확도·보안·제한사항
energy_uj 및 perf power 이벤트 접근에 CAP_SYS_RAWIO 또는 root 권한이 필요하도록 변경되었습니다.| 항목 | 설명 |
|---|---|
| 에너지 해상도 | 약 15.3μJ (2-16 J × 에너지 단위). 실제 정확도는 ±5~10% 수준으로 절대 전력 측정보다는 상대 비교에 적합 |
| 카운터 오버플로 | 32비트 에너지 카운터는 max_energy_range_uj 도달 시 0으로 랩어라운드. 고부하 서버에서 ~60초마다 발생 가능 — 폴링 간격 주의 |
| 접근 권한 (5.10+) | /sys/class/powercap/*/energy_uj: root 또는 CAP_SYS_RAWIO. perf power 이벤트: perf_event_paranoid ≤ 0 또는 CAP_PERFMON |
| 가상화 환경 | VM에서는 RAPL MSR 접근이 차단됨 (hypervisor가 trap). KVM -cpu host로 패스스루 가능하나 보안상 비권장 |
| 컨테이너 | cgroup v2의 perf 이벤트 네임스페이스와 결합하여 컨테이너별 에너지 측정 가능 (Kepler 프로젝트 등 활용) |
# 현재 perf 접근 권한 확인
cat /proc/sys/kernel/perf_event_paranoid
# 4: 모든 perf 이벤트 제한 (기본값, 일부 배포판)
# 2: 커널 이벤트 제한 (기본값)
# 1: CPU 이벤트 제한
# 0: 제한 없음
# -1: 모든 접근 허용
# RAPL 에너지 읽기가 Permission denied인 경우
sudo sysctl kernel.perf_event_paranoid=0 # 임시 해제 (재부팅 시 초기화)
# 오버플로 범위 확인
cat /sys/class/powercap/intel-rapl:0/max_energy_range_uj
# 예: 262143328850 (≈262 kJ, 150W 시 ~29분마다 오버플로)
Regulator 프레임워크
Consumer / Provider 모델
/* Consumer API — 디바이스 드라이버에서 전원 요청 */
struct regulator *reg = devm_regulator_get(dev, "vdd");
regulator_enable(reg);
regulator_set_voltage(reg, 1800000, 1800000); /* 1.8V */
regulator_disable(reg);
/* Provider — PMIC 드라이버에서 regulator 등록 */
static const struct regulator_ops my_ops = {
.enable = my_enable,
.disable = my_disable,
.set_voltage_sel = my_set_voltage,
.get_voltage_sel = my_get_voltage,
.list_voltage = regulator_list_voltage_linear,
};
static const struct regulator_desc my_desc = {
.name = "LDO1",
.ops = &my_ops,
.type = REGULATOR_VOLTAGE,
.min_uV = 800000,
.uV_step = 50000,
.n_voltages = 32,
};
Device Tree 바인딩
pmic@48 {
compatible = "vendor,pmic";
regulators {
ldo1: LDO1 {
regulator-name = "vdd_1v8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
buck1: BUCK1 {
regulator-name = "vdd_cpu";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <1400000>;
regulator-ramp-delay = <12500>; /* μV/μs */
};
};
};
/* Consumer 참조 */
cpu@0 {
cpu-supply = <&buck1>;
};
PM 디버깅
pm_test 모드
Suspend를 단계별로 테스트하여 문제 구간을 좁힐 수 있습니다:
# 테스트 모드 설정 (해당 단계까지만 진행 후 resume)
echo freezer > /sys/power/pm_test # 프로세스 동결만 테스트
echo devices > /sys/power/pm_test # 디바이스 suspend까지
echo platform > /sys/power/pm_test # 플랫폼 콜백까지
echo processors > /sys/power/pm_test # CPU 비활성화까지
echo core > /sys/power/pm_test # 코어까지 (실제 진입 직전)
# 테스트 실행
echo mem > /sys/power/state
# 테스트 후 복원
echo none > /sys/power/pm_test
suspend_stats
# Suspend 통계 확인
cat /sys/power/suspend_stats/success # 성공 횟수
cat /sys/power/suspend_stats/fail # 실패 횟수
cat /sys/power/suspend_stats/last_failed_dev # 마지막 실패 디바이스
cat /sys/power/suspend_stats/last_failed_step # 마지막 실패 단계
pm_trace & PM 디버그 메시지
# pm_trace 활성화 (RTC에 해시 저장, 실패 디바이스 추적)
echo 1 > /sys/power/pm_trace
echo mem > /sys/power/state
# resume 후 dmesg에서 "hash matches" 메시지 확인
# 상세 PM 로그 활성화
echo 1 > /sys/power/pm_debug_messages
# initcall 디버깅 (부팅 시 느린 PM 콜백 찾기)
# 커널 cmdline: initcall_debug pm_debug_messages
ftrace PM 이벤트
# PM 관련 tracepoint
echo 1 > /sys/kernel/debug/tracing/events/power/suspend_resume/enable
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_idle/enable
echo 1 > /sys/kernel/debug/tracing/events/power/device_pm_callback_start/enable
echo 1 > /sys/kernel/debug/tracing/events/power/device_pm_callback_end/enable
# suspend/resume 시간 프로파일링
echo mem > /sys/power/state
cat /sys/kernel/debug/tracing/trace
흔한 문제 & 해결
| 증상 | 원인 | 진단/해결 |
|---|---|---|
| Suspend 시 시스템 정지 | 디바이스 드라이버 콜백 무한 대기 | pm_test=devices로 범위 좁히기, pm_debug_messages=1 |
| 즉시 Wake-up | Spurious wakeup source | /proc/acpi/wakeup에서 소스 비활성화, wakeup_count 사용 |
| Resume 후 디바이스 동작 안 함 | resume 콜백에서 레지스터 복원 누락 | 드라이버의 resume/restore 콜백 확인 |
| s2idle에서 전력 소비 높음 | 디바이스가 D0 상태 유지 | turbostat/powertop으로 C-state 확인, PCI ASPM 점검 |
| Hibernate 복원 실패 | resume= 파라미터 오류, swap 부족 | resume=/dev/sdX 확인, image_size 조정 |
| 런타임 PM 동작 안 함 | pm_runtime_enable() 미호출 | runtime_status sysfs 확인, control=auto 설정 |
PM 도구 요약
| 도구 | 용도 | 패키지 |
|---|---|---|
powertop | 전력 소비 분석, 튜닝 제안 | powertop |
turbostat | C-state/P-state/전력 실시간 모니터링 | linux-tools |
cpupower | cpufreq/cpuidle 설정 조회/변경 | linux-tools |
sleepgraph | suspend/resume 타임라인 HTML 보고서 | sleepgraph (pm-graph) |
intel_gpu_top | Intel GPU 전력/활동 모니터링 | intel-gpu-tools |
rapl-read | RAPL 에너지 카운터 직접 읽기 | 커스텀/perf |
커널 설정 종합
| Kconfig 옵션 | 설명 | 기본값 |
|---|---|---|
CONFIG_PM | PM 프레임워크 전체 | Y |
CONFIG_PM_SLEEP | 시스템 절전 (suspend/hibernate) 지원 | Y |
CONFIG_SUSPEND | suspend-to-RAM (S3) / s2idle | Y |
CONFIG_HIBERNATION | Hibernate (S4, swap 이미지) | Y (데스크톱) |
CONFIG_PM_AUTOSLEEP | autosleep (/sys/power/autosleep) | N |
CONFIG_PM_WAKELOCKS | 사용자 공간 wakelock (Android) | N |
CONFIG_PM_DEBUG | PM 디버그 기능 (pm_test, suspend_stats) | N |
CONFIG_PM_TRACE | pm_trace (RTC 기반 디바이스 추적) | N |
CONFIG_CPU_IDLE | cpuidle 프레임워크 | Y |
CONFIG_CPU_IDLE_GOV_MENU | menu 거버너 | Y |
CONFIG_CPU_IDLE_GOV_TEO | TEO 거버너 | Y |
CONFIG_CPU_IDLE_GOV_HALTPOLL | haltpoll 거버너 (KVM) | M |
CONFIG_INTEL_IDLE | intel_idle 드라이버 | Y (x86) |
CONFIG_PM_GENERIC_DOMAINS | genpd (PM 도메인) | Y |
CONFIG_PM_OPP | OPP 프레임워크 | Y (ARM) |
CONFIG_POWERCAP | powercap 프레임워크 | Y |
CONFIG_INTEL_RAPL | Intel RAPL powercap 드라이버 | M |
CONFIG_REGULATOR | Regulator 프레임워크 | Y (ARM) |
CONFIG_ENERGY_MODEL | Energy Model | Y (ARM) |
CONFIG_ENERGY_EFFICIENT_SCHED | EAS 활성화 (sched 내부) | Y (ARM) |