CPUFreq

Linux 커널 CPU 주파수 스케일링(CPUFreq) 서브시스템을 심층 분석합니다. DVFS·P-state 기본 원리, policy/driver/governor 계층 구조, schedutil/ondemand/performance의 선택 기준, Intel P-state·AMD P-state·CPPC/HWP 연동, Turbo Boost와 thermal pressure 상호작용, latency/throughput/power 균형을 위한 실전 튜닝과 관측 지표까지 종합적으로 다룹니다.

전제 조건: 커널 아키텍처 문서를 먼저 읽으세요. CPU, 메모리, 인터럽트의 기본 흐름을 알고 있으면 본 문서를 더 빠르게 이해할 수 있습니다.
일상 비유: 이 개념은 작업 순서표를 따라 문제를 해결하는 과정과 비슷합니다. 핵심 용어를 먼저 잡고, 실제 동작 순서를 단계별로 확인하면 복잡한 커널 내부 동작을 안정적으로 이해할 수 있습니다.

핵심 요약

  • 핵심 객체 — 이 문서의 중심이 되는 자료구조/API를 먼저 파악합니다.
  • 실행 경로 — 요청이 들어와 처리되고 종료되는 흐름을 확인합니다.
  • 병목 지점 — 지연이나 처리량 저하가 발생하는 구간을 점검합니다.
  • 동기화 지점 — 경합과 경쟁 조건이 생길 수 있는 구간을 구분합니다.
  • 운영 포인트 — 관측 지표와 튜닝 항목을 함께 확인합니다.

단계별 이해

  1. 구성요소 확인
    핵심 자료구조와 주요 API를 먼저 식별합니다.
  2. 요청 흐름 추적
    입력부터 완료까지의 호출 경로를 순서대로 따라갑니다.
  3. 예외 경로 점검
    실패 처리, 재시도, 타임아웃 등 경계 조건을 확인합니다.
  4. 성능/안정성 점검
    잠금 경합, 큐 적체, 병목 지점을 측정하고 조정합니다.

개요

CPUFreq는 CPU 주파수와 전압을 동적으로 조정하여 전력 소비를 최적화하는 서브시스템입니다. 커널 소스에서 drivers/cpufreq/ 디렉터리에 위치하며, include/linux/cpufreq.h에 핵심 자료구조가 정의되어 있습니다. 최신 커널(6.x)에서는 schedutil 거버너가 기본값으로 설정되어, 스케줄러 부하 추적(PELT)과 직접 연동하여 에너지 효율과 반응성을 동시에 달성합니다.

DVFS (Dynamic Voltage and Frequency Scaling)

전력 소비 공식

CPU 동적 전력 소비는 주파수와 전압에 비례합니다:

P_dynamic = C × V² × F

P_dynamic : 동적 전력 (Watts)
C         : 스위칭 용량 (Capacitance, 게이트 수와 활동률에 비례)
V         : 공급 전압 (Voltage)
F         : 동작 주파수 (Frequency)

주파수를 절반으로 낮추고 전압도 낮추면:

  • 주파수: 3.0GHz → 1.5GHz (50%)
  • 전압: 1.2V → 0.9V (75%)
  • 전력: 1.2² × 3.0 = 4.32 → 0.9² × 1.5 = 1.22 (72% 감소)

전압의 제곱에 비례하므로, 전압 감소가 전력 절감에 가장 큰 영향을 줍니다. 이것이 DVFS에서 전압과 주파수를 함께 조정하는 이유입니다.

P-states vs C-states

항목P-states (CPUFreq)C-states (CPUIdle)
목적실행 중 주파수/전압 조정유휴 시 전력 절약
상태CPU 활성 (실행 중)CPU 유휴 (대기 중)
전력 절감중간 (50-80%)큼 (90-99%)
복귀 시간즉시 (0 us)수 us ~ ms
제어CPUFreq GovernorCPUIdle Governor
ACPI 명세P0(최고) ~ Pn(최저)C0(활성) ~ C3(깊은 절전)
레지스터MSR_IA32_PERF_CTLMWAIT 힌트
스케줄러 연동schedutil (실행 중 조정)menu/TEO (유휴 시 선택)
P-state와 C-state의 협력: CPU가 활성 상태일 때는 CPUFreq(P-state)가 부하에 맞게 주파수를 조정하고, 유휴 상태에 진입하면 CPUIdle(C-state)이 더 깊은 절전 모드로 전환합니다. 두 서브시스템은 상호 독립적으로 동작하지만, 에너지 효율은 둘의 조합으로 결정됩니다.

Governor (정책)

Governor는 CPU 주파수를 언제, 어떻게 변경할지 결정하는 정책입니다. 커널은 struct cpufreq_governor로 거버너를 추상화하며, 각 거버너는 init(), start(), stop(), limits() 콜백을 구현합니다.

Governor 종류

Governor동작전력성능적합한 경우
performance항상 최고 주파수높음최고서버, HPC, 게임
powersave항상 최저 주파수낮음최저극한 절전 (비권장)
ondemand부하 95% 이상 시 최대중간높음데스크톱 (레거시)
conservative단계적 증가/감소낮음중간노트북
schedutil스케줄러 정보 활용최적높음최신 기본값 (권장)
userspace유저 공간 제어가변가변벤치마크, 디버깅

cpufreq_governor 구조체

/* include/linux/cpufreq.h */
struct cpufreq_governor {
    char    name[CPUFREQ_NAME_LEN];

    /* 거버너 생명주기 콜백 */
    int     (*init)(struct cpufreq_policy *policy);
    void    (*exit)(struct cpufreq_policy *policy);
    int     (*start)(struct cpufreq_policy *policy);
    void    (*stop)(struct cpufreq_policy *policy);
    void    (*limits)(struct cpufreq_policy *policy);

    /* sysfs 속성 */
    struct attribute_group   *attr_group_gov;

    /* 모듈 소유자 */
    struct module            *owner;

    /* 거버너 목록 연결 */
    struct list_head         governor_list;
};

거버너 생명주기

거버너는 정책(policy)에 연결될 때 다음 순서로 콜백이 호출됩니다:

  1. init() - 데이터 구조 초기화, sysfs 속성 생성
  2. start() - 스케줄러 콜백 등록, 주파수 조정 시작
  3. limits() - 정책 제한(min/max) 변경 시 호출
  4. stop() - 스케줄러 콜백 해제
  5. exit() - 데이터 구조 해제, sysfs 속성 제거

schedutil Governor (권장)

Linux 4.7+에서 도입된 스케줄러 기반 Governor입니다. 전통적인 거버너(ondemand, conservative)가 유휴 시간 비율로 부하를 추정하는 것과 달리, schedutil은 PELT(Per-Entity Load Tracking) 신호를 직접 사용합니다.

/* kernel/sched/cpufreq_schedutil.c */
static void sugov_update_single(struct update_util_data *hook, u64 time,
                                  unsigned int flags)
{
    struct sugov_cpu *sg_cpu = container_of(hook, struct sugov_cpu, update_util);
    unsigned long util, max;
    unsigned int next_f;

    /* 스케줄러에서 CPU 활용률 가져오기 */
    sugov_get_util(&util, &max);

    /* 목표 주파수 계산: freq = util / max * max_freq */
    next_f = get_next_freq(sg_cpu, util, max);

    /* 주파수 변경 */
    sugov_update_commit(sg_cpu, time, next_f);
}

Governor 선택 가이드

  • 최신 시스템 (2017+): schedutil (기본값, 최적 균형)
  • 고성능 서버: performance (지연 시간 최소화)
  • 레거시 데스크톱: ondemand (널리 테스트됨)
  • 배터리 우선: conservative (점진적 조정)

아키텍처

CPUFreq는 3계층 구조로 설계되었습니다. Governor가 주파수 결정 정책을, Core가 공통 인터페이스를, Driver가 하드웨어 제어를 담당합니다.

계층 구조

사용자 공간 sysfs cpupower power-profiles-daemon tuned Governor (정책 결정) schedutil ondemand conservative performance powersave PELT CPUFreq Core cpufreq_policy freq_table notifier chain sysfs 인터페이스 Driver (하드웨어 제어) intel_pstate amd-pstate acpi-cpufreq cppc_cpufreq 하드웨어 (MSR / ACPI / MMIO)

cpufreq_policy 구조체

cpufreq_policy는 동일한 클록/전압 도메인을 공유하는 CPU 집합을 나타냅니다. 하나의 정책 객체에 여러 CPU가 연결될 수 있으며, 이들은 동일한 주파수로 동작합니다.

/* include/linux/cpufreq.h */
struct cpufreq_policy {
    cpumask_var_t           cpus;          /* 온라인 상태인 CPU들 */
    cpumask_var_t           related_cpus;  /* 같은 주파수 도메인의 모든 CPU */
    cpumask_var_t           real_cpus;     /* 이 정책에 실제 연결된 CPU */

    unsigned int            shared_type;   /* CPUFREQ_SHARED_TYPE_* */
    unsigned int            cpu;           /* 정책의 대표 CPU */

    struct clk              *clk;          /* clock framework 연결 */

    /* 하드웨어 한계 */
    struct cpufreq_cpuinfo   cpuinfo;       /* min_freq, max_freq, transition_latency */

    /* 사용자/정책 한계 */
    unsigned int            min;           /* 최소 주파수 (kHz) */
    unsigned int            max;           /* 최대 주파수 (kHz) */
    unsigned int            cur;           /* 현재 주파수 (kHz) */

    /* 거버너 */
    struct cpufreq_governor  *governor;      /* 현재 governor */
    void                    *governor_data; /* 거버너 전용 데이터 */

    /* 주파수 테이블 */
    struct cpufreq_frequency_table *freq_table;
    unsigned int            suspend_freq;  /* suspend 시 주파수 */

    /* 전환 통계 */
    struct cpufreq_stats     *stats;
    struct cpufreq_stats     *last_stats;

    /* 동기화 */
    struct rw_semaphore      rwsem;         /* 정책 접근 보호 */

    /* fast_switch 지원 여부 */
    bool                    fast_switch_possible;
    bool                    fast_switch_enabled;

    /* 전환 지연 (나노초) */
    unsigned int            transition_delay_us;

    /* EMD (Energy Model) 통합 */
    struct em_perf_domain    *em_pd;
};

cpufreq_cpuinfo 구조체

/* include/linux/cpufreq.h */
struct cpufreq_cpuinfo {
    unsigned int    max_freq;            /* 하드웨어 최대 주파수 (kHz) */
    unsigned int    min_freq;            /* 하드웨어 최소 주파수 (kHz) */
    unsigned int    transition_latency;  /* P-state 전환 지연 (ns) */
};

cpufreq_driver 콜백 상세

CPUFreq 드라이버는 struct cpufreq_driver를 통해 하드웨어 인터페이스를 추상화합니다. 드라이버는 cpufreq_register_driver()로 등록되며, 코어는 이 구조체의 콜백을 통해 하드웨어를 제어합니다.

cpufreq_driver 구조체

/* include/linux/cpufreq.h */
struct cpufreq_driver {
    char            name[CPUFREQ_NAME_LEN];
    u16             flags;

    /* ---- 필수 콜백 ---- */
    int     (*init)(struct cpufreq_policy *policy);
    int     (*verify)(struct cpufreq_policy_data *policy_data);

    /* ---- 주파수 설정 (택 1) ---- */
    int     (*setpolicy)(struct cpufreq_policy *policy);
    int     (*target)(struct cpufreq_policy *policy,
                       unsigned int target_freq, unsigned int relation);
    int     (*target_index)(struct cpufreq_policy *policy,
                             unsigned int index);
    unsigned int (*fast_switch)(struct cpufreq_policy *policy,
                                unsigned int target_freq);

    /* ---- 선택 콜백 ---- */
    unsigned int (*get)(unsigned int cpu);
    unsigned int (*resolve_freq)(struct cpufreq_policy *policy,
                                  unsigned int target_freq);
    int     (*exit)(struct cpufreq_policy *policy);
    int     (*suspend)(struct cpufreq_policy *policy);
    int     (*resume)(struct cpufreq_policy *policy);
    void    (*ready)(struct cpufreq_policy *policy);
    int     (*set_boost)(struct cpufreq_policy *policy, int state);

    /* ---- 중간 주파수 전환 (PLL 재설정 시) ---- */
    unsigned int (*get_intermediate)(struct cpufreq_policy *policy,
                                     unsigned int index);
    int     (*target_intermediate)(struct cpufreq_policy *policy,
                                    unsigned int index);

