← Architecture 비교DUJINLABS.COM

Linux 6.18.37 LTS · Architecture comparison 17/21

CPU idle, suspend와 resume context

CPU가 clock gating부터 system suspend까지 내려가면서 어느 register와 cache/coherency 상태를 잃고, firmware에서 돌아온 뒤 어떤 순서로 Linux execution context를 복구하는지 비교합니다.

비교 대상
arm64 / x86-64 / RISC-V
실제 원본
3 files · 191 annotated lines
기준 tag
Linux v6.18.37
분석 축
state · ordering · lifetime · latency

01 · QUESTION

무엇을 확인할 것인가

suspend 함수가 C 호출처럼 return하는 것처럼 보여도 왜 page table, stack, interrupt controller와 timer를 다시 초기화해야 하는가?

cpuidle은 idle state별 exit latency와 target residency를 보고 선택하고 system suspend는 device quiesce, secondary CPU offline, firmware entry와 resume를 수행한다. 깊은 state는 CPU context 또는 coherency를 잃을 수 있다.

kernel가 보존하는 memory context, architecture assembly save area, firmware contract와 always-on hardware를 나눈다. noirq 단계 이후에는 일반 interrupt, allocator와 sleep 가능한 service를 기대할 수 없다.

지연 시간 관점exit latency는 power domain, PLL, cache/coherency, firmware, secondary CPU와 device resume의 합이다. governor는 평균이 아니라 deadline까지 남은 시간과 state별 worst-case latency를 비교한다.

02 · CONTRACT

공통 계약과 architecture 구현

architecture핵심 mechanism실패 형태확인할 상태
arm64WFI/WFE, PSCI CPU_SUSPEND와 cpu_suspend save areaPSCI state encoding, context ID, resume PA 또는 cache flush가 틀리면 firmware가 즉시 실패하거나 resume 직후 stale memory를 실행한다.PSCI return, MPIDR/power state, save area PA, SCTLR/TTBR, GIC redistributor sleep와 architected timer context를 본다.
x86-64HLT/MWAIT, ACPI C-state와 S3/S0ix resumeTSC stop, LAPIC timer 정지, 잘못된 wakeup physical address 또는 BSP/AP resume 순서가 time jump와 suspend hang을 만든다.ACPI state latency, MWAIT hint, PM timer/TSC continuity, wakeup header PA, CR0/CR3/CR4/EFER와 APIC state를 확인한다.
RISC-VWFI와 SBI system suspend/hart suspendSBI extension/version 불일치, hart state transition race 또는 resume address mode 오류가 invalid-param과 영구 정지를 만든다.hartid, SBI suspend type/return, resume address/opaque, SATP/SSTATUS, interrupt enable와 timer comparator를 기록한다.

03 · DIAGRAMS

세 그림으로 먼저 읽기

그림 1. 같은 목적, 서로 다른 mechanism각 ISA에서 실제로 추적할 state와 checkpoint를 한 줄에 맞췄습니다.

arm64

mechanism
WFI/WFE, PSCI CPU_SUSPEND와 cpu_suspend save area
state
shallow idle은 WFI로 돌아오지만 power-down state는 cpu_suspend()가 callee-saved context와 resume PA를 보존하고 PSCI firmware에 power state를 요청한다. resume assembly가 MMU/cache와 CPU feature state를 복구한다.
checkpoint
PSCI return, MPIDR/power state, save area PA, SCTLR/TTBR, GIC redistributor sleep와 architected timer context를 본다.

x86-64

mechanism
HLT/MWAIT, ACPI C-state와 S3/S0ix resume
state
cpuidle은 ACPI _CST 또는 native state에서 I/O port, MWAIT/UMWAIT를 선택한다. system suspend는 wakeup trampoline을 low memory에 준비하고 firmware가 real mode/32-bit resume vector로 CPU를 되돌리면 long mode, CR3와 per-CPU state를 복원한다.
checkpoint
ACPI state latency, MWAIT hint, PM timer/TSC continuity, wakeup header PA, CR0/CR3/CR4/EFER와 APIC state를 확인한다.

RISC-V

