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와 소유자 정보로 복원을 피할 수 있는 조건을 검토해 보십시오.

그림으로 보는 변화

FPU, SIMD, SVE/SME, XSAVE와 RISC-V Vector state의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 현재 상태 확인

TIF_NEED_FPU_LOAD와 하드웨어 지원, task 종류를 검사합니다.

화살표는 저장 필요 여부를 고르는 조건 분기입니다.

2. 메모리에 저장

실제 레지스터 값을 task의 FP 저장 영역에 기록합니다.

CPU에서 메모리로 향하는 화살표입니다.

3. 향후 복원 판단

복원 필요 표식과 마지막 CPU 번호를 남깁니다.

표식은 데이터를 복사하는 대신 후속 경로의 판단 근거가 됩니다.

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

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

맨 위로 ↑