    /* ---- sysfs 속성 ---- */
    struct freq_attr  **attr;
};

콜백 역할 상세

콜백필수컨텍스트역할
init필수프로세스정책 초기화: cpuinfo.min/max_freq, freq_table, transition_latency 설정
verify필수프로세스정책 제한값 유효성 검증 (cpufreq_verify_within_limits 헬퍼 사용)
target택1프로세스목표 주파수와 관계(RELATION_L/H/C) 지정하여 설정 (레거시)
target_index택1프로세스주파수 테이블의 인덱스로 직접 설정 (가장 일반적)
fast_switch택1인터럽트스케줄러 컨텍스트에서 직접 호출, 슬립 금지 (schedutil용)
setpolicy택1프로세스HWP 모드: min/max 범위만 전달, 하드웨어가 자율 결정
get선택프로세스현재 실제 동작 주파수 반환 (하드웨어에서 읽기)
resolve_freq선택프로세스목표 주파수를 실제 가능한 주파수로 변환 (실제 설정 없이)
exit선택프로세스정책 해제 시 정리 (CPU 핫플러그 오프라인)
suspend선택프로세스시스템 절전 진입 시 호출 (거버너 정지 후)
resume선택프로세스시스템 절전 복귀 시 호출 (거버너 시작 전)
set_boost선택프로세스Turbo/Boost 활성화/비활성화

fast_switch vs target 경로 비교

fast_switch vs target 호출 경로 fast_switch 경로 (인터럽트 컨텍스트) 스케줄러 tick / 태스크 깨움 sugov_update_single/shared() cpufreq_driver_fast_switch() MSR 직접 쓰기 (슬립 없음) 지연: 수 us 이내 target 경로 (프로세스 컨텍스트) 스케줄러 tick / 태스크 깨움 sugov_update_single/shared() irq_work 큐잉 kthread_worker 깨움 __cpufreq_driver_target() ACPI / 슬립 가능 I/O 지연: 수십 ~ 수백 us
비교 항목fast_switchtarget / target_index
호출 컨텍스트인터럽트 (스케줄러 tick)프로세스 (kthread)
슬립 가능불가 (IRQ disabled)가능
전환 지연수 us수십 ~ 수백 us
notifier 호출불가PRE/POST 호출
대표 드라이버intel_pstate (HWP), amd-pstate (MSR)acpi-cpufreq, cppc_cpufreq
schedutil 활용직접 호출 (최적 경로)irq_work → kthread 우회
fast_switch 활성화 확인: policy->fast_switch_possibletrue면 드라이버가 fast_switch를 지원합니다. schedutil은 이 플래그를 확인하고, 가능하면 fast_switch 경로를 우선 사용합니다. 최신 Intel(HWP)과 AMD(CPPC MSR) CPU는 모두 fast_switch를 지원합니다.

드라이버 플래그

/* include/linux/cpufreq.h - 주요 드라이버 플래그 */
#define CPUFREQ_STICKY               (1 << 0)  /* 언로드 불가 */
#define CPUFREQ_CONST_LOOPS          (1 << 1)  /* loops_per_jiffy 고정 */
#define CPUFREQ_HAVE_GOVERNOR_PER_POLICY (1 << 3)  /* 정책별 거버너 */
#define CPUFREQ_NEED_INITIAL_FREQ_CHECK  (1 << 5)  /* 초기 주파수 검증 */
#define CPUFREQ_IS_COOLING_DEV       (1 << 7)  /* cooling device 자동 등록 */
#define CPUFREQ_NEED_UPDATE_LIMITS   (1 << 8)  /* 한계값 변경 시 업데이트 */

schedutil 거버너 심화

schedutil은 스케줄러의 PELT(Per-Entity Load Tracking) 신호를 직접 활용하는 유일한 거버너입니다. 기존 거버너가 주기적으로 CPU 유휴 시간을 샘플링하는 것과 달리, 스케줄러 이벤트(태스크 깨움, 마이그레이션, tick)마다 실시간으로 주파수를 업데이트합니다.

PELT-schedutil 파이프라인

schedutil-PELT 파이프라인 PELT 신호 rq->cfs.avg util_avg / util_est 활용률 합산 u_cfs + u_rt + u_irq + u_dl UCLAMP clamp(u, min, max) 태스크별 제한 주파수 계산 f = 1.25 * u * f_max / max 드라이버 fast_switch 또는 target IO-wait Boost IO 완료로 깨어난 태스크 → util 최대치로 부스트 schedutil 주파수 계산 공식 u_cfs = max(running, util_est) [UTIL_EST 활성 시] u = clamp(u_cfs + u_rt, u_min, u_max) + u_irq + u_dl f_desired = min(f_max, 1.25 * u * f_max / max_capacity)

주파수 계산 공식 상세

/* kernel/sched/cpufreq_schedutil.c */
static unsigned int get_next_freq(struct sugov_policy *sg_policy,
                                   unsigned long util, unsigned long max)
{
    struct cpufreq_policy *policy = sg_policy->policy;
    unsigned int freq;

    /* 1.25 배율 적용: 100% 활용률 → 최대 주파수 도달 보장
     * C = 1.25 이므로 util이 max의 80%일 때 max_freq에 도달
     * 이는 스케줄러 활용률이 실제보다 약간 낮게 추적되는 것을 보상 */
    freq = map_util_freq(util, policy->cpuinfo.max_freq, max);

    /* resolve_freq가 있으면 실제 가능한 주파수로 변환 */
    if (policy->freq_table)
        freq = cpufreq_driver_resolve_freq(policy, freq);

    return freq;
}

/* map_util_freq: f = C * util / max * f_max */
static inline unsigned long map_util_freq(unsigned long util,
                                           unsigned long freq,
                                           unsigned long cap)
{
    return (freq + (freq >> 2)) * util / cap;  /* freq * 1.25 * util / cap */
}

IO-wait 부스트 메커니즘

IO 완료로 깨어난 태스크는 보통 짧은 CPU burst를 수행합니다. schedutil은 이 패턴을 인식하여 주파수를 즉시 부스트합니다:

/* IO-wait 부스트 로직 */
static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, u64 time,
                               unsigned int flags)
{
    /* SCHED_CPUFREQ_IOWAIT 플래그 확인 */
    if (flags & SCHED_CPUFREQ_IOWAIT) {
        if (sg_cpu->iowait_boost_pending)
            return;

        sg_cpu->iowait_boost_pending = true;

        /* 부스트 레벨 두 배로 증가 (최대 max_freq까지) */
        if (sg_cpu->iowait_boost) {
            sg_cpu->iowait_boost <<= 1;
            if (sg_cpu->iowait_boost > sg_cpu->iowait_boost_max)
                sg_cpu->iowait_boost = sg_cpu->iowait_boost_max;
        } else {
            sg_cpu->iowait_boost = sg_cpu->sg_policy->policy->min;
        }
    }
}
IO-wait 부스트 과다 문제: IO가 빈번한 워크로드(데이터베이스, NVMe 스토리지)에서 IO-wait 부스트가 과도하게 작동하여 불필요한 전력 소모가 발생할 수 있습니다. 이 경우 iowait_boost_enable 파라미터를 비활성화하거나, EAS(Energy Aware Scheduling)를 통해 에너지 모델 기반으로 보정할 수 있습니다.

sugov_update_shared vs sugov_update_single

비교 항목sugov_update_singlesugov_update_shared
적용 조건정책에 CPU가 1개정책에 CPU가 2개 이상
활용률 계산해당 CPU만 확인정책 내 모든 CPU 중 최대값
동기화불필요per-cpu 잠금 필요
주파수 결정단일 CPU 기반가장 부하 높은 CPU 기준
fast_switch직접 호출직접 호출 (최대 활용률 CPU에서)
/* 공유 정책에서의 업데이트 */
static void sugov_update_shared(struct update_util_data *hook,
                                u64 time, unsigned int flags)
{
    struct sugov_cpu *sg_cpu = container_of(hook, struct sugov_cpu, update_util);
    struct sugov_policy *sg_policy = sg_cpu->sg_policy;
    unsigned long util, max;
    unsigned int next_f;

    /* per-cpu 잠금으로 동시 접근 방지 */
    raw_spin_lock(&sg_policy->update_lock);

    /* 이 CPU의 활용률 저장 */
    sg_cpu->util = util;
    sg_cpu->max = max;
    sg_cpu->last_update = time;

    /* 정책 내 모든 CPU 중 최대 활용률 계산 */
    sugov_get_util(sg_policy);

    /* 최대 활용률 기반으로 주파수 결정 */
    next_f = get_next_freq(sg_policy, util, max);

    raw_spin_unlock(&sg_policy->update_lock);
}

Intel P-state 드라이버

Intel CPU (Sandy Bridge+)는 전용 intel_pstate 드라이버를 사용합니다. 이 드라이버는 두 가지 동작 모드를 가집니다: active mode(HWP)와 passive mode(SWP).

동작 모드

모드커널 파라미터거버너 사용주파수 결정적용 CPU
Active (HWP)intel_pstate=hwp (기본)내부 알고리즘하드웨어 자율Skylake+
Passive (SWP)intel_pstate=passivecpufreq 거버너드라이버 제어Sandy Bridge+
비활성intel_pstate=disableacpi-cpufreqACPI P-state모든 Intel

HWP (Hardware P-states)

Skylake+ CPU는 하드웨어가 자동으로 P-state를 선택합니다 (HWP). 소프트웨어는 IA32_HWP_REQUEST MSR을 통해 선호도(min, max, desired, EPP)만 전달하고, 실제 P-state 결정은 CPU 마이크로코드가 수행합니다.

HWP MSR 레지스터 레이아웃

IA32_HWP_REQUEST MSR (0x774) 비트 63:32 Reserved / Package 비트 31:24 EPP 비트 23:16 Desired Perf 비트 15:8 Maximum Perf 비트 7:0 Minimum Perf Energy Performance Preference 0=성능 우선, 255=절전 우선 원하는 성능 수준 0=자동(EPP 기반) 허용 최대 P-state 허용 최소 P-state
/* drivers/cpufreq/intel_pstate.c - HWP 요청 쓰기 */
static void intel_pstate_hwp_set(unsigned int cpu)
{
    struct cpudata *cpu_data = all_cpu_data[cpu];
    u64 value;

    value = (u64)cpu_data->epp << 24   |   /* EPP */
            (u64)cpu_data->desired << 16 |   /* Desired */
            (u64)cpu_data->max_perf << 8 |   /* Maximum */
            (u64)cpu_data->min_perf;          /* Minimum */

    wrmsrl_on_cpu(cpu, MSR_HWP_REQUEST, value);
}

EPP (Energy Performance Preference)

HWP 모드에서는 EPP로 성능/전력 균형을 제어합니다.

EPP 값의미주파수 경향전형적 사용처
0performance항상 높음HPC, 게임 서버
128balance_performance성능 우선 균형웹 서버, DB
192balance_power전력 우선 균형데스크톱 일반
255power항상 낮음배터리 절약
# EPP 설정 확인/변경
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference
# balance_performance

# 사용 가능한 EPP 목록
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences
# default performance balance_performance balance_power power

# 모든 CPU에 performance EPP 적용
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
    echo performance > "$cpu"
done

Intel P-state 전용 sysfs

# Intel P-state 드라이버 상태 확인
cat /sys/devices/system/cpu/intel_pstate/status
# active (HWP 모드) 또는 passive (SWP 모드)

# 최대/최소 성능 비율 (% 단위)
cat /sys/devices/system/cpu/intel_pstate/max_perf_pct
# 100
cat /sys/devices/system/cpu/intel_pstate/min_perf_pct
# 20

# Turbo 비활성화
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo

# HWP 동적 부스트 (Alder Lake+)
cat /sys/devices/system/cpu/intel_pstate/hwp_dynamic_boost
# 1 (활성)
Intel P-state active 모드의 특수성: active 모드(HWP)에서 intel_pstate는 cpufreq 거버너 계층을 우회합니다. sysfs에서 scaling_governor를 보면 performance 또는 powersave만 표시되지만, 실제 주파수 결정은 CPU 마이크로코드가 EPP와 min/max 힌트를 기반으로 자율적으로 수행합니다.

AMD P-state 드라이버

AMD Zen2+ CPU는 amd-pstate 드라이버를 사용하여 CPPC(Collaborative Processor Performance Control) 인터페이스를 통해 주파수를 제어합니다. 기존 acpi-cpufreq가 3단계 이산 P-state만 지원하는 것과 달리, CPPC는 연속적인 성능 범위를 제공합니다.

CPPC2 통신 경로

