이 코드는 어떤 문제를 푸나요?
정수 레지스터를 바꿔도 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와 소유자 정보로 복원을 피할 수 있는 조건을 검토해 보십시오.
그림으로 보는 변화
TIF_NEED_FPU_LOAD를 먼저 설정하고 FP 상태를 저장한 뒤 last_cpu를 기록합니다. 거짓이면 세 작업을 모두 건너뜁니다. 고정 원문 34–53행.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
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고