Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64/sme: Implement SVCR context switching

In SME the use of both streaming SVE mode and ZA are tracked through
PSTATE.SM and PSTATE.ZA, visible through the system register SVCR. In
order to context switch the floating point state for SME we need to
context switch the contents of this register as part of context
switching the floating point state.

Since changing the vector length exits streaming SVE mode and disables
ZA we also make sure we update SVCR appropriately when setting vector
length, and similarly ensure that new threads have streaming SVE mode
and ZA disabled.

Signed-off-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20220419112247.711548-14-broonie@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Mark Brown and committed by
Catalin Marinas
b40c559b a9d69158

+29 -3
+2 -1
arch/arm64/include/asm/fpsimd.h
··· 46 46 extern void fpsimd_update_current_state(struct user_fpsimd_state const *state); 47 47 48 48 extern void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *state, 49 - void *sve_state, unsigned int sve_vl); 49 + void *sve_state, unsigned int sve_vl, 50 + u64 *svcr); 50 51 51 52 extern void fpsimd_flush_task_state(struct task_struct *target); 52 53 extern void fpsimd_save_and_flush_cpu_state(void);
+1
arch/arm64/include/asm/processor.h
··· 169 169 u64 mte_ctrl; 170 170 #endif 171 171 u64 sctlr_user; 172 + u64 svcr; 172 173 u64 tpidr2_el0; 173 174 }; 174 175
+1
arch/arm64/include/asm/thread_info.h
··· 82 82 #define TIF_SVE_VL_INHERIT 24 /* Inherit SVE vl_onexec across exec */ 83 83 #define TIF_SSBD 25 /* Wants SSB mitigation */ 84 84 #define TIF_TAGGED_ADDR 26 /* Allow tagged user addresses */ 85 + #define TIF_SME 27 /* SME in use */ 85 86 #define TIF_SME_VL_INHERIT 28 /* Inherit SME vl_onexec across exec */ 86 87 87 88 #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
+17 -1
arch/arm64/kernel/fpsimd.c
··· 121 121 struct fpsimd_last_state_struct { 122 122 struct user_fpsimd_state *st; 123 123 void *sve_state; 124 + u64 *svcr; 124 125 unsigned int sve_vl; 125 126 }; 126 127 ··· 360 359 WARN_ON(!system_supports_fpsimd()); 361 360 WARN_ON(!have_cpu_fpsimd_context()); 362 361 362 + if (IS_ENABLED(CONFIG_ARM64_SME) && test_thread_flag(TIF_SME)) 363 + write_sysreg_s(current->thread.svcr, SYS_SVCR_EL0); 364 + 363 365 if (IS_ENABLED(CONFIG_ARM64_SVE) && test_thread_flag(TIF_SVE)) { 364 366 sve_set_vq(sve_vq_from_vl(task_get_sve_vl(current)) - 1); 365 367 sve_load_state(sve_pffr(&current->thread), ··· 393 389 394 390 if (test_thread_flag(TIF_FOREIGN_FPSTATE)) 395 391 return; 392 + 393 + if (IS_ENABLED(CONFIG_ARM64_SME) && 394 + test_thread_flag(TIF_SME)) { 395 + u64 *svcr = last->svcr; 396 + *svcr = read_sysreg_s(SYS_SVCR_EL0); 397 + } 396 398 397 399 if (IS_ENABLED(CONFIG_ARM64_SVE) && 398 400 test_thread_flag(TIF_SVE)) { ··· 750 740 fpsimd_flush_task_state(task); 751 741 if (test_and_clear_tsk_thread_flag(task, TIF_SVE)) 752 742 sve_to_fpsimd(task); 743 + 744 + if (system_supports_sme() && type == ARM64_VEC_SME) 745 + task->thread.svcr &= ~(SYS_SVCR_EL0_SM_MASK | 746 + SYS_SVCR_EL0_ZA_MASK); 753 747 754 748 if (task == current) 755 749 put_cpu_fpsimd_context(); ··· 1418 1404 last->st = &current->thread.uw.fpsimd_state; 1419 1405 last->sve_state = current->thread.sve_state; 1420 1406 last->sve_vl = task_get_sve_vl(current); 1407 + last->svcr = &current->thread.svcr; 1421 1408 current->thread.fpsimd_cpu = smp_processor_id(); 1422 1409 1423 1410 if (system_supports_sve()) { ··· 1433 1418 } 1434 1419 1435 1420 void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *st, void *sve_state, 1436 - unsigned int sve_vl) 1421 + unsigned int sve_vl, u64 *svcr) 1437 1422 { 1438 1423 struct fpsimd_last_state_struct *last = 1439 1424 this_cpu_ptr(&fpsimd_last_state); ··· 1442 1427 WARN_ON(!in_softirq() && !irqs_disabled()); 1443 1428 1444 1429 last->st = st; 1430 + last->svcr = svcr; 1445 1431 last->sve_state = sve_state; 1446 1432 last->sve_vl = sve_vl; 1447 1433 }
+2
arch/arm64/kernel/process.c
··· 310 310 dst->thread.sve_state = NULL; 311 311 clear_tsk_thread_flag(dst, TIF_SVE); 312 312 313 + dst->thread.svcr = 0; 314 + 313 315 /* clear any pending asynchronous tag fault raised by the parent */ 314 316 clear_tsk_thread_flag(dst, TIF_MTE_ASYNC_FAULT); 315 317
+6 -1
arch/arm64/kvm/fpsimd.c
··· 109 109 WARN_ON_ONCE(!irqs_disabled()); 110 110 111 111 if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) { 112 + /* 113 + * Currently we do not support SME guests so SVCR is 114 + * always 0 and we just need a variable to point to. 115 + */ 112 116 fpsimd_bind_state_to_cpu(&vcpu->arch.ctxt.fp_regs, 113 117 vcpu->arch.sve_state, 114 - vcpu->arch.sve_max_vl); 118 + vcpu->arch.sve_max_vl, 119 + NULL); 115 120 116 121 clear_thread_flag(TIF_FOREIGN_FPSTATE); 117 122 update_thread_flag(TIF_SVE, vcpu_has_sve(vcpu));