AMD CPPC2 통신 경로 OS (amd-pstate 드라이버) Desired Perf Min/Max Perf EPP (0-255) Autonomous Select MSR / MMIO ACPI PCC 프로세서 펌웨어 (SMU - System Management Unit) 열/전력 제약 확인 실제 주파수 결정 VID(전압) 계산 FID/DID 설정 Preferred Core 순위 코어 랭킹 업데이트 Boost 클록 관리 CPPC 성능 레벨 (읽기 전용) Highest Perf ........... 255 (부스트 포함) Nominal Perf ........... 166 (지속 가능 최대) Lowest Nonlinear ...... 80 (효율 임계점) Lowest Perf ............. 20 (절대 최소)

AMD P-state 동작 모드

모드커널 파라미터설명드라이버 역할펌웨어 역할
Activeamd_pstate=activeEPP 기반 자율 제어min/max/EPP만 설정자율 주파수 결정
Guidedamd_pstate=guided범위 기반 자율 제어min/max 범위 설정범위 내 자율 결정
Passiveamd_pstate=passive명시적 목표 제어desired_perf 직접 지정지정값 반영

CPPC 성능 수준

/* drivers/cpufreq/amd-pstate.h */
struct amd_cpudata {
    u32     highest_perf;     /* 부스트 포함 최고 성능 */
    u32     nominal_perf;     /* 지속 가능한 최고 성능 */
    u32     lowest_nonlinear_perf; /* 효율 임계점 */
    u32     lowest_perf;      /* 절대 최저 성능 */

    u32     max_freq;         /* nominal_perf에 대응하는 주파수 */
    u32     max_limit_freq;   /* 허용 최대 주파수 */
    u32     min_limit_freq;   /* 허용 최소 주파수 */
    u32     lowest_nonlinear_freq; /* 비선형 임계 주파수 */

    u32     epp_cached;       /* 캐시된 EPP 값 */
    bool    boost_supported;  /* 부스트 지원 여부 */
    bool    hw_prefcore;      /* HW Preferred Core 지원 */
};
# AMD P-state 드라이버 상태 확인
cat /sys/devices/system/cpu/amd_pstate/status
# active / guided / passive

# 성능 수준 확인 (정책별)
cat /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_highest_perf
# 166
cat /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_max_freq
# 4500000  (4.5 GHz)
cat /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_lowest_nonlinear_freq
# 1400000  (1.4 GHz)

# Preferred Core 확인
cat /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_hw_prefcore
# supported
cat /sys/devices/system/cpu/cpufreq/policy0/amd_pstate_prefcore_ranking
# 166  (코어 성능 순위)

# 동작 모드 런타임 전환 (6.5+ 커널)
echo guided > /sys/devices/system/cpu/amd_pstate/status
Preferred Core 활용: AMD Zen3+ CPU는 실리콘 공정 편차로 코어마다 도달 가능한 최대 주파수가 다릅니다. amd-pstate 드라이버는 CPPC highest_perf 레지스터를 통해 코어별 성능 순위를 스케줄러에 전달하여, 고성능 코어에 중요 태스크를 우선 배치합니다. amd_pstate_prefcore_ranking이 높은 코어일수록 더 높은 클록에 도달할 수 있습니다.

Turbo Boost / Precision Boost

Turbo는 열/전력 여유가 있을 때 일시적으로 최대 주파수를 초과하는 기능입니다.

Turbo 작동 원리

Turbo Boost 주의사항

  • 비결정적: 온도/전력에 따라 주파수 변동 → 벤치마크 시 비활성화 권장
  • 전체 코어: 모든 코어 100% 시 Turbo 미적용 또는 감소된 부스트 (TDP 제한)
  • 서버: 예측 가능성 위해 Turbo 비활성화하는 경우 많음
  • 열 스로틀링: Turbo 주파수에서 장시간 동작 시 열 압력(thermal pressure)으로 강제 감속
# Intel Turbo 비활성화
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo

# AMD Boost 비활성화
echo 0 > /sys/devices/system/cpu/cpufreq/boost

# 범용 (acpi-cpufreq 등)
echo 0 > /sys/devices/system/cpu/cpufreq/boost

열 압력 (Thermal Pressure) 연동

CPU 온도가 임계값을 초과하면 하드웨어 또는 펌웨어가 최대 주파수를 제한합니다. 커널은 이 정보를 thermal pressure로 스케줄러에 전달하여 부하 분산 결정에 반영합니다.

열 압력 피드백 루프

열 압력 피드백 루프 CPU 부하 증가 schedutil 주파수 상승 온도 상승 Tjunction 접근 HW 스로틀링 max_freq 강제 제한 thermal pressure 커널에 보고 arch_scale_thermal_pressure capacity 보정 스케줄러 결정 스로틀된 CPU 부하 분산 schedutil 보정 실효 capacity 감소 반영 thermal pressure 계산 thermal_pressure = max_capacity - capped_capacity effective_capacity = max_capacity - thermal_pressure
/* include/linux/sched/topology.h */
/* 열 압력을 스케줄러에 전달 */
void arch_set_thermal_pressure(const struct cpumask *cpus,
                               unsigned long th_pressure);

/* 스케줄러가 열 압력 조회 */
static inline unsigned long arch_scale_thermal_pressure(int cpu)
{
    return per_cpu(thermal_pressure, cpu);
}

/* cpufreq cooling device에서 호출 */
static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
                                unsigned long state)
{
    struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
    unsigned int clip_freq = cpufreq_cdev->freq_table[state].frequency;

    /* 최대 주파수 제한 */
    cpufreq_cdev->clipped_freq = clip_freq;
    cpufreq_update_policy(cpufreq_cdev->policy->cpu);

    /* thermal pressure 업데이트 */
    arch_set_thermal_pressure(cpufreq_cdev->policy->cpus,
                              max_capacity - clip_capacity);
    return 0;
}
열 스로틀링 감지: 성능 저하가 의심되면 dmesg | grep -i throttle로 열 스로틀링 로그를 확인하세요. Intel CPU는 turbostatPkgWattCorWatt 컬럼으로 전력 제한 여부를, perf stat -e msr/tsc/,msr/aperf/,msr/mperf/로 실제 vs 요청 주파수 비율을 확인할 수 있습니다.

에너지 모델 (Energy Model)

에너지 모델(EM, Energy Model)은 CPU의 주파수-전력 관계를 모델링하여 EAS(Energy Aware Scheduling)에 제공합니다. 스케줄러는 EM을 참조하여 태스크를 가장 에너지 효율적인 CPU에 배치합니다.

에너지 모델 곡선

에너지 모델: 주파수-전력-에너지 관계 주파수 (MHz) 전력 / 에너지 800 1200 1800 2400 3200 4000 전력 (W) 에너지/작업 최적 효율점 전력 = C * V^2 * F
/* 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[];        /* 이 도메인의 CPU들 */
};

/* 에너지 모델 등록 - cpufreq 드라이버가 호출 */
int em_dev_register_perf_domain(struct device *dev,
                               unsigned int nr_states,
                               struct em_data_callback *cb,
                               cpumask_var_t cpus,
                               bool microwatts);

EAS 연동

EAS(Energy Aware Scheduling)는 에너지 모델을 참조하여 태스크 배치 시 에너지 소비를 최소화합니다:

/* kernel/sched/fair.c - EAS 태스크 배치 */
static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
{
    unsigned long best_delta = ULONG_MAX;
    int best_energy_cpu = prev_cpu;

    /* 각 성능 도메인에 대해 */
    for_each_pd(pd, ...) {
        /* 현재 에너지 + 태스크 추가 시 에너지 계산 */
        unsigned long cur_energy = compute_energy(p, prev_cpu, pd);
        unsigned long new_energy = compute_energy(p, cpu, pd);
        unsigned long delta = new_energy - cur_energy;

        if (delta < best_delta) {
            best_delta = delta;
            best_energy_cpu = cpu;
        }
    }
    return best_energy_cpu;
}
EAS 활성화 조건: EAS는 비대칭 CPU 토폴로지(big.LITTLE, hybrid)에서 자동 활성화됩니다. 동종 CPU(서버급)에서는 에너지 도메인이 동일하므로 EAS 효과가 미미합니다. /proc/sys/kernel/sched_energy_aware가 1이면 EAS가 활성 상태입니다.

OPP (Operating Performance Points) 프레임워크

OPP 프레임워크는 주파수-전압 쌍을 관리하는 공통 인터페이스입니다. ARM SoC에서 주로 사용되며, Device Tree에서 OPP 테이블을 정의하고 cpufreq 드라이버가 이를 읽어 주파수 테이블을 구성합니다.

OPP 테이블에서 cpufreq 연동까지

OPP 테이블 → cpufreq 연동 Device Tree opp-table { opp-1800MHz { ... } opp-1200MHz { ... } } OPP 프레임워크 dev_pm_opp_of_add_table() struct dev_pm_opp cpufreq 드라이버 cpufreq_generic_init() freq_table 생성 에너지 모델 em_dev_register _perf_domain() 연동 흐름 1. DT에서 OPP 테이블 파싱 → 2. dev_pm_opp 객체 생성 → 3. cpufreq_frequency_table 변환 → 4. 에너지 모델 등록 (전력값 포함) → 5. EAS/schedutil이 에너지 최적 주파수 결정

Device Tree OPP 바인딩

/* Device Tree OPP 테이블 예제 (ARM SoC) */
cpu0_opp_table: opp-table {
    compatible = "operating-points-v2";
    opp-shared;

    opp-600000000 {
        opp-hz = /bits/ 64 <600000000>;     /* 600 MHz */
        opp-microvolt = <900000>;         /* 0.9V */
        opp-microamp = <300000>;          /* 300 mA */
        clock-latency-ns = <300000>;      /* 300 us */
    };

    opp-1200000000 {
        opp-hz = /bits/ 64 <1200000000>;    /* 1.2 GHz */
        opp-microvolt = <1000000>;        /* 1.0V */
        opp-microamp = <500000>;          /* 500 mA */
        clock-latency-ns = <300000>;
    };

    opp-1800000000 {
        opp-hz = /bits/ 64 <1800000000>;    /* 1.8 GHz */
        opp-microvolt = <1100000 1150000 1050000>; /* target, max, min */
        opp-microamp = <800000>;
        clock-latency-ns = <300000>;
        opp-suspend;                       /* suspend 시 이 OPP 사용 */
    };
};
/* OPP 프레임워크 API */
#include <linux/pm_opp.h>

/* DT에서 OPP 테이블 로드 */
int dev_pm_opp_of_add_table(struct device *dev);

/* 주어진 주파수 이상의 OPP 찾기 */
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
                                              unsigned long *freq);

/* OPP의 전압 조회 */
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);

/* OPP 수 조회 */
int dev_pm_opp_get_opp_count(struct device *dev);

/* cpufreq 드라이버에서 OPP → freq_table 변환 */
int dev_pm_opp_init_cpufreq_table(struct device *dev,
                                  struct cpufreq_frequency_table **table);

주파수 불변성 (Frequency Invariance)

스케줄러의 PELT 신호는 현재 CPU 주파수에 영향을 받습니다. 1GHz에서 50% 활용률과 2GHz에서 50% 활용률은 실제 처리량이 다르지만, 보정 없이는 동일하게 보입니다. 주파수 불변성(frequency invariance)은 이 차이를 보정합니다.

주파수 불변성 보정

주파수 불변성 보정 보정 전 (주파수 종속) CPU0: 1GHz, util=50% → 실제 처리량 낮음 CPU1: 3GHz, util=50% → 실제 처리량 높음 스케줄러: 둘 다 동일하게 취급 (오류) 보정 보정 후 (주파수 불변) CPU0: util=50% * (1G/3G) = 16.7% CPU1: util=50% * (3G/3G) = 50.0% 스케줄러: 정확한 비교 가능 보정 공식 freq_scale = arch_scale_freq_capacity(cpu) = cur_freq / max_freq * 1024 util_inv = util_raw * freq_scale / 1024 x86: AMU(Activity Monitor Unit) 카운터 기반, ARM: freq_scale 기반
/* arch/x86/kernel/smpboot.c - x86 주파수 불변성 */
static void x86_arch_scale_freq_tick(void)
{
    u64 aperf, mperf;
    unsigned long freq_scale;

    /* APERF: 실제 동작 사이클 수 */
    rdmsrl(MSR_IA32_APERF, aperf);
    /* MPERF: 최대 주파수 기준 사이클 수 */
    rdmsrl(MSR_IA32_MPERF, mperf);

    /* freq_scale = aperf / mperf * SCHED_CAPACITY_SCALE */
    freq_scale = div64_u64(aperf * SCHED_CAPACITY_SCALE, mperf);

    this_cpu_write(arch_freq_scale, freq_scale);
}

