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를 기대할 수 없다.
02 · CONTRACT
공통 계약과 architecture 구현
| architecture | 핵심 mechanism | 실패 형태 | 확인할 상태 |
|---|---|---|---|
| arm64 | WFI/WFE, PSCI CPU_SUSPEND와 cpu_suspend save area | PSCI 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-64 | HLT/MWAIT, ACPI C-state와 S3/S0ix resume | TSC 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-V | WFI와 SBI system suspend/hart suspend | SBI 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
세 그림으로 먼저 읽기
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를 기록한다.
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개 줄에 각각 설명을 붙였습니다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* cpu_suspendLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* arg: argument to pass to the finisher functionLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* fn: finisher function pointerLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
int ret = 0;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
unsigned long flags;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
struct sleep_stack_data state;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
struct arm_cpuidle_irq_context context;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Some portions of CPU state (e.g. PSTATE.{PAN,DIT}) are initializedLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* before alternatives are patched, but are only restored byLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* __cpu_suspend_exit() after alternatives are patched. To avoidLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* accidentally losing these bits we must not attempt to suspend untilLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* after alternatives have been patched.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
WARN_ON(!system_capabilities_finalized());불가능해야 하는 상태 또는 복구 가능한 오류를 외부에 드러내는 줄이다. 직전 register/object 값을 함께 남겨 재현 가능한 failure signature를 만든다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Report any MTE async fault before going to suspend */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
mte_suspend_enter();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* From this point debug exceptions are disabled to preventLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* updates to mdscr register (saved and restored along withLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* general purpose registers) from kernel debuggers.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Strictly speaking the trace_hardirqs_off() here is superfluous,Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* hardirqs should be firmly off by now. This really ought to useLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* something like raw_local_daif_save().Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
flags = local_daif_save();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Function graph tracer state gets inconsistent when the kernelLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* calls functions that never return (aka suspend finishers) henceLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* disable graph tracing during their execution.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
pause_graph_tracing();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Switch to using DAIF.IF instead of PMR in order to reliablyLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* resume if we're using pseudo-NMIs.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
arm_cpuidle_save_irq_context(&context);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
ct_cpuidle_enter();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (__cpu_suspend_enter(&state)) {이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
/* Call the suspend finisher */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
ret = fn(arg);PSCI 또는 platform backend를 호출해 실제 low-power state로 진입한다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Never gets here, unless the suspend finisher fails.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Successful cpu_suspend() should return from cpu_resume(),Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* returning through this code path is considered an errorLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* If the return value is set to 0 force ret = -EOPNOTSUPPLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* to make sure a proper error condition is propagatedLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (!ret)저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
ret = -EOPNOTSUPP;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
ct_cpuidle_exit();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
} else {이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
ct_cpuidle_exit();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__cpu_suspend_exit();전원 복귀 뒤 CPU-local register와 kernel execution 조건을 재구성한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
arm_cpuidle_restore_irq_context(&context);helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
unpause_graph_tracing();helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 arm64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Restore pstate flags. OS lock and mdscr have been alreadyLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* restored, so from this point onwards, debugging is fullyLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* 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개 줄에 각각 설명을 붙였습니다.
unsigned int cpu = smp_processor_id();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
struct cstate_entry *percpu_entry;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
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를 연결해 본다.
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를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
EXPORT_SYMBOL_GPL(acpi_processor_ffh_play_dead);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
void __cpuidle acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
unsigned int cpu = smp_processor_id();helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
struct cstate_entry *percpu_entry;선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
(blank)빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
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를 연결해 본다.
mwait_idle_with_hints(percpu_entry->states[cx->index].eax,ACPI FFH가 제공한 MWAIT hint로 CPU-local low-power state에 진입한다.
percpu_entry->states[cx->index].ecx);이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static int __init ffh_cstate_init(void)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
struct cpuinfo_x86 *c = &boot_cpu_data;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
if (c->x86_vendor != X86_VENDOR_INTEL &&이 조건이 x86-64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
c->x86_vendor != X86_VENDOR_AMD &&이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
c->x86_vendor != X86_VENDOR_HYGON)이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
return -1;이 함수가 CPU idle, suspend와 resume context 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
(blank)빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
cpu_cstate_entry = alloc_percpu(struct cstate_entry);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
return 0;이 함수가 CPU idle, suspend와 resume context 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
static void __exit ffh_cstate_exit(void)이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
free_percpu(cpu_cstate_entry);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
cpu_cstate_entry = NULL;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 x86-64 CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
arch_initcall(ffh_cstate_init);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
__exitcall(ffh_cstate_exit);helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(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개 줄에 각각 설명을 붙였습니다.
#endifKconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.
csr_write(CSR_STIMECMP, context->stimecmp);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
csr_write(CSR_SATP, context->satp);활성 translation context 또는 translation cache를 바꾸는 줄이다. 다음 instruction과 stack access가 새 mapping에서도 유효해야 한다.
#endifKconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
int cpu_suspend(unsigned long arg,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
int (*finish)(unsigned long arg,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long entry,선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
unsigned long context))이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
int rc = 0;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
struct suspend_context context = { 0 };계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Finisher should be non-NULL */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (!finish)이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
return -EINVAL;이 함수가 CPU idle, suspend와 resume context 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Save additional CSRs*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
suspend_save_csrs(&context);helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Function graph tracer state gets incosistent when the kernelLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* calls functions that never return (aka finishers) hence disableLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* graph tracing during their execution.Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
pause_graph_tracing();helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Save context on stack */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (__cpu_suspend_enter(&context)) {이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
/* Call the finisher */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
rc = finish(arg, __pa_symbol(__cpu_resume_enter),계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
(ulong)&context);이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/*Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* Should never reach here, unless the suspend finisherLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* fails. Successful cpu_suspend() should return fromLinux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
* __cpu_resume_entry()Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
*/Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
if (!rc)이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.
rc = -EOPNOTSUPP;계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Enable function graph tracer */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
unpause_graph_tracing();helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
/* Restore additional CSRs */Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.
suspend_restore_csrs(&context);복귀 뒤 supervisor CSR과 Linux execution context를 재설치한다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
return rc;이 함수가 CPU idle, suspend와 resume context 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
#ifdef CONFIG_RISCV_SBIKconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.
static int sbi_system_suspend(unsigned long sleep_type,이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
unsigned long resume_addr,선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.
unsigned long opaque)이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
{C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
struct sbiret ret;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
ret = sbi_ecall(SBI_EXT_SUSP, SBI_EXT_SUSP_SYSTEM_SUSPEND,저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
sleep_type, resume_addr, opaque, 0, 0, 0);이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. CPU idle, suspend와 resume context의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.
if (ret.error)저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
return sbi_err_map_linux_errno(ret.error);저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
return ret.value;저장된 실행 문맥으로 돌아가는 제어 이전이다. PC뿐 아니라 privilege, interrupt mask, stack과 architecture status가 함께 복원된다.
}C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.
(blank)빈 줄은 RISC-V CPU idle, suspend와 resume context 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.
05 · WORKED EXAMPLE
숫자로 검산하기
idle state 선택의 deadline 계산
다음 timer가 800us 뒤이고 C1 exit 2us/target 5us, C2 exit 90us/target 200us, C3 exit 700us/target 1500us라고 가정한다.
- C1800us가 target 5us보다 길고 exit 2us를 충분히 감당하므로 가능하다.
- C2800us가 target 200us보다 길고 90us exit 뒤에도 약 710us 여유가 있어 가능하다.
- C3target residency 1500us를 채우지 못하고 700us exit는 scheduler latency budget도 거의 소모하므로 제외한다.
- choicegovernor는 energy와 최근 실제 residency를 고려해 C1/C2 중 C2를 선택할 수 있다.
결론깊은 state 이름이 아니라 exit latency, minimum residency와 다음 deadline을 숫자로 비교해야 한다.
06 · DEEP DIVE
경계별 상세 분석
공통 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를 기대할 수 없다.
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를 본다.
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를 확인한다.
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를 기록한다.
객체 수명과 소유권을 먼저 고정한다
resume entry physical address, save area와 page table은 suspend 동안 DRAM retention 또는 reserved memory에 유지되어야 한다. firmware가 그 memory를 덮거나 전원을 끄면 복귀할 수 없다.
주소나 register 값이 맞는지만 확인하면 stale state를 놓친다. producer, publication, consumer와 폐기 지점을 같은 표에 기록한다.
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
실패를 어떤 증거로 나눌 것인가
| 분류 | 관찰되는 결과 | 첫 확인값 |
|---|---|---|
| arm64 | PSCI 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-64 | TSC 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-V | SBI 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
재현과 계측 절차
- cpuidle trace와 hardware counter로 requested state, actual residency, exit latency를 architecture별로 비교한다.
- suspend resume의 마지막 console 지점과 firmware trace를 맞춰 Linux와 firmware 중 어느 쪽에서 멈췄는지 분리한다.
- 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
- 소스만 읽고 끝내지 않고 최종
vmlinux의objdump -dr,readelf -SW결과로 선택된 alternative와 section 배치를 확인한다.
09 · REFERENCES
원문 좌표
- arm64arch/arm64/kernel/suspend.c:89-169
- x86-64arch/x86/kernel/acpi/cstate.c:211-251
- RISC-Varch/riscv/kernel/suspend.c:57-125
Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.