mechanism
WFI와 SBI system suspend/hart suspend
state
shallow idle은 WFI를 사용하고 deeper state는 SBI HSM/STA 또는 platform suspend extension이 hart state와 resume address를 firmware에 넘긴다. cpu_suspend()는 non-retentive state에서 architecture context를 저장하고 resume path를 호출한다.
checkpoint
hartid, SBI suspend type/return, resume address/opaque, SATP/SSTATUS, interrupt enable와 timer comparator를 기록한다.
그림 2. 공통 kernel과 architecture hook의 소유권공통 정책이 hardware state를 직접 소유하지 않는 경계를 표시합니다.
Linux common contractcpuidle은 idle state별 exit latency와 target residency를 보고 선택하고 system suspend는 device quiesce, secondary CPU offline, firmware entry와 resume를 수행한다. 깊은 state는 CPU context 또는 coherency를 잃을 수 있다.
arm64WFI/WFE, PSCI CPU_SUSPEND와 cpu_suspend save areacache clean과 coherency exit, firmware call, resume page-table/MMU enable 순서가 memory 가시성을 보장한다.
x86-64HLT/MWAIT, ACPI C-state와 S3/S0ix resumeMONITOR/MWAIT 조건과 broadcast timer, wake vector publication, cache flush 및 platform ACPI ordering을 맞춘다.
RISC-VWFI와 SBI system suspend/hart suspendcache coherency가 platform-defined인 경우 memory save area publication과 firmware call 전 fence가 필요하다. timer pending과 IPI wake source도 firmware contract에 포함된다.
lifetime boundaryresume entry physical address, save area와 page table은 suspend 동안 DRAM retention 또는 reserved memory에 유지되어야 한다. firmware가 그 memory를 덮거나 전원을 끄면 복귀할 수 없다.
그림 3. publication과 관찰 순서state를 준비한 뒤 architecture ordering을 거쳐 관찰 가능한 checkpoint가 됩니다.
arm64state 준비cache clean과 coherency exit, firmware call, resume page-table/MMU enable 순서가 memory 가시성을 보장한다.관찰: PSCI return, MPIDR/power state, save area PA, SCTLR/TTBR, GIC redistributor sleep와 architected timer context를 본다.
x86-64state 준비MONITOR/MWAIT 조건과 broadcast timer, wake vector publication, cache flush 및 platform ACPI ordering을 맞춘다.관찰: ACPI state latency, MWAIT hint, PM timer/TSC continuity, wakeup header PA, CR0/CR3/CR4/EFER와 APIC state를 확인한다.
RISC-Vstate 준비cache coherency가 platform-defined인 경우 memory save area publication과 firmware call 전 fence가 필요하다. timer pending과 IPI wake source도 firmware contract에 포함된다.관찰: hartid, SBI suspend type/return, resume address/opaque, SATP/SSTATUS, interrupt enable와 timer comparator를 기록한다.

04 · SOURCE

Linux 6.18.37 원본 코드와 줄별 설명

소스 위치를 고정된 숫자로 복사하지 않고 Linux v6.18.37 tree에서 함수 선언을 다시 찾아 발췌했습니다. 아래 코드와 각 줄의 설명은 1:1로 대응합니다.

arm64 · Linux 6.18.37

WFI/WFE, PSCI CPU_SUSPEND와 cpu_suspend save area

shallow idle은 WFI로 돌아오지만 power-down state는 cpu_suspend()가 callee-saved context와 resume PA를 보존하고 PSCI firmware에 power state를 요청한다. resume assembly가 MMU/cache와 CPU feature state를 복구한다.

원본 코드: arch/arm64/kernel/suspend.c:89-169