/* 스케줄러가 주파수 스케일 팩터 조회 */
unsigned long arch_scale_freq_capacity(int cpu)
{
    return per_cpu(arch_freq_scale, cpu);
}
아키텍처주파수 불변성 소스정확도업데이트 주기
x86 (Intel/AMD)APERF/MPERF MSR 카운터높음 (HW 측정)매 스케줄러 tick
ARM64 (AMU)Activity Monitor Unit 카운터높음 (HW 측정)매 스케줄러 tick
ARM64 (비AMU)cpufreq 드라이버 피드백중간 (SW 추적)주파수 전환 시
ARM32cpufreq 드라이버 피드백중간 (SW 추적)주파수 전환 시

거버너 비교 분석

각 거버너의 내부 동작 원리와 파라미터를 비교 분석합니다.

거버너 결정 트리

거버너 선택 결정 트리 커널 4.7+ 인가? 아니오 최대 성능 필수인가? performance 아니오 HWP/CPPC 활성? schedutil + HWP 아니오 fast_switch 지원? schedutil (fast) 아니오 schedutil (target) 응답성 중요? ondemand 아니오 conservative Intel P-state active(HWP) 모드에서는 거버너 선택이 무의미 (performance/powersave만 가능)

거버너별 파라미터 비교

파라미터ondemandconservativeschedutil
부하 추적 방식유휴 시간 비율유휴 시간 비율PELT (스케줄러 직접)
주파수 상승 조건부하 > up_threshold부하 > up_threshold활용률 비례 계산
주파수 하강 조건부하 < down_differential부하 < down_threshold활용률 비례 계산
상승 패턴즉시 최대freq_step% 단계적비례 (1.25x)
하강 패턴비례 감소freq_step% 단계적비례 (1.25x)
샘플링 주기sampling_rate (us)sampling_rate (us)스케줄러 이벤트 기반
IO-wait 부스트io_is_busy없음iowait_boost
fast_switch미지원미지원지원

ondemand / conservative 파라미터 상세

# ========== ondemand 파라미터 ==========
ls /sys/devices/system/cpu/cpufreq/ondemand/

# 부하 임계치 (기본 95%): 이 이상이면 즉시 최대 주파수
echo 80 > /sys/devices/system/cpu/cpufreq/ondemand/up_threshold

# 샘플링 간격 (마이크로초): 부하 확인 주기
echo 10000 > /sys/devices/system/cpu/cpufreq/ondemand/sampling_rate

# IO 부하 반영: 1이면 IO-wait도 CPU 부하에 포함
echo 1 > /sys/devices/system/cpu/cpufreq/ondemand/io_is_busy

# 주파수 하강 차등: up_threshold - sampling_down_factor
echo 10 > /sys/devices/system/cpu/cpufreq/ondemand/sampling_down_factor

# ========== conservative 파라미터 ==========
ls /sys/devices/system/cpu/cpufreq/conservative/

# 상승 임계치
echo 80 > /sys/devices/system/cpu/cpufreq/conservative/up_threshold

# 하강 임계치
echo 20 > /sys/devices/system/cpu/cpufreq/conservative/down_threshold

# 주파수 변경 단계 (% 단위, 기본 5%)
echo 5 > /sys/devices/system/cpu/cpufreq/conservative/freq_step

cpufreq sysfs 인터페이스 완전 가이드

cpufreq의 모든 제어와 모니터링은 /sys/devices/system/cpu/ 아래의 sysfs 인터페이스를 통해 이루어집니다.

정책별 sysfs 속성

경로: /sys/devices/system/cpu/cpufreq/policyX/ (X는 정책 번호)

속성읽기/쓰기설명
affected_cpusR이 정책의 영향을 받는 온라인 CPU 목록
related_cpusR같은 주파수 도메인의 모든 CPU (오프라인 포함)
cpuinfo_min_freqR하드웨어 지원 최소 주파수 (kHz)
cpuinfo_max_freqR하드웨어 지원 최대 주파수 (kHz)
cpuinfo_transition_latencyRP-state 전환 지연 시간 (ns)
scaling_driverR사용 중인 드라이버 이름
scaling_governorR/W현재 거버너 (변경 가능)
scaling_available_governorsR사용 가능한 거버너 목록
scaling_min_freqR/W사용자 설정 최소 주파수 제한
scaling_max_freqR/W사용자 설정 최대 주파수 제한
scaling_cur_freqR마지막으로 요청된 주파수
scaling_available_frequenciesR지원 주파수 목록 (이산 드라이버만)
scaling_setspeedR/Wuserspace 거버너에서 주파수 직접 설정
cpuinfo_cur_freqR하드웨어에서 읽은 실제 현재 주파수
energy_performance_preferenceR/WEPP 설정 (HWP/CPPC 드라이버)
energy_performance_available_preferencesR사용 가능한 EPP 목록

전역 sysfs 속성

# 전역 속성 (모든 정책 공통)
cat /sys/devices/system/cpu/cpufreq/boost
# 1 (Turbo/Boost 활성)

# Intel P-state 전용
ls /sys/devices/system/cpu/intel_pstate/
# max_perf_pct  min_perf_pct  no_turbo  status  hwp_dynamic_boost

# AMD P-state 전용
ls /sys/devices/system/cpu/amd_pstate/
# status  prefcore

# 전환 통계
cat /sys/devices/system/cpu/cpufreq/policy0/stats/total_trans
# 15432  (총 전환 횟수)
cat /sys/devices/system/cpu/cpufreq/policy0/stats/time_in_state
# 800000 5234     (800MHz에서 5234 jiffies)
# 1200000 3421    (1.2GHz에서 3421 jiffies)
# ...
scaling_cur_freq vs cpuinfo_cur_freq: scaling_cur_freq는 드라이버가 마지막으로 요청한 주파수이고, cpuinfo_cur_freq는 하드웨어에서 실제로 읽은 주파수입니다. HWP 모드에서는 하드웨어가 자율적으로 주파수를 결정하므로 두 값이 다를 수 있습니다. 정확한 주파수를 알려면 cpuinfo_cur_freq를 확인하세요.

cpufreq 드라이버 작성 가이드

새로운 SoC나 플랫폼을 위한 cpufreq 드라이버를 작성하는 단계별 가이드입니다.

최소 드라이버 예제

#include <linux/cpufreq.h>
#include <linux/module.h>
#include <linux/pm_opp.h>
#include <linux/clk.h>

#define DRIVER_NAME   "my_soc_cpufreq"

/* ---- 1단계: 정책 초기화 ---- */
static int my_cpufreq_init(struct cpufreq_policy *policy)
{
    struct device *cpu_dev;
    struct cpufreq_frequency_table *freq_table;
    int ret;

    cpu_dev = get_cpu_device(policy->cpu);
    if (!cpu_dev)
        return -ENODEV;

    /* OPP 테이블에서 주파수 목록 로드 */
    ret = dev_pm_opp_of_add_table(cpu_dev);
    if (ret)
        return ret;

    /* OPP → cpufreq 주파수 테이블 변환 */
    ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
    if (ret)
        goto err_opp;

    /* 정책에 주파수 테이블 설정 */
    ret = cpufreq_table_validate_and_sort(policy);
    policy->freq_table = freq_table;

    /* clock framework 연결 */
    policy->clk = clk_get(cpu_dev, NULL);

    /* 전환 지연 설정 (나노초) */
    policy->cpuinfo.transition_latency = 300000; /* 300 us */

    /* 현재 주파수 설정 */
    policy->cur = clk_get_rate(policy->clk) / 1000;

    return 0;

err_opp:
    dev_pm_opp_of_remove_table(cpu_dev);
    return ret;
}

/* ---- 2단계: 정책 유효성 검증 ---- */
static int my_cpufreq_verify(struct cpufreq_policy_data *policy)
{
    return cpufreq_generic_frequency_table_verify(policy);
}

/* ---- 3단계: 주파수 설정 (target_index) ---- */
static int my_cpufreq_target_index(struct cpufreq_policy *policy,
                                    unsigned int index)
{
    struct cpufreq_frequency_table *freq_table = policy->freq_table;
    unsigned int new_freq = freq_table[index].frequency;
    struct device *cpu_dev = get_cpu_device(policy->cpu);
    struct dev_pm_opp *opp;
    unsigned long volt, freq_hz = new_freq * 1000;
    int ret;

    /* 목표 주파수의 OPP 찾기 */
    opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
    if (IS_ERR(opp))
        return PTR_ERR(opp);

    volt = dev_pm_opp_get_voltage(opp);
    dev_pm_opp_put(opp);

    /* 전압 설정 (레귤레이터) → 주파수 설정 (클록) */
    ret = regulator_set_voltage(cpu_reg, volt, volt);
    if (ret)
        return ret;

    ret = clk_set_rate(policy->clk, freq_hz);
    return ret;
}

/* ---- 4단계: 현재 주파수 조회 ---- */
static unsigned int my_cpufreq_get(unsigned int cpu)
{
    struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
    return clk_get_rate(policy->clk) / 1000;
}

/* ---- 5단계: 정리 ---- */
static int my_cpufreq_exit(struct cpufreq_policy *policy)
{
    clk_put(policy->clk);
    dev_pm_opp_of_remove_table(get_cpu_device(policy->cpu));
    return 0;
}

/* ---- 6단계: 드라이버 구조체 정의 ---- */
static struct cpufreq_driver my_cpufreq_driver = {
    .name           = DRIVER_NAME,
    .flags          = CPUFREQ_NEED_INITIAL_FREQ_CHECK |
                      CPUFREQ_IS_COOLING_DEV,
    .init           = my_cpufreq_init,
    .exit           = my_cpufreq_exit,
    .verify         = my_cpufreq_verify,
    .target_index   = my_cpufreq_target_index,
    .get            = my_cpufreq_get,
    .attr           = cpufreq_generic_attr,
    .suspend        = cpufreq_generic_suspend,
};

/* ---- 7단계: 모듈 등록 ---- */
static int __init my_cpufreq_driver_init(void)
{
    return cpufreq_register_driver(&my_cpufreq_driver);
}

static void __exit my_cpufreq_driver_exit(void)
{
    cpufreq_unregister_driver(&my_cpufreq_driver);
}

