From f116204c5fece3ce802acc5b9045c0a92b18c58c Mon Sep 17 00:00:00 2001 From: Drew Fustini Date: Tue, 19 Aug 2025 02:40:21 -0700 Subject: [PATCH] riscv: Add sysctl to control discard of vstate on syscall entry Vector registers are always clobbered in the syscall entry path to enforce the documented ABI that vector state is not preserved across syscalls. However, this operation can be slow on some RISC-V cores. To mitigate this performance impact, add a sysctl knob to control whether vector state is discarded in the syscall entry path: /proc/sys/abi/riscv_v_vstate_discard Valid values are: 0: Vector state is not intentionally clobbered when entering a syscall 1: Vector state is always clobbered when entering a syscall The initial state is controlled by CONFIG_RISCV_ISA_V_VSTATE_DISCARD. Fixes: 9657e9b7d253 ("riscv: Discard vector state on syscalls") Signed-off-by: Drew Fustini Signed-off-by: Linux RISC-V bot --- Documentation/arch/riscv/vector.rst | 27 +++++++++++++++++++++++++-- arch/riscv/Kconfig | 20 ++++++++++++++++++++ arch/riscv/include/asm/vector.h | 4 ++++ arch/riscv/kernel/vector.c | 16 +++++++++++++++- 4 files changed, 64 insertions(+), 3 deletions(-) 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; }