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

FPU, SIMD, SVE/SME, XSAVE와 RISC-V Vector state

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

정수 레지스터를 바꿔도 SIMD와 벡터 레지스터에 이전 task의 계산값이 남을 수 있습니다. arm64는 FPSIMD·SVE·SME, x86은 FPU/XSAVE 상태, RISC-V는 FP 및 Vector 상태를 별도로 관리합니다. 대표로 x86 switch_fpu에서 언제 저장이 필요한지 읽습니다.

읽을 범위: v6.18.37 · arch/x86/include/asm/fpu/sched.h · switch_fpu 32–53행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

상태 소유자

현재 CPU의 FP 레지스터가 어느 task의 값인지를 추적합니다. 메모리 사본과 실제 레지스터가 언제 같은지도 중요합니다.

지연 복원

정수 task 전환 직후 무조건 큰 상태를 복원하지 않고 실제 사용자 복귀 전에 복원할 수 있습니다. 이는 복원을 생략해도 된다는 뜻이 아닙니다.

상태 크기

SVE VL, SME ZA, x86 XFEATURE, RISC-V VLEN에 따라 달라집니다. 모두 고정 512바이트라고 가정하면 안 됩니다.

처음 읽을 때

메모리 사본과 CPU 레지스터 중 어디에 최신 값이 있는지 표시해 보십시오.

더 깊이 살펴볼 때

같은 CPU로 돌아왔을 때 last_cpu와 소유자 정보로 복원을 피할 수 있는 조건을 검토해 보십시오.

그림으로 보는 변화

x86 switch_fpu의 조건 참 경로: TIF_NEED_FPU_LOAD 설정, FP 상태 저장, last_cpu 기록 순서. 조건 거짓 경로는 세 작업을 모두 건너뜀.
세 조건이 모두 참이면 TIF_NEED_FPU_LOAD를 먼저 설정하고 FP 상태를 저장한 뒤 last_cpu를 기록합니다. 거짓이면 세 작업을 모두 건너뜁니다. 고정 원문 34–53행.
x86 switch_fpu 조건과 참·거짓 경로: 조건 또는 번호 검사

1. 조건을 검사해 분기

1. 조건을 검사해 분기

NEED_FPU_LOAD 미설정, FPU 지원, 일반 사용자 task 조건이 모두 참인지 확인합니다. 하나라도 거짓이면 이 if 본문의 표식·저장·CPU 기록을 건너뜁니다.

2. 표식부터 세운 후 저장

set_tsk_thread_flag(39행)가 save_fpregs_to_fpstate(40행)보다 먼저 실행됩니다. 역순으로 읽으면 안 됩니다.

3. 마지막 CPU 기록

참 경로에서는 저장 뒤 last_cpu = cpu(49행)를 기록합니다. 같은 CPU의 후속 복원 판단에 쓰이며 이 함수가 다음 task를 즉시 복원한다는 뜻은 아닙니다.

switch_fpu를 한 줄씩 읽기

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

static inline void switch_fpu(struct task_struct *old, int cpu)
{
	if (!test_tsk_thread_flag(old, TIF_NEED_FPU_LOAD) &&
	    cpu_feature_enabled(X86_FEATURE_FPU) &&
	    !(old->flags & (PF_KTHREAD | PF_USER_WORKER))) {
		struct fpu *old_fpu = x86_task_fpu(old);

		set_tsk_thread_flag(old, TIF_NEED_FPU_LOAD);
		save_fpregs_to_fpstate(old_fpu);
		/*
		 * The save operation preserved register state, so the
		 * fpu_fpregs_owner_ctx is still @old_fpu. Store the
		 * current CPU number in @old_fpu, so the next return
		 * to user space can avoid the FPU register restore
		 * when is returns on the same CPU and still owns the
		 * context. See fpregs_restore_userregs().
		 */
		old_fpu->last_cpu = cpu;

		trace_x86_fpu_regs_deactivated(old_fpu);
	}
}
static inline void switch_fpu(struct task_struct *old, int cpu)

이전 task와 현재 CPU 번호를 받아 전환 시 FP 저장이 필요한지 판단합니다. 이 함수는 다음 task의 상태를 무조건 즉시 복원하는 함수가 아닙니다.

	if (!test_tsk_thread_flag(old, TIF_NEED_FPU_LOAD) &&

이전 task에 이미 NEED_FPU_LOAD가 설정되어 있다면 현재 레지스터를 그 task의 최신 값으로 가정할 수 없습니다. 플래그가 없는 경우만 다음 조건을 확인합니다.

	    cpu_feature_enabled(X86_FEATURE_FPU) &&

실제 FPU 지원이 있는지도 검사합니다. 소프트웨어 상태만 보고 없는 하드웨어에 접근하지 않게 합니다.

	    !(old->flags & (PF_KTHREAD | PF_USER_WORKER))) {

일반 사용자 FP 문맥 대상이 아닌 커널 스레드와 사용자 worker를 제외합니다. 세 조건이 모두 맞을 때 본문으로 들어갑니다.

		struct fpu *old_fpu = x86_task_fpu(old);

이전 task의 FP 저장 자료구조를 얻습니다. CPU 레지스터의 값을 보관할 메모리 쪽 대상입니다.

		set_tsk_thread_flag(old, TIF_NEED_FPU_LOAD);

이 task를 다음에 사용할 때 FP load 여부를 다시 판단해야 함을 표시합니다.

		save_fpregs_to_fpstate(old_fpu);

실제 FP 레지스터 내용을 해당 task의 메모리 저장 상태로 보존합니다.

		old_fpu->last_cpu = cpu;

저장 후에도 CPU 레지스터 내용이 보존되므로 마지막 CPU 번호를 기억합니다. 같은 CPU에서 소유권이 유지되면 이후 불필요한 복원을 피할 근거가 됩니다.

		trace_x86_fpu_regs_deactivated(old_fpu);

FP 레지스터 소유가 비활성화된 사건을 추적에 남깁니다. 상태를 추가로 복원하는 명령은 아닙니다.

함께 생각해 볼 질문

복원 필요 플래그가 있으면 레지스터가 이미 그 task 값입니까?

그렇게 가정하면 안 됩니다. 사용 전에 소유자와 복원 조건을 확인해야 합니다.

커널 스레드는 항상 사용자 FP 상태를 저장합니까?

아닙니다. 이 함수는 PF_KTHREAD와 PF_USER_WORKER를 조건에서 제외합니다.

큰 벡터는 계산 성능에만 영향을 줍니까?

아닙니다. 전환, 시그널, 디버깅에 복사할 상태 크기도 커집니다.

출처와 읽은 범위

Linux stable v6.18.37 · arch/x86/include/asm/fpu/sched.h

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

맨 위로 ↑