module_init(my_cpufreq_driver_init);
module_exit(my_cpufreq_driver_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Example CPUFreq driver");

드라이버 작성 체크리스트

단계필수 항목관련 함수/매크로
1주파수 테이블 구성 (OPP 또는 수동)dev_pm_opp_of_add_table()
2init 콜백에서 policy 초기화cpuinfo.min/max_freq, cur, clk
3verify 콜백 구현cpufreq_generic_frequency_table_verify()
4target/target_index/fast_switch 중 택1슬립 가능 여부에 따라 선택
5get 콜백으로 실제 주파수 조회clk_get_rate() 또는 MSR 읽기
6exit 콜백으로 정리OPP 테이블 해제, clock put
7에너지 모델 등록 (EAS 지원 시)em_dev_register_perf_domain()
8cooling device 등록 (열 관리)CPUFREQ_IS_COOLING_DEV 플래그
fast_switch 지원 시 주의: fast_switch 콜백은 인터럽트 컨텍스트에서 호출되므로 슬립할 수 없습니다. mutex, kmalloc(GFP_KERNEL), usleep_range 등을 사용할 수 없습니다. MSR 쓰기나 MMIO 같은 비차단 연산만 가능합니다. 지원하려면 init에서 policy->fast_switch_possible = true를 설정하세요.

모니터링

CPU 주파수 상태를 확인하는 다양한 방법입니다.

/sys/devices/system/cpu/cpu*/cpufreq

# 현재 주파수
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
# 2400000  (2.4GHz)

# 지원 주파수 목록
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
# 800000 1200000 1600000 2000000 2400000 3000000

# Governor
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# schedutil

# 지원 Governor 목록
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
# conservative ondemand schedutil performance powersave

cpupower 유틸리티

# 주파수 정보 확인
cpupower frequency-info
# CPU 0: 800 MHz - 3.0 GHz
# current CPU frequency: 1.5 GHz (asserted by call to hardware)

# Governor 변경
cpupower frequency-set -g performance

# 주파수 범위 제한
cpupower frequency-set -d 1.2GHz -u 2.4GHz

# 모든 CPU 정보 요약
cpupower -c all frequency-info -m

실시간 모니터링

# watch로 실시간 확인
watch -n1 'grep MHz /proc/cpuinfo'

# turbostat (가장 상세)
turbostat --interval 1
# Core  CPU  Avg_MHz  Busy%  Bzy_MHz  TSC_MHz  IRQ  PkgWatt  CorWatt
#    0    0     1200   40.00     3000     3000  1234   45.2     32.1

# perf로 주파수 전환 추적
perf stat -e power/energy-cores/,power/energy-pkg/ -a sleep 5

# ftrace로 cpufreq 이벤트 추적
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
cat /sys/kernel/debug/tracing/trace
# <idle>-0  [001]  123.456: cpu_frequency: state=2400000 cpu_id=1

cpufreq 트레이스포인트

# 사용 가능한 cpufreq 트레이스포인트
ls /sys/kernel/debug/tracing/events/power/
# cpu_frequency        (주파수 변경)
# cpu_frequency_limits (주파수 제한 변경)
# pstate_sample        (intel_pstate 샘플링)

# 주파수 변경 이벤트 실시간 모니터링
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
cat /sys/kernel/debug/tracing/trace_pipe
# sugov:0-1234 [000] 456.789: cpu_frequency: state=3600000 cpu_id=0
# sugov:0-1234 [000] 457.012: cpu_frequency: state=800000 cpu_id=0

성능 튜닝

워크로드별 최적 설정입니다.

워크로드별 권장 설정

워크로드GovernorTurboEPP이유
데스크톱schedutilONbalance_performance응답성 + 절전
노트북schedutilONbalance_power배터리 수명
웹 서버performanceOFFperformance예측 가능한 지연
HPC/렌더링performanceONperformance최대 성능
실시간 (PREEMPT_RT)performanceOFFperformance결정적 동작
배치 처리schedutilONbalance_performance효율성
데이터베이스performanceONbalance_performance지연 시간 최소
가상화 호스트performanceONperformance게스트 성능 보장
임베디드/IoTconservativeOFFpower전력 절약 극대화

서버 최적화 예제

# ========== 웹 서버 - 낮은 지연 우선 ==========
cpupower frequency-set -g performance
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
    echo performance > "$cpu"
done

# ========== DB 서버 - 예측 가능성 + 성능 ==========
cpupower frequency-set -g performance
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
    echo balance_performance > "$cpu"
done

# ========== 개발 서버 - 균형 ==========
cpupower frequency-set -g schedutil

# ========== Kubernetes 노드 - 안정적 성능 ==========
cpupower frequency-set -g performance
# cgroup v2 cpu.max로 컨테이너별 CPU 시간 제한
echo "100000 100000" > /sys/fs/cgroup/my-pod/cpu.max

Governor 파라미터 튜닝

# ondemand governor 파라미터
echo 50 > /sys/devices/system/cpu/cpufreq/ondemand/up_threshold
# 50% 부하에서 주파수 상승 (기본값: 95)

echo 10000 > /sys/devices/system/cpu/cpufreq/ondemand/sampling_rate
# 10ms마다 부하 확인 (기본값: 가변)

# conservative governor 파라미터
echo 5 > /sys/devices/system/cpu/cpufreq/conservative/freq_step
# 5% 단계로 주파수 변경 (기본값: 5)

자동화 스크립트

#!/bin/bash
# cpufreq-setup.sh - 서버 최적화 자동 설정 스크립트

set -euo pipefail

PROFILE="${1:-balanced}"
DRIVER=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver)

echo "드라이버: $DRIVER, 프로필: $PROFILE"

case "$PROFILE" in
    performance)
        cpupower frequency-set -g performance
        if [[ "$DRIVER" == "intel_pstate" ]]; then
            echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
            for f in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
                echo performance > "$f"
            done
        fi
        ;;
    balanced)
        cpupower frequency-set -g schedutil
        if [[ "$DRIVER" == "intel_pstate" ]]; then
            for f in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
                echo balance_performance > "$f"
            done
        fi
        ;;
    powersave)
        cpupower frequency-set -g schedutil
        if [[ "$DRIVER" == "intel_pstate" ]]; then
            echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
            for f in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
                echo power > "$f"
            done
        fi
        ;;
esac

echo "설정 완료. 현재 주파수:"
cpupower -c all frequency-info -f

Intel vs AMD 상세 비교

항목Intel (intel_pstate)AMD (amd-pstate)
드라이버intel_pstate (전용)amd-pstate (Zen2+) 또는 acpi-cpufreq
HW 인터페이스MSR (IA32_HWP_REQUEST)MSR (CPPC) 또는 ACPI PCC
자율 모드HWP (active mode)EPP autonomous (active mode)
소프트웨어 제어passive mode (SWP)passive mode / guided mode
성능 범위이산 P-state (테이블 기반)연속 범위 (CPPC 단위 없는 값)
Turbo 이름Turbo Boost 2.0/3.0Precision Boost 2/Overdrive
EPP 범위0-255 (4개 프리셋)0-255 (4개 프리셋)
코어 우선순위Turbo Boost Max 3.0Preferred Core (CPPC)
fast_switchHWP: MSR 직접 쓰기MSR 지원 시 가능 (Zen3+)
에너지 모델미사용 (HWP 자율)passive/guided 모드에서 사용 가능
최소 CPUSandy Bridge (2세대)Zen2 (Ryzen 3000)
# 드라이버 확인
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
# intel_pstate  (Intel CPU)
# amd-pstate    (AMD Zen2+ CPU)
# acpi-cpufreq  (레거시)

# Intel: HWP 활성 확인
cat /sys/devices/system/cpu/intel_pstate/status
# active (HWP 사용 중)

# AMD: CPPC 모드 확인
cat /sys/devices/system/cpu/amd_pstate/status
# active / guided / passive

# 두 드라이버 모두: CPPC/HWP 지원 여부 확인
grep -E 'hwp|cppc' /proc/cpuinfo
# flags: ... hwp hwp_notify hwp_act_window hwp_epp ...

문제 해결

일반적인 CPUFreq 관련 문제와 해결 방법입니다.

주파수가 변경되지 않음

# 1. BIOS에서 SpeedStep/Cool'n'Quiet 활성화 확인

# 2. Governor가 userspace가 아닌지 확인
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# performance로 변경

# 3. 드라이버 로드 확인
lsmod | grep -E 'pstate|cpufreq'

# 4. 커널 파라미터 확인
cat /proc/cmdline | grep intel_pstate
# intel_pstate=disable이 없어야 함

# 5. 주파수 제한 확인
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
# min == max이면 주파수 고정 상태

# 6. CPU가 오프라인인지 확인
cat /sys/devices/system/cpu/online

성능 저하

# 현재 주파수 확인
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq

# Thermal throttling 확인
dmesg | grep -i -E 'throttle|thermal|temperature'

# Intel: PROCHOT 외부 제한 확인
turbostat --interval 1 | grep -E 'PROCHOT|PkgWatt'

# 전력 제한 확인
cat /sys/class/powercap/intel-rapl/intel-rapl:0/constraint_0_power_limit_uw

# Governor를 performance로 강제
cpupower frequency-set -g performance

# 열 압력 확인
cat /sys/devices/system/cpu/cpu*/topology/thermal_pressure

자주 발생하는 문제

증상원인해결
주파수가 항상 최저powersave 거버너 또는 EPP=power거버너/EPP 변경
주파수가 올라갔다 내려감열 스로틀링냉각 확인, Turbo 비활성화
scaling_driver가 acpi-cpufreqintel_pstate 비활성화됨커널 파라미터 확인
boost 파일이 없음HW 부스트 미지원 또는 드라이버 미지원CPU/BIOS 확인
EPP 변경 불가HWP 모드가 아님intel_pstate status 확인
amd-pstate 로드 안됨커널 파라미터 미설정 (6.3 이전)amd_pstate=active 추가
주파수 전환이 느림ACPI 기반 드라이버 (fast_switch 미지원)MSR 기반 드라이버 사용
운영 환경 변경 주의: 운영 중인 서버에서 거버너나 주파수 제한을 변경하면 일시적 성능 변동이 발생할 수 있습니다. 변경 전 turbostat으로 현재 상태를 기록하고, 변경 후 충분한 모니터링 시간을 확보하세요. 특히 DB 서버에서 주파수 전환 지연이 쿼리 레이턴시에 영향을 줄 수 있습니다.

커널 설정 옵션

CPUFreq 관련 주요 커널 설정(CONFIG_) 옵션입니다.

주요 CONFIG 옵션

옵션설명기본값
CONFIG_CPU_FREQCPUFreq 서브시스템 활성화Y
CONFIG_CPU_FREQ_STAT주파수 전환 통계Y
CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL기본 거버너를 schedutil로 설정Y (6.x)
CONFIG_CPU_FREQ_GOV_PERFORMANCEperformance 거버너Y
CONFIG_CPU_FREQ_GOV_POWERSAVEpowersave 거버너Y
CONFIG_CPU_FREQ_GOV_ONDEMANDondemand 거버너M/Y
CONFIG_CPU_FREQ_GOV_CONSERVATIVEconservative 거버너M
CONFIG_CPU_FREQ_GOV_SCHEDUTILschedutil 거버너Y
CONFIG_X86_INTEL_PSTATEIntel P-state 드라이버Y
CONFIG_X86_AMD_PSTATEAMD P-state 드라이버Y
CONFIG_X86_ACPI_CPUFREQACPI 기반 cpufreq 드라이버M
CONFIG_ENERGY_MODEL에너지 모델 프레임워크Y
CONFIG_PM_OPPOPP 프레임워크Y (ARM)
# 현재 커널의 CPUFreq 관련 설정 확인
zcat /proc/config.gz | grep -i cpu_freq
# 또는
grep -i cpu_freq /boot/config-$(uname -r)

# 로드된 cpufreq 모듈 확인
lsmod | grep -i cpufreq
# cpufreq_conservative    16384  0
# cpufreq_ondemand        16384  0

커널 커맨드라인 파라미터

파라미터설명예시
intel_pstate=disableintel_pstate 비활성화 (acpi-cpufreq 사용)레거시 호환
intel_pstate=passiveintel_pstate를 passive 모드로 (cpufreq 거버너 사용)schedutil 직접 사용
intel_pstate=hwp_onlyHWP 미지원 시 로드 안함HWP 필수 환경
intel_pstate=no_hwpHWP 비활성화 (SWP 모드 강제)HWP 문제 디버깅
amd_pstate=activeAMD P-state active 모드EPP 기반 자율 제어
amd_pstate=guidedAMD P-state guided 모드범위 기반 자율 제어
amd_pstate=passiveAMD P-state passive 모드명시적 목표 제어
amd_pstate.shared_mem=1ACPI 공유 메모리 방식 사용MSR 미지원 CPU
cpufreq.default_governor=기본 거버너 오버라이드=performance

주파수 전환 메커니즘

CPU 주파수가 변경될 때 커널은 notifier chain을 통해 관련 서브시스템에 통보합니다. 이 메커니즘은 loops_per_jiffy 재계산, 타이머 보정 등에 필수적입니다.

전환 Notifier

/* include/linux/cpufreq.h */
#define CPUFREQ_PRECHANGE    (0)  /* 주파수 변경 전 */
#define CPUFREQ_POSTCHANGE   (1)  /* 주파수 변경 후 */

struct cpufreq_freqs {
    struct cpufreq_policy *policy;
    unsigned int          old;    /* 이전 주파수 (kHz) */
    unsigned int          new;    /* 새 주파수 (kHz) */
    u8                    flags;  /* CPUFREQ_CONST_LOOPS 등 */
};

/* notifier 등록 */
int cpufreq_register_notifier(struct notifier_block *nb,
                             unsigned int list);

/* notifier 해제 */
int cpufreq_unregister_notifier(struct notifier_block *nb,
                               unsigned int list);

전환 흐름 상세

/* drivers/cpufreq/cpufreq.c - 주파수 전환 전체 흐름 */
int __cpufreq_driver_target(struct cpufreq_policy *policy,
                           unsigned int target_freq,
                           unsigned int relation)
{
    unsigned int old_freq = policy->cur;
    int retval;

    /* 1단계: 주파수 해석 (resolve) */
    unsigned int resolved = cpufreq_driver_resolve_freq(policy, target_freq);

    /* 2단계: PRECHANGE 통지 */
    struct cpufreq_freqs freqs = {
        .policy = policy,
        .old = old_freq,
        .new = resolved,
    };
    cpufreq_freq_transition_begin(policy, &freqs);

    /* 3단계: 실제 하드웨어 주파수 변경 */
    retval = cpufreq_driver->target_index(policy, index);

    /* 4단계: POSTCHANGE 통지 */
    cpufreq_freq_transition_end(policy, &freqs, retval);

    /* 5단계: 정책의 현재 주파수 업데이트 */
    if (!retval)
        policy->cur = resolved;

    return retval;
}

