diff --git a/Documentation/arch/riscv/vector.rst b/Documentation/arch/riscv/vector.rst index 3987f5f76a9deb..2a6b52990ee75a 100644 --- a/Documentation/arch/riscv/vector.rst +++ b/Documentation/arch/riscv/vector.rst @@ -134,7 +134,30 @@ processes in form of sysctl knob: 3. Vector Register State Across System Calls --------------------------------------------- -As indicated by version 1.0 of the V extension [1], vector registers are -clobbered by system calls. +Linux adopts the syscall ABI proposed by version 1.0 of the V extension [1], +where vector registers are clobbered by system calls. Specifically: + + Executing a system call causes all caller-saved vector registers + (v0-v31, vl, vtype) and vstart to become unspecified. + +Linux clobbers the vector registers (e.g. discards vector state) on the syscall +entry path. This is done to identify userspace programs that mistakenly expect +vector registers to be preserved across syscalls. This can be helpful for +debugging and testing. However, clobbering vector state can negatively impact +performance on some RISC-V implementations, and is not strictly necessary. + +To mitigate this performance impact, a sysctl knob is provided that controls +whether vector state is always clobbered on syscall entry: + +* /proc/sys/abi/riscv_v_vstate_discard + + Valid values are: + + * 0: Vector state is not always clobbered in all syscalls + * 1: Mandatory clobbering of vector state in all syscalls + + Reading this file returns the current discard behavior. Write to '0' or '1' + to file to change the current behavior. The initial state is controlled by + CONFIG_RISCV_ISA_V_VSTATE_DISCARD. 1: https://github.com/riscv/riscv-v-spec/blob/master/calling-convention.adoc diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a4b233a0659ed8..5009bee5c77a15 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -654,6 +654,26 @@ config RISCV_ISA_V_DEFAULT_ENABLE If you don't know what to do here, say Y. +config RISCV_ISA_V_VSTATE_DISCARD + bool "Enable Vector state discard by default" + depends on RISCV_ISA_V + default n + help + Discarding vector state (also known as clobbering) on syscall entry + can help identify userspace programs that are mistakenly relying on + vector registers being preserved across syscalls. This can be useful + for debugging and testing. However, this behavior can negatively + impact performance on some RISC-V implementations and is not strictly + necessary. + + Select Y here if you want mandatory clobbering of vector state even + though it can increase the duration of syscalls on some RISC-V cores. + If you don't know what to do, then select N. + + This choice sets the initial value of the abi.riscv_v_vstate_discard + sysctl. Regardless of whether you choose Y or N, the sysctl can still + be changed by the user while the system is running. + config RISCV_ISA_V_UCOPY_THRESHOLD int "Threshold size for vectorized user copies" depends on RISCV_ISA_V diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index b61786d43c2054..9d236e456d608f 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -40,6 +40,7 @@ _res; \ }) +extern bool riscv_v_vstate_discard_ctl; extern unsigned long riscv_v_vsize; int riscv_v_setup_vsize(void); bool insn_is_vector(u32 insn_buf); @@ -270,6 +271,9 @@ static inline void __riscv_v_vstate_discard(void) { unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1); + if (READ_ONCE(riscv_v_vstate_discard_ctl) == 0) + return; + riscv_v_enable(); if (has_xtheadvector()) asm volatile (THEAD_VSETVLI_T4X0E8M8D1 : : : "t4"); diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c index 184f780c932d44..7a4c209ad337ef 100644 --- a/arch/riscv/kernel/vector.c +++ b/arch/riscv/kernel/vector.c @@ -26,6 +26,7 @@ static struct kmem_cache *riscv_v_user_cachep; static struct kmem_cache *riscv_v_kernel_cachep; #endif +bool riscv_v_vstate_discard_ctl = IS_ENABLED(CONFIG_RISCV_ISA_V_VSTATE_DISCARD); unsigned long riscv_v_vsize __read_mostly; EXPORT_SYMBOL_GPL(riscv_v_vsize); @@ -307,11 +308,24 @@ static const struct ctl_table riscv_v_default_vstate_table[] = { }, }; +static const struct ctl_table riscv_v_vstate_discard_table[] = { + { + .procname = "riscv_v_vstate_discard", + .data = &riscv_v_vstate_discard_ctl, + .maxlen = sizeof(riscv_v_vstate_discard_ctl), + .mode = 0644, + .proc_handler = proc_dobool, + }, +}; + static int __init riscv_v_sysctl_init(void) { - if (has_vector() || has_xtheadvector()) + if (has_vector() || has_xtheadvector()) { if (!register_sysctl("abi", riscv_v_default_vstate_table)) return -EINVAL; + if (!register_sysctl("abi", riscv_v_vstate_discard_table)) + return -EINVAL; + } return 0; }