89 
90/*
91 * cpu_suspend
92 *
93 * arg: argument to pass to the finisher function
94 * fn: finisher function pointer
95 *
96 */
97int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
98{
99	int ret = 0;
100	unsigned long flags;
101	struct sleep_stack_data state;
102	struct arm_cpuidle_irq_context context;
103 
104	/*
105	 * Some portions of CPU state (e.g. PSTATE.{PAN,DIT}) are initialized
106	 * before alternatives are patched, but are only restored by
107	 * __cpu_suspend_exit() after alternatives are patched. To avoid
108	 * accidentally losing these bits we must not attempt to suspend until
109	 * after alternatives have been patched.
110	 */
111	WARN_ON(!system_capabilities_finalized());
112 
113	/* Report any MTE async fault before going to suspend */
114	mte_suspend_enter();
115 
116	/*
117	 * From this point debug exceptions are disabled to prevent
118	 * updates to mdscr register (saved and restored along with
119	 * general purpose registers) from kernel debuggers.
120	 *
121	 * Strictly speaking the trace_hardirqs_off() here is superfluous,
122	 * hardirqs should be firmly off by now. This really ought to use
123	 * something like raw_local_daif_save().
124	 */
125	flags = local_daif_save();
126 
127	/*
128	 * Function graph tracer state gets inconsistent when the kernel
129	 * calls functions that never return (aka suspend finishers) hence
130	 * disable graph tracing during their execution.
131	 */
132	pause_graph_tracing();
133 
134	/*
135	 * Switch to using DAIF.IF instead of PMR in order to reliably
136	 * resume if we're using pseudo-NMIs.
137	 */
138	arm_cpuidle_save_irq_context(&context);
139 
140	ct_cpuidle_enter();
141 
142	if (__cpu_suspend_enter(&state)) {
143		/* Call the suspend finisher */
144		ret = fn(arg);
145 
146		/*
147		 * Never gets here, unless the suspend finisher fails.
148		 * Successful cpu_suspend() should return from cpu_resume(),
149		 * returning through this code path is considered an error
150		 * If the return value is set to 0 force ret = -EOPNOTSUPP
151		 * to make sure a proper error condition is propagated
152		 */
153		if (!ret)
154			ret = -EOPNOTSUPP;
155 
156		ct_cpuidle_exit();
157	} else {
158		ct_cpuidle_exit();
159		__cpu_suspend_exit();
160	}
161 
162	arm_cpuidle_restore_irq_context(&context);
163 
164	unpause_graph_tracing();
165 
166	/*
167	 * Restore pstate flags. OS lock and mdscr have been already
168	 * restored, so from this point onwards, debugging is fully
169	 * reenabled if it was enabled when core started shutdown.

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 81개 줄에 각각 설명을 붙였습니다.

L89(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L90/*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L91 * cpu_suspend

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L92 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L93 * arg: argument to pass to the finisher function

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L94 * fn: finisher function pointer

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L95 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L96 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L97int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))

이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L98{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L99 int ret = 0;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L100 unsigned long flags;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L101 struct sleep_stack_data state;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L102 struct arm_cpuidle_irq_context context;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L103(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L104 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L105 * Some portions of CPU state (e.g. PSTATE.{PAN,DIT}) are initialized

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L106 * before alternatives are patched, but are only restored by

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L107 * __cpu_suspend_exit() after alternatives are patched. To avoid

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L108 * accidentally losing these bits we must not attempt to suspend until

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L109 * after alternatives have been patched.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L110 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L111 WARN_ON(!system_capabilities_finalized());

불가능해야 하는 상태 또는 복구 가능한 오류를 외부에 드러내는 줄이다. 직전 register/object 값을 함께 남겨 재현 가능한 failure signature를 만든다.

L112(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L113 /* Report any MTE async fault before going to suspend */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L114 mte_suspend_enter();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L115(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L116 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L117 * From this point debug exceptions are disabled to prevent

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L118 * updates to mdscr register (saved and restored along with

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L119 * general purpose registers) from kernel debuggers.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L120 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L121 * Strictly speaking the trace_hardirqs_off() here is superfluous,

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L122 * hardirqs should be firmly off by now. This really ought to use

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L123 * something like raw_local_daif_save().

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L124 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L125 flags = local_daif_save();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L126(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L127 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L128 * Function graph tracer state gets inconsistent when the kernel

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L129 * calls functions that never return (aka suspend finishers) hence

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L130 * disable graph tracing during their execution.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L131 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L132 pause_graph_tracing();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L133(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L134 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L135 * Switch to using DAIF.IF instead of PMR in order to reliably

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L136 * resume if we're using pseudo-NMIs.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L137 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L138 arm_cpuidle_save_irq_context(&context);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L139(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L140 ct_cpuidle_enter();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L141(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L142 if (__cpu_suspend_enter(&state)) {

이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L143 /* Call the suspend finisher */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L144 ret = fn(arg);

PSCI 또는 platform backend를 호출해 실제 low-power state로 진입한다.

L145(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L146 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L147 * Never gets here, unless the suspend finisher fails.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L148 * Successful cpu_suspend() should return from cpu_resume(),

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L149 * returning through this code path is considered an error

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L150 * If the return value is set to 0 force ret = -EOPNOTSUPP

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L151 * to make sure a proper error condition is propagated

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L152 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L153 if (!ret)

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L154 ret = -EOPNOTSUPP;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L155(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L156 ct_cpuidle_exit();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L157 } else {

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L158 ct_cpuidle_exit();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L159 __cpu_suspend_exit();

전원 복귀 뒤 CPU-local register와 kernel execution 조건을 재구성한다.

L160 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L161(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L162 arm_cpuidle_restore_irq_context(&context);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L163(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L164 unpause_graph_tracing();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L165(blank)

빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L166 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L167 * Restore pstate flags. OS lock and mdscr have been already

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L168 * restored, so from this point onwards, debugging is fully

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L169 * reenabled if it was enabled when core started shutdown.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

x86-64 · Linux 6.18.37

HLT/MWAIT, ACPI C-state와 S3/S0ix resume

cpuidle은 ACPI _CST 또는 native state에서 I/O port, MWAIT/UMWAIT를 선택한다. system suspend는 wakeup trampoline을 low memory에 준비하고 firmware가 real mode/32-bit resume vector로 CPU를 되돌리면 long mode, CR3와 per-CPU state를 복원한다.

원본 코드: arch/x86/kernel/acpi/cstate.c:211-251

211	unsigned int cpu = smp_processor_id();
212	struct cstate_entry *percpu_entry;
213 
214	percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu);
215	mwait_play_dead(percpu_entry->states[cx->index].eax);
216}
217EXPORT_SYMBOL_GPL(acpi_processor_ffh_play_dead);
218 
219void __cpuidle acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
220{
221	unsigned int cpu = smp_processor_id();
222	struct cstate_entry *percpu_entry;
223 
224	percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu);
225	mwait_idle_with_hints(percpu_entry->states[cx->index].eax,
226	                      percpu_entry->states[cx->index].ecx);
227}
228EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter);
229 
230static int __init ffh_cstate_init(void)
231{
232	struct cpuinfo_x86 *c = &boot_cpu_data;
233 
234	if (c->x86_vendor != X86_VENDOR_INTEL &&
235	    c->x86_vendor != X86_VENDOR_AMD &&
236	    c->x86_vendor != X86_VENDOR_HYGON)
237		return -1;
238 
239	cpu_cstate_entry = alloc_percpu(struct cstate_entry);
240	return 0;
241}
242 
243static void __exit ffh_cstate_exit(void)
244{
245	free_percpu(cpu_cstate_entry);
246	cpu_cstate_entry = NULL;
247}
248 
249arch_initcall(ffh_cstate_init);
250__exitcall(ffh_cstate_exit);
251 

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 41개 줄에 각각 설명을 붙였습니다.

L211 unsigned int cpu = smp_processor_id();

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L212 struct cstate_entry *percpu_entry;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L213(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L214 percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L215 mwait_play_dead(percpu_entry->states[cx->index].eax);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L216}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L217EXPORT_SYMBOL_GPL(acpi_processor_ffh_play_dead);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L218(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L219void __cpuidle acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)

이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L220{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L221 unsigned int cpu = smp_processor_id();

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L222 struct cstate_entry *percpu_entry;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L223(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L224 percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L225 mwait_idle_with_hints(percpu_entry->states[cx->index].eax,

ACPI FFH가 제공한 MWAIT hint로 CPU-local low-power state에 진입한다.

L226 percpu_entry->states[cx->index].ecx);

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L227}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L228EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L229(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L230static int __init ffh_cstate_init(void)

이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L231{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L232 struct cpuinfo_x86 *c = &boot_cpu_data;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L233(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L234 if (c->x86_vendor != X86_VENDOR_INTEL &&

이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L235 c->x86_vendor != X86_VENDOR_AMD &&

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L236 c->x86_vendor != X86_VENDOR_HYGON)

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L237 return -1;

이 함수가 CPU idle, suspend와 resume context 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L238(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L239 cpu_cstate_entry = alloc_percpu(struct cstate_entry);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L240 return 0;

이 함수가 CPU idle, suspend와 resume context 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L241}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L242(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L243static void __exit ffh_cstate_exit(void)

이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L244{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L245 free_percpu(cpu_cstate_entry);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L246 cpu_cstate_entry = NULL;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L247}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L248(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L249arch_initcall(ffh_cstate_init);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L250__exitcall(ffh_cstate_exit);

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L251(blank)

빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

RISC-V · Linux 6.18.37

WFI와 SBI system suspend/hart suspend

shallow idle은 WFI를 사용하고 deeper state는 SBI HSM/STA 또는 platform suspend extension이 hart state와 resume address를 firmware에 넘긴다. cpu_suspend()는 non-retentive state에서 architecture context를 저장하고 resume path를 호출한다.

원본 코드: arch/riscv/kernel/suspend.c:57-125

57#endif
58		csr_write(CSR_STIMECMP, context->stimecmp);
59	}
60 
61	csr_write(CSR_SATP, context->satp);
62#endif
63}
64 
65int cpu_suspend(unsigned long arg,
66		int (*finish)(unsigned long arg,
67			      unsigned long entry,
68			      unsigned long context))
69{
70	int rc = 0;
71	struct suspend_context context = { 0 };
72 
73	/* Finisher should be non-NULL */
74	if (!finish)
75		return -EINVAL;
76 
77	/* Save additional CSRs*/
78	suspend_save_csrs(&context);
79 
80	/*
81	 * Function graph tracer state gets incosistent when the kernel
82	 * calls functions that never return (aka finishers) hence disable
83	 * graph tracing during their execution.
84	 */
85	pause_graph_tracing();
86 
87	/* Save context on stack */
88	if (__cpu_suspend_enter(&context)) {
89		/* Call the finisher */
90		rc = finish(arg, __pa_symbol(__cpu_resume_enter),
91			    (ulong)&context);
92 
93		/*
94		 * Should never reach here, unless the suspend finisher
95		 * fails. Successful cpu_suspend() should return from
96		 * __cpu_resume_entry()
97		 */
98		if (!rc)
99			rc = -EOPNOTSUPP;
100	}
101 
102	/* Enable function graph tracer */
103	unpause_graph_tracing();
104 
105	/* Restore additional CSRs */
106	suspend_restore_csrs(&context);
107 
108	return rc;
109}
110 
111#ifdef CONFIG_RISCV_SBI
112static int sbi_system_suspend(unsigned long sleep_type,
113			      unsigned long resume_addr,
114			      unsigned long opaque)
115{
116	struct sbiret ret;
117 
118	ret = sbi_ecall(SBI_EXT_SUSP, SBI_EXT_SUSP_SYSTEM_SUSPEND,
119			sleep_type, resume_addr, opaque, 0, 0, 0);
120	if (ret.error)
121		return sbi_err_map_linux_errno(ret.error);
122 
123	return ret.value;
124}
125 

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 69개 줄에 각각 설명을 붙였습니다.

L57#endif

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L58 csr_write(CSR_STIMECMP, context->stimecmp);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L59 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L60(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L61 csr_write(CSR_SATP, context->satp);

활성 translation context 또는 translation cache를 바꾸는 줄이다. 다음 instruction과 stack access가 새 mapping에서도 유효해야 한다.

L62#endif

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L63}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L64(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L65int cpu_suspend(unsigned long arg,

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L66 int (*finish)(unsigned long arg,

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L67 unsigned long entry,

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L68 unsigned long context))

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L69{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L70 int rc = 0;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L71 struct suspend_context context = { 0 };

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L72(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L73 /* Finisher should be non-NULL */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L74 if (!finish)

이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L75 return -EINVAL;

이 함수가 CPU idle, suspend와 resume context 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L76(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L77 /* Save additional CSRs*/

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L78 suspend_save_csrs(&context);

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L79(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L80 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L81 * Function graph tracer state gets incosistent when the kernel

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L82 * calls functions that never return (aka finishers) hence disable

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L83 * graph tracing during their execution.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L84 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L85 pause_graph_tracing();

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L86(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L87 /* Save context on stack */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L88 if (__cpu_suspend_enter(&context)) {

이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L89 /* Call the finisher */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L90 rc = finish(arg, __pa_symbol(__cpu_resume_enter),

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L91 (ulong)&context);

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L92(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L93 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L94 * Should never reach here, unless the suspend finisher

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L95 * fails. Successful cpu_suspend() should return from

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L96 * __cpu_resume_entry()

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L97 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L98 if (!rc)

이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L99 rc = -EOPNOTSUPP;

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L100 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L101(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L102 /* Enable function graph tracer */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L103 unpause_graph_tracing();

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L104(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L105 /* Restore additional CSRs */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L106 suspend_restore_csrs(&context);

복귀 뒤 supervisor CSR과 Linux execution context를 재설치한다.

L107(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L108 return rc;

이 함수가 CPU idle, suspend와 resume context 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L109}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L110(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L111#ifdef CONFIG_RISCV_SBI

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L112static int sbi_system_suspend(unsigned long sleep_type,

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L113 unsigned long resume_addr,

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L114 unsigned long opaque)

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L115{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L116 struct sbiret ret;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L117(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L118 ret = sbi_ecall(SBI_EXT_SUSP, SBI_EXT_SUSP_SYSTEM_SUSPEND,

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L119 sleep_type, resume_addr, opaque, 0, 0, 0);

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L120 if (ret.error)

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L121 return sbi_err_map_linux_errno(ret.error);

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L122(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L123 return ret.value;

저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.

L124}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L125(blank)

빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

05 · WORKED EXAMPLE

숫자로 검산하기

01

idle state 선택의 deadline 계산

다음 timer가 800us 뒤이고 C1 exit 2us/target 5us, C2 exit 90us/target 200us, C3 exit 700us/target 1500us라고 가정한다.

  1. C1800us가 target 5us보다 길고 exit 2us를 충분히 감당하므로 가능하다.
  2. C2800us가 target 200us보다 길고 90us exit 뒤에도 약 710us 여유가 있어 가능하다.
  3. C3target residency 1500us를 채우지 못하고 700us exit는 scheduler latency budget도 거의 소모하므로 제외한다.
  4. choicegovernor는 energy와 최근 실제 residency를 고려해 C1/C2 중 C2를 선택할 수 있다.

결론깊은 state 이름이 아니라 exit latency, minimum residency와 다음 deadline을 숫자로 비교해야 한다.

06 · DEEP DIVE

경계별 상세 분석

01

공통 kernel core와 architecture hook의 경계

cpuidle은 idle state별 exit latency와 target residency를 보고 선택하고 system suspend는 device quiesce, secondary CPU offline, firmware entry와 resume를 수행한다. 깊은 state는 CPU context 또는 coherency를 잃을 수 있다.

kernel가 보존하는 memory context, architecture assembly save area, firmware contract와 always-on hardware를 나눈다. noirq 단계 이후에는 일반 interrupt, allocator와 sleep 가능한 service를 기대할 수 없다.

02

arm64: WFI/WFE, PSCI CPU_SUSPEND와 cpu_suspend save area

shallow idle은 WFI로 돌아오지만 power-down state는 cpu_suspend()가 callee-saved context와 resume PA를 보존하고 PSCI firmware에 power state를 요청한다. resume assembly가 MMU/cache와 CPU feature state를 복구한다.

cache clean과 coherency exit, firmware call, resume page-table/MMU enable 순서가 memory 가시성을 보장한다. 디버깅할 때는 PSCI return, MPIDR/power state, save area PA, SCTLR/TTBR, GIC redistributor sleep와 architected timer context를 본다.

03

x86-64: HLT/MWAIT, ACPI C-state와 S3/S0ix resume

cpuidle은 ACPI _CST 또는 native state에서 I/O port, MWAIT/UMWAIT를 선택한다. system suspend는 wakeup trampoline을 low memory에 준비하고 firmware가 real mode/32-bit resume vector로 CPU를 되돌리면 long mode, CR3와 per-CPU state를 복원한다.

MONITOR/MWAIT 조건과 broadcast timer, wake vector publication, cache flush 및 platform ACPI ordering을 맞춘다. 디버깅할 때는 ACPI state latency, MWAIT hint, PM timer/TSC continuity, wakeup header PA, CR0/CR3/CR4/EFER와 APIC state를 확인한다.

04

RISC-V: WFI와 SBI system suspend/hart suspend

shallow idle은 WFI를 사용하고 deeper state는 SBI HSM/STA 또는 platform suspend extension이 hart state와 resume address를 firmware에 넘긴다. cpu_suspend()는 non-retentive state에서 architecture context를 저장하고 resume path를 호출한다.

cache coherency가 platform-defined인 경우 memory save area publication과 firmware call 전 fence가 필요하다. timer pending과 IPI wake source도 firmware contract에 포함된다. 디버깅할 때는 hartid, SBI suspend type/return, resume address/opaque, SATP/SSTATUS, interrupt enable와 timer comparator를 기록한다.

05

객체 수명과 소유권을 먼저 고정한다

resume entry physical address, save area와 page table은 suspend 동안 DRAM retention 또는 reserved memory에 유지되어야 한다. firmware가 그 memory를 덮거나 전원을 끄면 복귀할 수 없다.

주소나 register 값이 맞는지만 확인하면 stale state를 놓친다. producer, publication, consumer와 폐기 지점을 같은 표에 기록한다.

06

latency upper bound는 hardware instruction 하나가 아니다

exit latency는 power domain, PLL, cache/coherency, firmware, secondary CPU와 device resume의 합이다. governor는 평균이 아니라 deadline까지 남은 시간과 state별 worst-case latency를 비교한다.

평균값 외에 interrupt-off 구간, remote CPU 응답, firmware 호출과 retry 횟수를 분리해야 최악 지연의 원인을 찾을 수 있다.

07 · FAILURE

실패를 어떤 증거로 나눌 것인가

분류관찰되는 결과첫 확인값
arm64PSCI state encoding, context ID, resume PA 또는 cache flush가 틀리면 firmware가 즉시 실패하거나 resume 직후 stale memory를 실행한다.PSCI return, MPIDR/power state, save area PA, SCTLR/TTBR, GIC redistributor sleep와 architected timer context를 본다.
x86-64TSC stop, LAPIC timer 정지, 잘못된 wakeup physical address 또는 BSP/AP resume 순서가 time jump와 suspend hang을 만든다.ACPI state latency, MWAIT hint, PM timer/TSC continuity, wakeup header PA, CR0/CR3/CR4/EFER와 APIC state를 확인한다.
RISC-VSBI extension/version 불일치, hart state transition race 또는 resume address mode 오류가 invalid-param과 영구 정지를 만든다.hartid, SBI suspend type/return, resume address/opaque, SATP/SSTATUS, interrupt enable와 timer comparator를 기록한다.

08 · LAB

재현과 계측 절차

  1. cpuidle trace와 hardware counter로 requested state, actual residency, exit latency를 architecture별로 비교한다.
  2. suspend resume의 마지막 console 지점과 firmware trace를 맞춰 Linux와 firmware 중 어느 쪽에서 멈췄는지 분리한다.
  3. 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
  4. 소스만 읽고 끝내지 않고 최종 vmlinuxobjdump -dr, readelf -SW 결과로 선택된 alternative와 section 배치를 확인한다.

09 · REFERENCES

원문 좌표

Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.