Notifier 등록 예제

/* 주파수 변경 이벤트를 감지하는 모듈 예제 */
static int my_cpufreq_callback(struct notifier_block *nb,
                               unsigned long val, void *data)
{
    struct cpufreq_freqs *freq = data;

    switch (val) {
    case CPUFREQ_PRECHANGE:
        pr_info("CPU%d: %u kHz -> %u kHz (시작)\n",
                freq->policy->cpu, freq->old, freq->new);
        break;
    case CPUFREQ_POSTCHANGE:
        pr_info("CPU%d: %u kHz -> %u kHz (완료)\n",
                freq->policy->cpu, freq->old, freq->new);
        break;
    }
    return NOTIFY_OK;
}

static struct notifier_block my_cpufreq_nb = {
    .notifier_call = my_cpufreq_callback,
};

/* 모듈 초기화 시 등록 */
cpufreq_register_notifier(&my_cpufreq_nb, CPUFREQ_TRANSITION_NOTIFIER);
fast_switch와 notifier: fast_switch 경로에서는 PRECHANGE/POSTCHANGE notifier가 호출되지 않습니다. 이는 fast_switch가 인터럽트 컨텍스트에서 동작하여 notifier chain을 안전하게 순회할 수 없기 때문입니다. 따라서 주파수 변경에 의존하는 서브시스템은 fast_switch 환경에서 대체 메커니즘이 필요합니다.

UCLAMP (Utilization Clamping) 연동

UCLAMP은 태스크별로 활용률의 최소/최대값을 제한하는 메커니즘입니다. schedutil 거버너는 UCLAMP 값을 주파수 결정에 직접 반영합니다.

UCLAMP 개념

/* include/linux/sched.h - 태스크별 UCLAMP */
struct task_struct {
    /* ... */
    struct uclamp_se    uclamp_req[UCLAMP_CNT]; /* 요청값 */
    struct uclamp_se    uclamp[UCLAMP_CNT];     /* 유효값 */
};

/* UCLAMP 종류 */
enum uclamp_id {
    UCLAMP_MIN = 0,  /* 최소 활용률 보장 (성능 보장) */
    UCLAMP_MAX = 1,  /* 최대 활용률 제한 (전력 절약) */
    UCLAMP_CNT,
};

UCLAMP이 schedutil에 미치는 영향

UCLAMP 설정schedutil 주파수 결정사용 시나리오
UCLAMP_MIN=512활용률이 낮아도 최소 50% 주파수 보장지연 시간이 중요한 태스크
UCLAMP_MAX=256활용률이 높아도 25% 주파수로 제한백그라운드 배치 작업
MIN=0, MAX=1024제한 없음 (기본값)일반 태스크
MIN=768, MAX=1024최소 75% 주파수 보장, 최대 제한 없음실시간 오디오/비디오
# UCLAMP 설정 (cgroup v2)
echo 512 > /sys/fs/cgroup/my-task/cpu.uclamp.min
# 최소 활용률 50% 보장 → 최소 50% 주파수

echo 256 > /sys/fs/cgroup/my-task/cpu.uclamp.max
# 최대 활용률 25% 제한 → 최대 25% 주파수

# sched_attr로 태스크별 설정 (프로그래밍 방식)
# struct sched_attr attr = {
#     .sched_util_min = 512,  // UCLAMP_MIN
#     .sched_util_max = 1024, // UCLAMP_MAX
# };
# sched_setattr(pid, &attr, 0);
UCLAMP으로 성능 보장: 데이터베이스의 쿼리 처리 스레드에 UCLAMP_MIN=768을 설정하면, CPU 활용률이 낮은 순간에도 높은 주파수가 유지되어 쿼리 레이턴시가 안정됩니다. 반대로, 로그 압축 같은 배치 태스크에 UCLAMP_MAX=256을 설정하면 전력 소비를 줄이면서 전체 시스템 열 부담을 감소시킬 수 있습니다.

cpufreq와 CPU 핫플러그

CPU가 온라인/오프라인될 때 cpufreq 정책이 어떻게 관리되는지 설명합니다.

핫플러그 시 cpufreq 흐름

/* CPU 온라인 시 */
cpufreq_online(cpu)
  → cpufreq_policy_alloc()        /* 정책 객체 할당 (없으면) */
  → cpufreq_driver->init(policy)   /* 드라이버 초기화 */cpufreq_init_policy(policy)    /* 거버너 연결 */cpufreq_start_governor(policy)  /* 거버너 시작 */

/* CPU 오프라인 시 */
cpufreq_offline(cpu)
  → cpufreq_stop_governor(policy)   /* 거버너 정지 */cpufreq_exit_governor(policy)   /* 거버너 해제 (마지막 CPU) */
  → cpufreq_driver->exit(policy)    /* 드라이버 정리 (마지막 CPU) */cpufreq_policy_free(policy)     /* 정책 해제 (마지막 CPU) */
상황정책 동작거버너 동작
정책의 첫 CPU 온라인정책 생성, freq_table 초기화init → start
정책에 CPU 추가cpus 마스크에 CPU 추가변경 없음
정책에서 CPU 제거 (마지막 아님)cpus 마스크에서 CPU 제거변경 없음
정책의 마지막 CPU 오프라인정책 해제stop → exit
정책의 대표 CPU 오프라인대표 CPU 변경stop → start (새 대표 CPU)
# CPU 오프라인 (cpufreq 정책에서 제거)
echo 0 > /sys/devices/system/cpu/cpu3/online

# CPU 온라인 (cpufreq 정책에 추가)
echo 1 > /sys/devices/system/cpu/cpu3/online

# 정책의 CPU 확인
cat /sys/devices/system/cpu/cpufreq/policy0/affected_cpus
# 0 1 2 3  (CPU3 오프라인 시: 0 1 2)

# 온라인 CPU 확인
cat /sys/devices/system/cpu/online
# 0-7

cpufreq 성능 분석 심화

CPU 주파수 스케일링이 실제 워크로드 성능에 미치는 영향을 분석하는 고급 기법들입니다.

핵심 성능 지표

지표측정 방법의미
IPC (Instructions Per Cycle)perf stat주파수 대비 실제 처리 효율
주파수 체류 시간time_in_state각 주파수에서 보낸 시간 비율
전환 횟수total_trans주파수 변경 빈도 (높으면 오버헤드)
C0 잔류 비율turbostatCPU가 활성 상태인 시간 비율
APERF/MPERF 비율MSR 읽기실제 동작 주파수 / 기준 주파수
에너지 소비RAPL (perf stat)총 에너지 소비량 (Joules)
EDP (Energy-Delay Product)계산에너지 효율의 종합 지표

성능 측정 실전

# ========== 1. IPC와 주파수 관계 측정 ==========
perf stat -e cycles,instructions,cache-misses,power/energy-cores/ \
    -a --interval-print 1000 -- sleep 10
# 시간별 IPC 변화와 에너지 소비 관찰

# ========== 2. 주파수 체류 시간 분석 ==========
# 변경 전 스냅샷
cat /sys/devices/system/cpu/cpufreq/policy0/stats/time_in_state > /tmp/before.txt
# 워크로드 실행
stress-ng --cpu 4 --timeout 30
# 변경 후 스냅샷
cat /sys/devices/system/cpu/cpufreq/policy0/stats/time_in_state > /tmp/after.txt
# 차이 분석
diff /tmp/before.txt /tmp/after.txt

# ========== 3. turbostat 상세 분석 ==========
turbostat --show Core,CPU,Avg_MHz,Busy%,Bzy_MHz,TSC_MHz,PkgWatt,CorWatt,PkgTmp \
    --interval 1

# ========== 4. perf로 cpufreq 이벤트와 성능 연관 분석 ==========
perf record -e power:cpu_frequency -e sched:sched_switch -a -- sleep 10
perf script | head -50

# ========== 5. 에너지 효율 비교 ==========
# performance 거버너에서
cpupower frequency-set -g performance
perf stat -e power/energy-pkg/ -- my_workload
# schedutil 거버너에서
cpupower frequency-set -g schedutil
perf stat -e power/energy-pkg/ -- my_workload
# EDP = Energy(J) * Time(s) → 낮을수록 효율적

BPF를 활용한 cpufreq 분석

/* cpufreq 전환 지연 측정 BPF 프로그램 개념 */
SEC("tracepoint/power/cpu_frequency")
int trace_cpu_freq(struct trace_event_raw_cpu_frequency *ctx)
{
    u32 cpu = ctx->cpu_id;
    u32 freq = ctx->state;
    u64 ts = bpf_ktime_get_ns();

    /* 주파수 변경 이벤트를 맵에 기록 */
    struct freq_event evt = {
        .cpu = cpu,
        .freq = freq,
        .timestamp = ts,
    };
    bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
                          &evt, sizeof(evt));
    return 0;
}

/* bpftrace 원라이너: 주파수 변경 분포 */
/* bpftrace -e 'tracepoint:power:cpu_frequency { @freq = hist(args->state / 1000); }' */
# bpftrace로 CPU 주파수 변경 실시간 모니터링
bpftrace -e 'tracepoint:power:cpu_frequency {
    printf("CPU%d: %d MHz\n", args->cpu_id, args->state / 1000);
}'

# 주파수 분포 히스토그램
bpftrace -e 'tracepoint:power:cpu_frequency {
    @freq_hist = hist(args->state / 1000);
} END { print(@freq_hist); }'

# 주파수 전환 간격 측정
bpftrace -e 'tracepoint:power:cpu_frequency {
    @last[args->cpu_id] = nsecs;
    if (@prev[args->cpu_id]) {
        @interval_us = hist((nsecs - @prev[args->cpu_id]) / 1000);
    }
    @prev[args->cpu_id] = nsecs;
}'

cpufreq Cooling Device

cpufreq는 thermal 프레임워크와 연동하여 cooling device로 동작합니다. 온도가 상승하면 thermal zone이 cooling device에 주파수 제한을 요청합니다.

Cooling 아키텍처

cpufreq Cooling Device 연동 온도 센서 thermal_zone_device Thermal Governor step_wise / power_allocator cpufreq Cooling max_freq 제한 cpufreq Policy 실제 주파수 감소 Cooling State 예시 (freq_table 역순) State 0: 4000 MHz (제한 없음) State 1: 3200 MHz (1단계 제한) State 2: 2400 MHz (2단계 제한) State 3: 1800 MHz (3단계 제한) State 4: 1200 MHz (4단계 제한) State 5: 800 MHz (최대 제한)
/* drivers/thermal/cpufreq_cooling.c */
struct cpufreq_cooling_device {
    struct thermal_cooling_device *cdev;
    struct cpufreq_policy *policy;
    unsigned int max_level;         /* 최대 cooling 단계 */
    struct freq_table *freq_table;   /* 주파수-state 매핑 */
    unsigned int clipped_freq;      /* 현재 제한 주파수 */
};

/* CPUFREQ_IS_COOLING_DEV 플래그 사용 시 자동 등록 */
/* 또는 수동 등록: */
struct thermal_cooling_device *cpufreq_cooling_register(
    struct cpufreq_policy *policy);
# cooling device 확인
ls /sys/class/thermal/cooling_device*/
cat /sys/class/thermal/cooling_device0/type
# cpufreq-0

# 현재 cooling 상태
cat /sys/class/thermal/cooling_device0/cur_state
# 0 (제한 없음)

# 최대 cooling 상태
cat /sys/class/thermal/cooling_device0/max_state
# 5

# 수동 cooling 테스트 (온도 기반이 아닌 강제 제한)
echo 3 > /sys/class/thermal/cooling_device0/cur_state
# → 3단계 주파수 제한 적용

하이브리드 아키텍처 (big.LITTLE / Intel Hybrid)

비대칭 멀티프로세싱(AMP) 환경에서 cpufreq는 코어 유형별로 독립적인 주파수 도메인을 관리합니다.

하이브리드 토폴로지와 cpufreq

