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

arm64/sve: Rework SVE access trap to convert state in registers

When we enable SVE usage in userspace after taking a SVE access trap we
need to ensure that the portions of the register state that are not
shared with the FPSIMD registers are zeroed. Currently we do this by
forcing the FPSIMD registers to be saved to the task struct and converting
them there. This is wasteful in the common case where the task state is
loaded into the registers and we will immediately return to userspace
since we can initialise the SVE state directly in registers instead of
accessing multiple copies of the register state in memory.

Instead in that common case do the conversion in the registers and
update the task metadata so that we can return to userspace without
spilling the register state to memory unless there is some other reason
to do so.

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

authored by

Mark Brown and committed by
Catalin Marinas
cccb78ce 68f638a4

+23 -9
+1
arch/arm64/include/asm/fpsimd.h
··· 73 73 extern void sve_load_from_fpsimd_state(struct user_fpsimd_state const *state, 74 74 unsigned long vq_minus_1); 75 75 extern unsigned int sve_get_vl(void); 76 + extern void sve_set_vq(unsigned long vq_minus_1); 76 77 77 78 struct arm64_cpu_capabilities; 78 79 extern void sve_kernel_enable(const struct arm64_cpu_capabilities *__unused);
+5
arch/arm64/kernel/entry-fpsimd.S
··· 48 48 ret 49 49 SYM_FUNC_END(sve_get_vl) 50 50 51 + SYM_FUNC_START(sve_set_vq) 52 + sve_load_vq x0, x1, x2 53 + ret 54 + SYM_FUNC_END(sve_set_vq) 55 + 51 56 /* 52 57 * Load SVE state from FPSIMD state. 53 58 *
+17 -9
arch/arm64/kernel/fpsimd.c
··· 926 926 * Trapped SVE access 927 927 * 928 928 * Storage is allocated for the full SVE state, the current FPSIMD 929 - * register contents are migrated across, and TIF_SVE is set so that 930 - * the SVE access trap will be disabled the next time this task 931 - * reaches ret_to_user. 929 + * register contents are migrated across, and the access trap is 930 + * disabled. 932 931 * 933 932 * TIF_SVE should be clear on entry: otherwise, fpsimd_restore_current_state() 934 933 * would have disabled the SVE access trap for userspace during ··· 945 946 946 947 get_cpu_fpsimd_context(); 947 948 948 - fpsimd_save(); 949 - 950 - /* Force ret_to_user to reload the registers: */ 951 - fpsimd_flush_task_state(current); 952 - 953 - fpsimd_to_sve(current); 954 949 if (test_and_set_thread_flag(TIF_SVE)) 955 950 WARN_ON(1); /* SVE access shouldn't have trapped */ 951 + 952 + /* 953 + * Convert the FPSIMD state to SVE, zeroing all the state that 954 + * is not shared with FPSIMD. If (as is likely) the current 955 + * state is live in the registers then do this there and 956 + * update our metadata for the current task including 957 + * disabling the trap, otherwise update our in-memory copy. 958 + */ 959 + if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) { 960 + sve_set_vq(sve_vq_from_vl(current->thread.sve_vl) - 1); 961 + sve_flush_live(); 962 + fpsimd_bind_task_to_cpu(); 963 + } else { 964 + fpsimd_to_sve(current); 965 + } 956 966 957 967 put_cpu_fpsimd_context(); 958 968 }