Linux v6.18.37 · 개념과 코드 읽기

CPU idle, suspend와 resume context

CPU_ON, WFI, suspend와 태스크 wakeup의 차이

꺼진 CPU를 새로 시작하는 CPU_ON은 다른 CPU가 secondary_entry의 물리 주소를 넘기는 경로입니다. CPU 상태를 잃는 suspend는 해당 CPU가 저장한 상태를 cpu_resume에서 복구하는 경로입니다. 얕은 WFI 복귀와 스케줄러의 태스크 wakeup도 각각 구분해야 합니다. PSCI의 성공 반환과 Linux online 완료는 같은 사건이 아닙니다.

양쪽 코드를 이어서 읽기

이 코드는 어떤 문제를 푸나요?

CPU가 깊이 잠들면 레지스터와 타이머·주소 변환 설정 일부가 사라질 수 있습니다. arm64의 PSCI/저장 경로, x86의 ACPI 및 플랫폼 상태, RISC-V의 SBI와 CSR 저장을 같은 것으로 취급할 수 없습니다. 대표로 RISC-V의 제어 레지스터 저장 함수를 읽습니다.

읽을 범위: v6.18.37 · arch/riscv/kernel/suspend.c · suspend_save_csrs 15–42행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

보존과 재초기화

RAM에 저장할 수 있는 상태와 하드웨어·펌웨어가 다시 만들어야 할 상태를 구분합니다.

SSTC

지원되는 환경에서 S-mode가 타이머 비교 레지스터를 직접 다룰 수 있게 하는 확장입니다.

pending과 enable

인터럽트 발생 중임을 나타내는 pending은 외부 장치·펌웨어가 바꿀 수 있습니다. 커널이 저장한 옛 값으로 임의 복원할 대상과 다릅니다.

처음 읽을 때

CPU가 잠들어도 남아 있는 RAM과 사라질 수 있는 레지스터를 나누어 그려 보십시오.

더 깊이 살펴볼 때

RV32에서 64비트 timer 값을 나누어 다루는 이유와 CSR_IP를 저장하지 않는 근거를 검토하십시오.

그림으로 보는 변화

CPU idle, suspend와 resume context의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 절전 전 상태 읽기

예외 벡터와 인터럽트 허용 상태를 context에 기록합니다.

CPU에서 RAM으로 향하는 화살표는 저장입니다.

2. 선택 기능 저장

SSTC와 MMU가 있으면 timer 비교값과 SATP를 저장합니다.

조건부 화살표는 기능과 빌드 설정에 따른 차이입니다.

3. 복귀 경로에서 사용

별도 복원 함수가 저장값으로 실행 환경을 재구성합니다.

저장 함수 자체가 CPU를 잠들게 하는 것은 아닙니다.

suspend_save_csrs를 한 줄씩 읽기

줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.

void suspend_save_csrs(struct suspend_context *context)
{
	if (riscv_has_extension_unlikely(RISCV_ISA_EXT_XLINUXENVCFG))
		context->envcfg = csr_read(CSR_ENVCFG);
	context->tvec = csr_read(CSR_TVEC);
	context->ie = csr_read(CSR_IE);

	/*
	 * No need to save/restore IP CSR (i.e. MIP or SIP) because:
	 *
	 * 1. For no-MMU (M-mode) kernel, the bits in MIP are set by
	 *    external devices (such as interrupt controller, timer, etc).
	 * 2. For MMU (S-mode) kernel, the bits in SIP are set by
	 *    M-mode firmware and external devices (such as interrupt
	 *    controller, etc).
	 */

#ifdef CONFIG_MMU
	if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SSTC)) {
		context->stimecmp = csr_read(CSR_STIMECMP);
#if __riscv_xlen < 64
		context->stimecmph = csr_read(CSR_STIMECMPH);
#endif
	}

	context->satp = csr_read(CSR_SATP);
#endif
}
void suspend_save_csrs(struct suspend_context *context)

절전 복귀에 사용할 제어 레지스터 저장 영역 context를 받습니다. 이 함수 자체가 잠드는 명령을 실행하지는 않습니다.

	if (riscv_has_extension_unlikely(RISCV_ISA_EXT_XLINUXENVCFG))

ENVCFG 상태를 사용하는 커널의 기능 조건이 충족되는지 검사합니다.

		context->envcfg = csr_read(CSR_ENVCFG);

지원되는 경우 ENVCFG를 저장합니다. 없는 CSR을 무조건 읽지 않도록 조건을 따릅니다.

	context->tvec = csr_read(CSR_TVEC);

예외가 도착할 벡터 위치를 저장합니다. 복귀 후 정상 trap 처리를 다시 연결하는 데 필요합니다.

	context->ie = csr_read(CSR_IE);

인터럽트 허용 비트를 보존합니다. 외부 요청의 pending 상태 자체를 이곳에서 저장하는 것은 아닙니다.

#ifdef CONFIG_MMU

MMU를 사용하는 빌드에서만 다음 timer·SATP 저장 코드를 포함합니다. 이는 런타임 if와 다른 전처리 조건입니다.

	if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SSTC)) {

SSTC 확장이 있으면 supervisor timer 비교 레지스터도 저장합니다.

		context->stimecmp = csr_read(CSR_STIMECMP);

다음 timer event와 관련된 비교값을 읽습니다. RV32에서는 아래에서 상위 절반도 읽습니다.

#if __riscv_xlen < 64

RISC-V 레지스터 폭이 64비트 미만인 빌드 조건을 선택합니다.

		context->stimecmph = csr_read(CSR_STIMECMPH);

32비트 CSR에 담기지 않는 timer 비교값의 상위 부분을 따로 저장합니다.

#endif

RV32 전용 부분의 끝입니다. RV64에는 이 상위 CSR 저장이 포함되지 않습니다.

	context->satp = csr_read(CSR_SATP);

주소 변환 루트·ASID·모드를 담는 SATP 값을 보존합니다.

함께 생각해 볼 질문

WFI와 system suspend가 같습니까?

아닙니다. 보존되는 상태와 플랫폼 작업 범위가 다릅니다.

pending 비트도 예전 값으로 복원합니까?

이 함수는 그렇게 하지 않습니다. 외부 장치와 펌웨어가 발생 상태를 관리하기 때문입니다.

SATP만 저장하면 모든 task 상태가 보존됩니까?

아닙니다. 일반 레지스터와 다른 플랫폼 상태는 별도 경로에서 처리합니다.

출처와 읽은 범위

Linux stable v6.18.37 · arch/riscv/kernel/suspend.c

해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고

맨 위로 ↑