항목ARM big.LITTLEIntel Hybrid (Alder Lake+)
고성능 코어big 코어 (Cortex-A78 등)P-코어 (Golden Cove 등)
효율 코어LITTLE 코어 (Cortex-A55 등)E-코어 (Gracemont 등)
주파수 도메인코어 클러스터별 독립P-코어/E-코어 그룹별 독립
cpufreq 정책클러스터당 1개 정책코어 그룹당 1개 정책
EAS 활용에너지 모델 기반 배치에너지 모델 + Thread Director
드라이버cpufreq-dt, scmiintel_pstate (HWP)
주파수 범위big: 1.0-2.8GHz, LITTLE: 0.5-1.8GHzP: 0.8-5.2GHz, E: 0.8-3.9GHz
# Intel Hybrid 코어 유형 확인
lscpu --extended
# CPU  NODE  SOCKET  CORE  L1d  L1i  L2   L3   ONLINE  TYPE
#   0     0       0     0   0    0   0    0    yes    Core
#   8     0       0     8   8    8   4    0    yes    Atom

# 코어별 cpufreq 정책 확인
for p in /sys/devices/system/cpu/cpufreq/policy*; do
    echo "$(basename $p): CPUs=$(cat $p/affected_cpus) max=$(cat $p/cpuinfo_max_freq)kHz"
done
# policy0: CPUs=0 1 2 3 4 5 6 7 max=5200000kHz  (P-코어)
# policy8: CPUs=8 9 10 11 max=3900000kHz          (E-코어)

# ARM big.LITTLE: 클러스터별 정책
# policy0: CPUs=0 1 2 3 max=1800000kHz  (LITTLE)
# policy4: CPUs=4 5 6 7 max=2800000kHz  (big)
Intel Thread Director와 cpufreq: Intel Hybrid 아키텍처에서는 Thread Director가 워크로드 특성을 분석하여 P-코어/E-코어 배치를 최적화합니다. HWP 모드의 intel_pstate가 각 코어 그룹의 주파수를 자율적으로 조정하며, Thread Director의 힌트와 결합하여 성능과 효율의 최적 균형을 달성합니다. Linux 6.x에서는 CONFIG_X86_HFI(Hardware Feedback Interface)로 Thread Director 정보를 활용합니다.

가상화 환경에서의 cpufreq

가상 머신(VM) 환경에서 cpufreq의 동작과 제한 사항을 설명합니다.

가상화 유형별 cpufreq 지원

환경cpufreq 가용성드라이버비고
베어메탈완전 지원intel_pstate / amd-pstate직접 MSR 접근
KVM (패스스루)MSR 패스스루 시 지원intel_pstate (HWP)호스트 설정 필요
KVM (기본)제한적acpi-cpufreq (에뮬레이션)가상 P-state
Xen (PV)dom0에서만acpi-cpufreqdomU는 제어 불가
VMware미지원 (일반적)없음호스트가 관리
컨테이너 (Docker)호스트와 공유호스트 드라이버cgroup으로 제한 가능
AWS EC2일부 인스턴스acpi-cpufreq전용 인스턴스에서 가능
# KVM 게스트에서 cpufreq 지원 확인
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
# acpi-cpufreq (가상 P-state) 또는 없음

# 가상화 환경 감지
systemd-detect-virt
# kvm, vmware, xen, none (베어메탈)

# KVM 호스트: 게스트에 HWP 패스스루 (QEMU 옵션)
# -cpu host,+invtsc,+hwp,+hwp_act_window

# 컨테이너: cgroup으로 CPU 주파수 제한 효과
# (직접 주파수 제어 불가, CPU 시간 할당으로 간접 제어)
echo "50000 100000" > /sys/fs/cgroup/my-container/cpu.max
# 50% CPU 시간 제한 → 실질적 성능 제한
가상화 환경 cpufreq 주의: 대부분의 가상화 환경에서 게스트 OS의 cpufreq 설정은 실제 하드웨어 주파수에 영향을 주지 않습니다. 호스트가 물리 CPU의 주파수를 관리하며, 게스트에서 보이는 주파수 변경은 가상화된 ACPI P-state 테이블에 의한 것입니다. 성능이 중요한 VM에서는 호스트에서 performance 거버너를 설정하고, 게스트에서는 cpufreq를 비활성화하는 것이 좋습니다.

cpufreq 내부 동기화

cpufreq 서브시스템은 여러 컨텍스트에서 동시에 접근되므로 정교한 동기화가 필요합니다.

동기화 메커니즘

잠금보호 대상유형사용 위치
policy->rwsem정책 전체 접근읽기/쓰기 세마포어sysfs 접근, 거버너 전환
cpufreq_driver_lock드라이버 등록/해제읽기/쓰기 잠금cpufreq_register_driver
sg_policy->update_lockschedutil 업데이트raw_spinlocksugov_update_shared
cpufreq_transition_lock주파수 전환 직렬화mutexfreq_transition_begin/end
policy->transition_lock전환 중 상태 보호spinlockPRE/POST 통지
/* sysfs에서 거버너 변경 시 동기화 */
static ssize_t store_scaling_governor(struct cpufreq_policy *policy,
                                     const char *buf, size_t count)
{
    int ret;
    char str_governor[CPUFREQ_NAME_LEN];

    sscanf(buf, "%15s", str_governor);

    /* 정책 쓰기 잠금 획득 */
    down_write(&policy->rwsem);

    /* 기존 거버너 정지 → 새 거버너 시작 */
    ret = cpufreq_set_policy(policy, str_governor);

    up_write(&policy->rwsem);

    return ret ? ret : count;
}

/* fast_switch 경로: IRQ 컨텍스트에서 잠금 최소화 */
static void sugov_fast_switch(struct sugov_policy *sg_policy,
                             u64 time, unsigned int next_freq)
{
    /* rwsem 미획득 - fast_switch는 잠금 없이 호출됨 */
    /* 드라이버의 fast_switch 콜백이 MSR에 직접 쓰기 */
    cpufreq_driver_fast_switch(sg_policy->policy, next_freq);
}
교착 상태(deadlock) 주의: cpufreq notifier 콜백 안에서 policy->rwsem을 획득하면 교착 상태가 발생합니다. notifier는 이미 rwsem을 보유한 상태에서 호출되기 때문입니다. notifier 콜백에서는 정책 변경이나 sysfs 접근을 시도하지 마세요. 비동기 작업이 필요하면 work queue를 사용하세요.

주파수 테이블 관리

cpufreq 드라이버는 지원하는 주파수 목록을 cpufreq_frequency_table 형태로 관리합니다.

주파수 테이블 구조

/* include/linux/cpufreq.h */
struct cpufreq_frequency_table {
    unsigned int    flags;         /* CPUFREQ_BOOST_FREQ 등 */
    unsigned int    driver_data;   /* 드라이버 전용 데이터 (인덱스 등) */
    unsigned int    frequency;     /* kHz 단위 주파수 */
};

/* 특수 주파수 값 */
#define CPUFREQ_ENTRY_INVALID    ~0u   /* 무효 엔트리 (건너뛰기) */
#define CPUFREQ_TABLE_END        ~1u   /* 테이블 종료 표시 */

/* 테이블 순회 매크로 */
#define cpufreq_for_each_entry(pos, table)         \
    for (pos = table;                             \
         pos->frequency != CPUFREQ_TABLE_END;    \
         pos++)

#define cpufreq_for_each_valid_entry(pos, table)   \
    cpufreq_for_each_entry(pos, table)             \
        if (pos->frequency == CPUFREQ_ENTRY_INVALID) \
            continue;                               \
        else

주파수 테이블 정의 예제

/* 정적 주파수 테이블 정의 */
static struct cpufreq_frequency_table my_freq_table[] = {
    { .driver_data = 0, .frequency =  800000 },   /* 800 MHz */
    { .driver_data = 1, .frequency = 1200000 },   /* 1.2 GHz */
    { .driver_data = 2, .frequency = 1600000 },   /* 1.6 GHz */
    { .driver_data = 3, .frequency = 2000000 },   /* 2.0 GHz */
    { .driver_data = 4, .frequency = 2400000 },   /* 2.4 GHz */
    { .driver_data = 5, .frequency = 3000000 },   /* 3.0 GHz (기본 최대) */
    { .driver_data = 6, .frequency = 3600000,
      .flags = CPUFREQ_BOOST_FREQ },                 /* 3.6 GHz (부스트) */
    { .frequency = CPUFREQ_TABLE_END },               /* 종료 */
};

/* init에서 테이블 등록 */
static int my_cpufreq_init(struct cpufreq_policy *policy)
{
    /* 주파수 테이블 설정 */
    policy->freq_table = my_freq_table;

    /* 테이블 유효성 검증 및 정렬 */
    return cpufreq_table_validate_and_sort(policy);
}

주파수 테이블 헬퍼 함수

함수역할
cpufreq_table_validate_and_sort()테이블 유효성 검증 및 오름차순 정렬
cpufreq_generic_frequency_table_verify()정책 제한에 대한 테이블 검증
cpufreq_frequency_table_target()관계(L/H/C)에 따라 목표 인덱스 찾기
cpufreq_table_find_index_l()목표 이하 가장 가까운 주파수 인덱스
cpufreq_table_find_index_h()목표 이상 가장 가까운 주파수 인덱스
cpufreq_table_find_index_c()목표에 가장 가까운 주파수 인덱스

실전 디버깅 시나리오

운영 환경에서 자주 마주치는 cpufreq 관련 문제와 체계적인 디버깅 절차를 다룹니다.

시나리오 1: 주파수가 올라가지 않음

# 체계적 진단 절차

# 1단계: 현재 상태 수집
echo "=== 드라이버 ==="
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
echo "=== 거버너 ==="
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
echo "=== 주파수 범위 ==="
echo "min: $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq)"
echo "max: $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq)"
echo "cur: $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq)"
echo "hw_min: $(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq)"
echo "hw_max: $(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq)"

# 2단계: 제한 원인 확인
echo "=== Thermal 제한 ==="
cat /sys/class/thermal/thermal_zone*/temp
echo "=== Cooling 상태 ==="
for cd in /sys/class/thermal/cooling_device*/; do
    type=$(cat "${cd}type")
    state=$(cat "${cd}cur_state")
    echo "  $type: state=$state"
done

# 3단계: RAPL 전력 제한 확인
echo "=== RAPL 제한 ==="
cat /sys/class/powercap/intel-rapl/intel-rapl:0/constraint_0_power_limit_uw 2>/dev/null
cat /sys/class/powercap/intel-rapl/intel-rapl:0/constraint_0_max_power_uw 2>/dev/null

# 4단계: 커널 로그 확인
dmesg | grep -iE 'cpufreq|pstate|throttl|thermal|power.limit' | tail -20

시나리오 2: 비정상적 주파수 진동 (frequency oscillation)

# 주파수가 빠르게 오르내리는 문제 진단

# 1. 주파수 전환 횟수 확인 (높으면 진동 의심)
cat /sys/devices/system/cpu/cpufreq/policy0/stats/total_trans
# 10초 간격으로 2회 측정하여 차이 확인
t1=$(cat /sys/devices/system/cpu/cpufreq/policy0/stats/total_trans)
sleep 10
t2=$(cat /sys/devices/system/cpu/cpufreq/policy0/stats/total_trans)
echo "10초 동안 전환: $((t2 - t1))회"
# 100회 이상이면 과도한 진동

# 2. ftrace로 주파수 변경 패턴 분석
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
sleep 5
echo 0 > /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
cat /sys/kernel/debug/tracing/trace | grep cpu_frequency | tail -30

# 3. 해결: schedutil rate_limit 조정
# rate_limit_us가 너무 낮으면 과도한 전환 발생
cat /sys/devices/system/cpu/cpufreq/policy0/schedutil/rate_limit_us
# 기본값이 낮으면 상향 조정
echo 2000 > /sys/devices/system/cpu/cpufreq/policy0/schedutil/rate_limit_us
# 2ms 미만 간격의 변경 억제

시나리오 3: schedutil이 최대 주파수를 사용하지 않음

# schedutil의 주파수 계산이 최대에 미달하는 경우

# 1. PELT 활용률 확인
cat /proc/schedstat
# 또는 perf sched record로 분석

# 2. thermal pressure 확인
cat /sys/devices/system/cpu/cpu0/topology/thermal_pressure
# 0이 아니면 열 제한으로 capacity 감소

# 3. UCLAMP 제한 확인
cat /proc/1/sched | grep uclamp
# uclamp.max가 1024 미만이면 제한 있음

# 4. 주파수 불변성 스케일 확인
# arch_scale_freq_capacity가 1024 미만이면 보정 적용 중

# 5. 해결: 확실한 최대 주파수가 필요하면
cpupower frequency-set -g performance
# 또는 UCLAMP_MIN=1024 설정

디버깅 도구 요약

도구용도명령어
cpupower주파수 정보/설정cpupower frequency-info -m
turbostat상세 주파수/전력/온도turbostat --interval 1
perf statIPC, 에너지 측정perf stat -e power/energy-pkg/
ftrace주파수 변경 이벤트 추적events/power/cpu_frequency
bpftrace고급 실시간 분석tracepoint:power:cpu_frequency
dmesg커널 로그 (throttle)dmesg | grep -i throttle
sysfs직접 상태 확인/변경/sys/devices/system/cpu/
stress-ng부하 생성 테스트stress-ng --cpu 4 -t 30

소스 코드 위치 가이드

CPUFreq 관련 커널 소스 코드의 주요 파일과 디렉터리를 안내합니다.

주요 소스 파일

경로설명
include/linux/cpufreq.hcpufreq_policy, cpufreq_driver, cpufreq_governor 정의
drivers/cpufreq/cpufreq.cCPUFreq 코어: 정책 관리, sysfs, notifier
drivers/cpufreq/cpufreq_stats.c주파수 전환 통계
drivers/cpufreq/freq_table.c주파수 테이블 헬퍼
kernel/sched/cpufreq_schedutil.cschedutil 거버너
drivers/cpufreq/cpufreq_ondemand.condemand 거버너
drivers/cpufreq/cpufreq_conservative.cconservative 거버너
drivers/cpufreq/cpufreq_performance.cperformance 거버너
drivers/cpufreq/cpufreq_powersave.cpowersave 거버너
drivers/cpufreq/cpufreq_userspace.cuserspace 거버너
drivers/cpufreq/intel_pstate.cIntel P-state 드라이버 (HWP 포함)
drivers/cpufreq/amd-pstate.cAMD P-state 드라이버 (CPPC)
drivers/cpufreq/amd-pstate-ut.cAMD P-state 유닛 테스트
drivers/cpufreq/acpi-cpufreq.cACPI 기반 cpufreq 드라이버
drivers/cpufreq/cppc_cpufreq.cCPPC cpufreq 드라이버 (ARM)
drivers/cpufreq/cpufreq-dt.cDevice Tree 기반 cpufreq (ARM)
drivers/thermal/cpufreq_cooling.ccpufreq cooling device
include/linux/energy_model.h에너지 모델 인터페이스
kernel/power/energy_model.c에너지 모델 구현
include/linux/pm_opp.hOPP 프레임워크 인터페이스
drivers/opp/core.cOPP 프레임워크 핵심 구현
drivers/opp/of.cOPP Device Tree 파싱
# 커널 소스에서 cpufreq 관련 파일 탐색
find drivers/cpufreq/ -name "*.c" | wc -l
# 약 80개 이상의 드라이버/거버너 소스 파일

# cpufreq 코어 코드 규모
wc -l drivers/cpufreq/cpufreq.c
# 약 2800줄

# schedutil 거버너 코드 규모
wc -l kernel/sched/cpufreq_schedutil.c
# 약 850줄

# intel_pstate 드라이버 코드 규모
wc -l drivers/cpufreq/intel_pstate.c
# 약 3300줄

# amd-pstate 드라이버 코드 규모
wc -l drivers/cpufreq/amd-pstate.c
# 약 1800줄

# 구조체 정의 빠르게 찾기
grep -n 'struct cpufreq_policy {' include/linux/cpufreq.h
grep -n 'struct cpufreq_driver {' include/linux/cpufreq.h
grep -n 'struct cpufreq_governor {' include/linux/cpufreq.h
코드 읽기 팁: cpufreq 서브시스템을 이해하려면 다음 순서로 소스를 읽는 것을 권장합니다: (1) include/linux/cpufreq.h에서 핵심 구조체 파악 → (2) drivers/cpufreq/cpufreq.ccpufreq_online()에서 초기화 흐름 추적 → (3) kernel/sched/cpufreq_schedutil.csugov_update_single()에서 주파수 결정 로직 확인 → (4) 관심 드라이버(intel_pstate.c 또는 amd-pstate.c)의 init()fast_switch() 콜백 분석.

cpufreq 이벤트 흐름 종합

cpufreq 서브시스템의 주요 이벤트 흐름을 종합적으로 정리합니다.

전체 생명주기 흐름

cpufreq 전체 생명주기 1. 커널 부팅 2. 드라이버 등록 3. 정책 생성 4. 거버너 시작 5. 런타임: 스케줄러 이벤트 → 주파수 조정 반복 sugov_update → get_next_freq → fast_switch/target 6. sysfs 변경: 거버너 전환 / 주파수 제한 7. 열 제한: cooling device → max_freq 감소 8. CPU 핫플러그: 온라인/오프라인 9. Suspend/Resume: 거버너 정지/재시작 10. 셧다운: 드라이버 해제 주요 호출 경로 부팅: cpufreq_register_driver() 정책: cpufreq_online(cpu) 거버너: cpufreq_start_governor() 런타임: sugov_update_*() 전환: store_scaling_governor() 열: cpufreq_set_cur_state() 핫플러그: cpufreq_offline() 절전: cpufreq_suspend() 복귀: cpufreq_resume() 해제: cpufreq_unregister_driver()

주요 API 요약

API유형호출자역할
cpufreq_register_driver()드라이버플랫폼 드라이버cpufreq 드라이버 등록
cpufreq_unregister_driver()드라이버모듈 해제cpufreq 드라이버 해제
cpufreq_register_governor()거버너거버너 모듈새 거버너 등록
cpufreq_register_notifier()알림관심 서브시스템주파수 변경 알림 등록
cpufreq_driver_fast_switch()코어schedutil인터럽트 컨텍스트 주파수 변경
cpufreq_driver_target()코어거버너프로세스 컨텍스트 주파수 변경
cpufreq_update_policy()코어cooling/sysfs정책 제한값 갱신
cpufreq_cpu_get()코어외부 모듈CPU의 정책 참조 획득
cpufreq_cpu_put()코어외부 모듈CPU의 정책 참조 해제
cpufreq_verify_within_limits()헬퍼드라이버 verify주파수 범위 유효성 검증
cpufreq_generic_suspend()헬퍼드라이버범용 suspend 처리
cpufreq_generic_attr속성드라이버기본 sysfs 속성 배열

이벤트 추적 가이드

# 모든 cpufreq 관련 트레이스포인트 활성화
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable

# schedutil 내부 추적 (디버그 빌드 필요)
echo 1 > /sys/kernel/debug/tracing/events/power/pstate_sample/enable

# 종합 추적 시작
echo > /sys/kernel/debug/tracing/trace
echo 1 > /sys/kernel/debug/tracing/tracing_on
# 워크로드 실행...
echo 0 > /sys/kernel/debug/tracing/tracing_on

# 결과 분석
cat /sys/kernel/debug/tracing/trace | \
    grep cpu_frequency | \
    awk '{print $4, $5}' | \
    sort | uniq -c | sort -rn
# 가장 빈번한 주파수 전환 패턴 확인
성능 프로파일링 워크플로: (1) turbostat으로 기준선 수집 → (2) cpupower frequency-set으로 거버너/제한 변경 → (3) 동일 워크로드 재실행 → (4) perf stat으로 IPC/에너지 비교 → (5) EDP(Energy-Delay Product)가 가장 낮은 설정 채택. 반복적인 A/B 테스트가 최적 설정을 찾는 가장 확실한 방법입니다.

빠른 참조 카드

작업명령어
현재 주파수 확인cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
거버너 변경cpupower frequency-set -g performance
주파수 고정cpupower frequency-set -d 2400MHz -u 2400MHz
Turbo 비활성화 (Intel)echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
Boost 비활성화 (AMD)echo 0 > /sys/devices/system/cpu/cpufreq/boost
EPP 설정echo performance > .../energy_performance_preference
드라이버 확인cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
전환 통계cat .../cpufreq/policy0/stats/time_in_state
실시간 모니터링turbostat --interval 1
열 스로틀링 확인dmesg | grep -i throttle
AMD 모드 전환echo guided > /sys/devices/system/cpu/amd_pstate/status
Intel 모드 확인cat /sys/devices/system/cpu/intel_pstate/status

cpufreq 서브시스템의 최근 커널 버전별 변경 사항과 향후 발전 방향을 정리합니다.

커널 버전별 주요 변경

커널 버전변경 사항영향
4.7 (2016)schedutil 거버너 도입스케줄러-cpufreq 직접 연동의 시작
5.1 (2019)amd-pstate 초기 프로토타입AMD CPPC 지원 기반 마련
5.17 (2022)amd-pstate passive 모드 도입AMD CPU에서 세밀한 주파수 제어
6.1 (2022)amd-pstate guided 모드범위 기반 자율 제어 추가
6.3 (2023)amd-pstate active 모드 (EPP)AMD EPP 기반 완전 자율 제어
6.4 (2023)amd-pstate Preferred Core코어별 성능 순위 스케줄러 반영
6.5 (2023)amd-pstate 런타임 모드 전환재부팅 없이 active/guided/passive 전환
6.7 (2024)Intel HFI(Hardware Feedback Interface) 개선Thread Director 정보 활용 강화
6.8 (2024)에너지 모델 EM2 개선인효율 테이블(inefficient states) 지원
6.9 (2024)cpufreq_driver 콜백 현대화레거시 target 콜백 정리

향후 발전 방향

주요 발전 방향:
  • 하드웨어 자율 제어 확대: Intel HWP와 AMD EPP active 모드에서 볼 수 있듯이, 주파수 결정 권한이 점점 하드웨어로 이동하고 있습니다. 소프트웨어는 정책 힌트(EPP, min/max)만 제공하는 방향으로 발전합니다.
  • 하이브리드 아키텍처 최적화: Intel Hybrid(P+E 코어), ARM DynamIQ(big.LITTLE+) 등 비대칭 멀티프로세싱 환경에서 코어 유형별 최적 주파수 관리가 더욱 중요해지고 있습니다.
  • 에너지 모델 정교화: EAS의 에너지 모델이 런타임 측정 기반으로 동적 업데이트되는 방향으로 진화하여, 더 정확한 에너지 효율 예측이 가능해질 전망입니다.
  • CXL/이기종 메모리 고려: CXL 메모리 접근 패턴에 따른 CPU 주파수 최적화가 새로운 연구 주제로 부상하고 있습니다.
  • AI 워크로드 최적화: NPU/GPU와 CPU의 협력 실행에서 CPU 주파수 조정이 전체 시스템 효율에 미치는 영향에 대한 연구가 진행되고 있습니다.

운영 모범 사례

  1. 기본 설정 유지: 대부분의 환경에서 schedutil + HWP/CPPC 기본 설정이 최적입니다. 특별한 이유 없이 변경하지 마세요.
  2. 벤치마크 시 Turbo 비활성화: 재현 가능한 벤치마크를 위해 Turbo를 비활성화하고, performance 거버너를 사용하세요.
  3. 프로덕션 변경 전 테스트: 거버너나 EPP 변경은 반드시 스테이징 환경에서 충분히 테스트한 후 적용하세요.
  4. 모니터링 필수: turbostat이나 Prometheus + node_exporter를 통해 주파수, 온도, 전력을 지속적으로 모니터링하세요.
  5. 열 관리 우선: 주파수를 높이기 전에 냉각 시스템이 충분한지 확인하세요. 열 스로틀링은 성능 저하의 가장 흔한 원인입니다.
  6. 커널 업데이트 시 확인: cpufreq 드라이버와 거버너는 커널 버전마다 변경될 수 있으므로, 업데이트 후 설정이 유지되는지 확인하세요.
  7. systemd-tmpfiles 또는 udev 규칙 활용: 부팅 시 자동으로 cpufreq 설정을 적용하려면 systemd-tmpfiles나 udev 규칙을 사용하세요.
# systemd-tmpfiles로 부팅 시 자동 설정 (권장)
# /etc/tmpfiles.d/cpufreq.conf
w /sys/devices/system/cpu/cpufreq/policy0/scaling_governor - - - - schedutil
w /sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq - - - - 800000
w /sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq - - - - 3600000

# udev 규칙으로 cpufreq 드라이버 로드 후 자동 설정
# /etc/udev/rules.d/99-cpufreq.rules
# SUBSYSTEM=="cpu", ACTION=="add", \
#   RUN+="/usr/bin/cpupower frequency-set -g schedutil"

# tuned 프로필 활용 (Red Hat 계열)
tuned-adm profile throughput-performance
# 또는
tuned-adm profile powersave

# power-profiles-daemon 활용 (데스크톱)
powerprofilesctl set performance
# 또는
powerprofilesctl set balanced

참고 자료