Linux-RISC-V Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/8] riscv: optimize mode switch latency for Vector
@ 2026-09-18 21:51 Andy Chiu
  2026-09-18 21:51 ` [PATCH v6 1/8] riscv: do not read csr in vector context switch Andy Chiu
                   ` (9 more replies)
  0 siblings, 10 replies; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	linux-riscv
  Cc: andybnac, Andy Chiu, dfustini, greentime.hu, linux-kernel, olof

This series provide several optimizations targeting system call latency
regarding vector context management. Before the series, the kernel
handled user's vector context in an conservative way, where registers
were null out and VS is tracked as DIRTY. This introduce excess context
saving and restoring when there is a context swicth.  Also, the kernel
turned off Vector at the exception entry, making all in-kernel vector
usecase take the serialization cost, which includes context switch and
user copies. The cost is not easy to hide as vector code are usually sit
right after enabling V.

Since vector register are set to a known state at syscall exit, the
series set VS to INIT at syscall entry and null out the vector register
at the exit, skipping unnecessary saves and restores. The series also
introduce riscv_novstateopt, when unset, enables vector in the kernel
mode, and do not perform register nulling on the syscall fast path,
where there is no context switch or kernel-mode vector during the
syscall.

With the whole series, nginx request throughput vs base on a four-core
Ascalon-S, by served page size (* = statistically distinct):

       86B     1KB     2KB     4KB     8KB     16KB    32KB
  v5   +0.79%* +1.36%* +2.33%* +2.02%* +0.14%  +0.93%  +2.42%*
  v6   +0.33%* -0.23%  +2.88%* +2.19%* +1.02%  +1.14%  +1.40%*

This series depends on [1], which is now queued in the KVM RISC-V tree
[2]. For those who prefer git, the series is also available at [3].

Patch summary:
 - New patches: 2
 - Modified patches: 7, 8
 - Unchanged patches: 1, 3, 4, 5, 6

Changelog v6:
 - Refactor the context switch of preemptible kernel-mode vector (2)
 - Address checkpatch warnings by reformatting patch 7
 - Drive the optimization with an alternative instead of
   CONFIG_RISCV_VSTATE_OPT, enable it by default (8)
 - Link to v5: https://lore.kernel.org/all/20260810172255.1532787-1-tchiu@tenstorrent.com/

Changelog v5:
 - Rebase on top of the kvm fix
 - Do not read sstatus in vector context swicth (1)
 - Add a test for vectorized user copy (2)
 - Enable vector in the kernel-mode and skip nulling at syscall fast
   path, gurad the optimization in a new config (7)
 - Link to v4: https://patchwork.kernel.org/project/linux-riscv/cover/20260528190927.886558-1-tchiu@tenstorrent.com/

Changelog v4:
 - Fix a build warning (1)
 - Prevent setting INIT when it is already and provide performance
   meassurements (2)
 - Address comments from sashiko (4)
 - Link to v3: https://lore.kernel.org/all/20260521162521.188629-1-tchiu@tenstorrent.com/

Changelog v3:
 - Refactor function names. (1, 2)
 - Merge daichengrong's patch, with a fix and optimzation. (2)
 - Fix ptrace GETREGSET failure. (3)
 - Strengthen ptrace SETREGSET semantics and add a test to cover it. (3,
   4)
 - Fix a potential ABI break in signal and add a test to prevent future
   breaks. (3, 4)
 - Link to v2: https://lore.kernel.org/linux-riscv/20260402043414.2421916-1-andybnac@gmail.com/

Changelog v2: rebase on top of for-next

[1] [PATCH v5 0/3] RISC-V: KVM: fix vcpu vector context handling
    https://lore.kernel.org/all/20260803215250.824417-1-tchiu@tenstorrent.com/
[2] https://github.com/kvm-riscv/linux/tree/riscv_kvm_next
[3] https://github.com/tchiu-TT/linux/commits/vctxopt/v6/

Andy Chiu (7):
  riscv: do not read csr in vector context switch
  riscv: vector: refactor context switch for kernel-mode vector
  selftest: riscv: test vectorized user copy
  riscv: vector: refactor vector context operations
  riscv: vector: adjust ptrace and signal behavior for INITIAL state
  selftests: riscv: Extend vector tests for sigreturn and ptrace
  riscv: vector: optimize vstate operations

daichengrong (1):
  riscv: clarify vector state semantics on syscall and context switch

 .../admin-guide/kernel-parameters.txt         |  13 ++
 arch/riscv/include/asm/alternative-macros.h   |   6 +
 arch/riscv/include/asm/alternative.h          |   3 -
 arch/riscv/include/asm/cpufeature-macros.h    |  12 +-
 arch/riscv/include/asm/kvm_vcpu_vector.h      |   8 +-
 arch/riscv/include/asm/vector.h               |  78 ++++---
 arch/riscv/kernel/cpufeature.c                |  19 ++
 arch/riscv/kernel/entry.S                     |  15 +-
 arch/riscv/kernel/kernel_mode_vector.c        |  13 +-
 arch/riscv/kernel/process.c                   |   3 +
 arch/riscv/kernel/ptrace.c                    |  13 +-
 arch/riscv/kernel/signal.c                    |  11 +-
 arch/riscv/kernel/vector.c                    |  56 ++++-
 arch/riscv/kvm/vcpu.c                         |   2 +-
 arch/riscv/kvm/vcpu_vector.c                  |   6 +-
 .../selftests/riscv/sigreturn/sigreturn.c     |  69 ++++++
 tools/testing/selftests/riscv/vector/Makefile |   6 +-
 .../selftests/riscv/vector/v_uaccess_stress.c | 221 ++++++++++++++++++
 .../selftests/riscv/vector/vstate_ptrace.c    | 111 ++++++++-
 19 files changed, 598 insertions(+), 67 deletions(-)
 create mode 100644 tools/testing/selftests/riscv/vector/v_uaccess_stress.c


base-commit: c93809c637bd1fb1e0d0f0168b53cfe1c4767d21
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v6 1/8] riscv: do not read csr in vector context switch
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
@ 2026-09-18 21:51 ` Andy Chiu
  2026-10-06 10:01   ` Paul Walmsley
  2026-09-18 21:51 ` [PATCH v6 2/8] riscv: vector: refactor context switch for kernel-mode vector Andy Chiu
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	linux-riscv
  Cc: andybnac, Andy Chiu, dfustini, greentime.hu, daichengrong,
	Yong-Xuan Wang

CSR operation can be costly as it may introduces serialization. Instead
of reading from sstatus.vs to the detect voluntary context switch in
kernel-mode vector, we can read the context depth from riscv_v_flags, as
it is always non-zero on a trap-introduced context switch.

Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v5:
 - new patch since v5
---
 arch/riscv/include/asm/vector.h        | 3 +--
 arch/riscv/kernel/kernel_mode_vector.c | 8 --------
 2 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index fffe72a77208..fccf9edb4e6a 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -377,8 +377,7 @@ static inline void __switch_to_vector(struct task_struct *prev,
 	struct pt_regs *regs;
 
 	if (riscv_preempt_v_started(prev)) {
-		if (riscv_v_is_on()) {
-			WARN_ON(prev->thread.riscv_v_flags & RISCV_V_CTX_DEPTH_MASK);
+		if (!(current->thread.riscv_v_flags & RISCV_V_CTX_DEPTH_MASK)) {
 			riscv_v_disable();
 			prev->thread.riscv_v_flags |= RISCV_PREEMPT_V_IN_SCHEDULE;
 		}
diff --git a/arch/riscv/kernel/kernel_mode_vector.c b/arch/riscv/kernel/kernel_mode_vector.c
index 77e98b504485..5ad93ddf6a10 100644
--- a/arch/riscv/kernel/kernel_mode_vector.c
+++ b/arch/riscv/kernel/kernel_mode_vector.c
@@ -182,14 +182,6 @@ static int riscv_v_start_kernel_context(void)
 	get_cpu_vector_context();
 	__riscv_flush_vector_context();
 	put_cpu_vector_context();
-	/*
-	 *  A voluntary context switch caused by put_cpu_vector_context() can
-	 *  raise the NEED_RESTORE flag if preempt_v starts too early due to a
-	 *  failed risv_v_is_on() check.
-	 *
-	 *  This causes the next context_nesting_end pollute the v-reg from
-	 *  the stale context memory in kernel-mode vector.
-	 */
 	riscv_v_start(RISCV_PREEMPT_V);
 	return 0;
 }
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 2/8] riscv: vector: refactor context switch for kernel-mode vector
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
  2026-09-18 21:51 ` [PATCH v6 1/8] riscv: do not read csr in vector context switch Andy Chiu
@ 2026-09-18 21:51 ` Andy Chiu
  2026-10-06 10:02   ` Paul Walmsley
  2026-09-18 21:51 ` [PATCH v6 3/8] selftest: riscv: test vectorized user copy Andy Chiu
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	linux-riscv
  Cc: andybnac, Andy Chiu, dfustini, greentime.hu, daichengrong,
	Yong-Xuan Wang

When context switching out a thread in preemptible kernel-mode vector,
there are 2 distinct cases: One is voluntary switch and the other is
trap-introduced switch. We don't need to save vector context for
voluntary switch and the context is always !dirty as long as we clear
the dirty bit at riscv_v_context_nesting_end().

Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v6:
 - new patch since v6
---
 arch/riscv/include/asm/vector.h        | 5 +++--
 arch/riscv/kernel/kernel_mode_vector.c | 1 +
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index fccf9edb4e6a..6aa125d66ace 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -378,10 +378,11 @@ static inline void __switch_to_vector(struct task_struct *prev,
 
 	if (riscv_preempt_v_started(prev)) {
 		if (!(current->thread.riscv_v_flags & RISCV_V_CTX_DEPTH_MASK)) {
+			/* Voluntary schedule(): nesting_end closed any dirty. */
+			WARN_ON(riscv_preempt_v_dirty(prev));
 			riscv_v_disable();
 			prev->thread.riscv_v_flags |= RISCV_PREEMPT_V_IN_SCHEDULE;
-		}
-		if (riscv_preempt_v_dirty(prev)) {
+		} else if (riscv_preempt_v_dirty(prev)) {
 			__riscv_v_vstate_save(&prev->thread.kernel_vstate,
 					      prev->thread.kernel_vstate.datap);
 			riscv_preempt_v_clear_dirty(prev);
diff --git a/arch/riscv/kernel/kernel_mode_vector.c b/arch/riscv/kernel/kernel_mode_vector.c
index 5ad93ddf6a10..b9481a3e40e3 100644
--- a/arch/riscv/kernel/kernel_mode_vector.c
+++ b/arch/riscv/kernel/kernel_mode_vector.c
@@ -219,6 +219,7 @@ asmlinkage void riscv_v_context_nesting_end(struct pt_regs *regs)
 			__riscv_v_vstate_clean(regs);
 			riscv_preempt_v_reset_flags();
 		}
+		riscv_preempt_v_clear_dirty(current);
 	}
 }
 #else
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 3/8] selftest: riscv: test vectorized user copy
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
  2026-09-18 21:51 ` [PATCH v6 1/8] riscv: do not read csr in vector context switch Andy Chiu
  2026-09-18 21:51 ` [PATCH v6 2/8] riscv: vector: refactor context switch for kernel-mode vector Andy Chiu
@ 2026-09-18 21:51 ` Andy Chiu
  2026-10-06 10:02   ` Paul Walmsley
  2026-09-18 21:51 ` [PATCH v6 4/8] riscv: vector: refactor vector context operations Andy Chiu
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Shuah Khan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Nathan Chancellor, Nick Desaulniers,
	Bill Wendling, Justin Stitt, linux-kselftest, linux-riscv, llvm
  Cc: andybnac, Andy Chiu, dfustini, greentime.hu, Sergey Matyukevich,
	Zong Li, Yong-Xuan Wang

Add a test that pushes a 1KiB pseudo-random stream through a pipe, so
write() and read() copy it in and out of the kernel on the vectorized
uaccess path, and compares the result against the original. Both buffers
shift every iteration to sweep all relative alignments, and 16 processes
per online CPU keep the vector unit contended. The compare is a byte at a
time loop through volatile pointers rather than memcmp(), keeping it scalar
and independent of the user vector state.

Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v5:
 - new patch since v5
---
 tools/testing/selftests/riscv/vector/Makefile |   6 +-
 .../selftests/riscv/vector/v_uaccess_stress.c | 221 ++++++++++++++++++
 2 files changed, 226 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/riscv/vector/v_uaccess_stress.c

diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile
index 7e0017b3fb8b..0ed09b69dd8a 100644
--- a/tools/testing/selftests/riscv/vector/Makefile
+++ b/tools/testing/selftests/riscv/vector/Makefile
@@ -2,7 +2,8 @@
 # Copyright (C) 2021 ARM Limited
 # Originally tools/testing/arm64/abi/Makefile
 
-TEST_GEN_PROGS := v_initval vstate_prctl vstate_ptrace validate_v_ptrace
+TEST_GEN_PROGS := v_initval vstate_prctl vstate_ptrace validate_v_ptrace \
+		  v_uaccess_stress
 TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
 TEST_GEN_LIBS := v_helpers.c sys_hwprobe.c
 
@@ -36,4 +37,7 @@ $(OUTPUT)/vstate_ptrace: vstate_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_hel
 $(OUTPUT)/validate_v_ptrace: validate_v_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
 	$(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^
 
+$(OUTPUT)/v_uaccess_stress: v_uaccess_stress.c
+	$(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^
+
 EXTRA_CLEAN += $(TEST_GEN_OBJ)
diff --git a/tools/testing/selftests/riscv/vector/v_uaccess_stress.c b/tools/testing/selftests/riscv/vector/v_uaccess_stress.c
new file mode 100644
index 000000000000..d70e0387e246
--- /dev/null
+++ b/tools/testing/selftests/riscv/vector/v_uaccess_stress.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Stress the kernel's user copy path from several processes at once.
+ *
+ * Each child pushes a 1KiB pseudo-random byte stream through a pipe: write()
+ * copies it from user space into the kernel (copy_from_user()) and read()
+ * copies it back out (copy_to_user()). The round trip is repeated 512 times
+ * per child, and every iteration the buffers are shifted so that the copies
+ * cover all of the source/destination alignment combinations.
+ *
+ * On RISC-V those copies may be serviced by kernel-mode vector routines, so
+ * the payload is generated and verified with plain scalar loads and stores
+ * only. The comparison goes through volatile pointers, which keeps both GCC
+ * and clang from turning the loop into a vectorised compare and keeps the
+ * check independent of the user vector state the kernel is supposed to
+ * preserve. memcmp() is avoided for the same reason: the libc version is free
+ * to use vector instructions.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "kselftest_harness.h"
+
+#define ITERATIONS	32768
+#define STREAM_SIZE	1024
+#define MAX_SHIFT	16
+#define BUF_SIZE	(STREAM_SIZE + MAX_SHIFT)
+
+#define CONCURRENCY	16
+
+/* xorshift64*, so the payload does not depend on any libc or kernel helper. */
+static unsigned long prng_state;
+
+static unsigned long prng_next(void)
+{
+	prng_state ^= prng_state >> 12;
+	prng_state ^= prng_state << 25;
+	prng_state ^= prng_state >> 27;
+
+	return prng_state * 2685821657736338717UL;
+}
+
+static void fill_random(unsigned char *buf, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		buf[i] = (unsigned char)(prng_next() >> 24);
+}
+
+/*
+ * Scalar, byte at a time comparison. Returns the offset of the first
+ * mismatching byte, or len when the two streams are identical.
+ */
+static size_t scalar_diff(const unsigned char *a, const unsigned char *b,
+			  size_t len)
+{
+	const volatile unsigned char *va = a;
+	const volatile unsigned char *vb = b;
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (va[i] != vb[i])
+			break;
+	}
+
+	return i;
+}
+
+static ssize_t write_all(int fd, const unsigned char *buf, size_t len)
+{
+	size_t done = 0;
+
+	while (done < len) {
+		ssize_t ret = write(fd, buf + done, len - done);
+
+		if (ret < 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		done += ret;
+	}
+
+	return done;
+}
+
+static ssize_t read_all(int fd, unsigned char *buf, size_t len)
+{
+	size_t done = 0;
+
+	while (done < len) {
+		ssize_t ret = read(fd, buf + done, len - done);
+
+		if (ret < 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		if (ret == 0)
+			return -1;
+		done += ret;
+	}
+
+	return done;
+}
+
+/* Runs in the child. Returns the exit status to hand back to the parent. */
+static int child_loop(unsigned long seed)
+{
+	unsigned char src[BUF_SIZE], dst[BUF_SIZE];
+	int pipefd[2];
+	int i;
+
+	prng_state = seed;
+
+	if (pipe(pipefd)) {
+		fprintf(stderr, "pid %d: pipe() failed: %s\n",
+			getpid(), strerror(errno));
+		return 1;
+	}
+
+	for (i = 0; i < ITERATIONS; i++) {
+		/*
+		 * Walk the source and destination through every relative
+		 * alignment, including the case where both are equally
+		 * misaligned.
+		 */
+		unsigned char *in = src + (i % MAX_SHIFT);
+		unsigned char *out = dst + ((i / MAX_SHIFT) % MAX_SHIFT);
+		size_t off;
+
+		fill_random(in, STREAM_SIZE);
+		memset(out, 0, STREAM_SIZE);
+
+		if (write_all(pipefd[1], in, STREAM_SIZE) < 0) {
+			fprintf(stderr, "pid %d: iteration %d: write() failed: %s\n",
+				getpid(), i, strerror(errno));
+			return 1;
+		}
+
+		if (read_all(pipefd[0], out, STREAM_SIZE) < 0) {
+			fprintf(stderr, "pid %d: iteration %d: read() failed: %s\n",
+				getpid(), i, strerror(errno));
+			return 1;
+		}
+
+		off = scalar_diff(in, out, STREAM_SIZE);
+		if (off != STREAM_SIZE) {
+			fprintf(stderr,
+				"pid %d: iteration %d: mismatch at byte %zu of %d (in %p, out %p): expected 0x%02x, got 0x%02x\n",
+				getpid(), i, off, STREAM_SIZE, in, out,
+				in[off], out[off]);
+			return 1;
+		}
+	}
+
+	close(pipefd[0]);
+	close(pipefd[1]);
+
+	return 0;
+}
+
+static int nr_children(void)
+{
+	long online = sysconf(_SC_NPROCESSORS_ONLN);
+
+	return online * CONCURRENCY;
+}
+
+TEST(uaccess_round_trip)
+{
+	int nr = nr_children();
+	int failures = 0;
+	pid_t pids[nr];
+	int i;
+
+	ksft_print_msg("%d children, %d iterations of %d bytes each\n",
+		       nr, ITERATIONS, STREAM_SIZE);
+
+	for (i = 0; i < nr; i++) {
+		pids[i] = fork();
+		ASSERT_LE(0, pids[i]) {
+			TH_LOG("fork() failed: %s", strerror(errno));
+		}
+
+		if (pids[i] == 0) {
+			/* A distinct, reproducible stream per child. */
+			_exit(child_loop(0x9e3779b97f4a7c15UL + i));
+		}
+	}
+
+	for (i = 0; i < nr; i++) {
+		int status;
+
+		if (waitpid(pids[i], &status, 0) < 0) {
+			TH_LOG("waitpid(%d) failed: %s", pids[i],
+			       strerror(errno));
+			failures++;
+			continue;
+		}
+
+		if (WIFSIGNALED(status)) {
+			TH_LOG("child %d killed by signal %d", pids[i],
+			       WTERMSIG(status));
+			failures++;
+		} else if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+			TH_LOG("child %d failed", pids[i]);
+			failures++;
+		}
+	}
+
+	ASSERT_EQ(0, failures);
+}
+
+TEST_HARNESS_MAIN
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 4/8] riscv: vector: refactor vector context operations
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
                   ` (2 preceding siblings ...)
  2026-09-18 21:51 ` [PATCH v6 3/8] selftest: riscv: test vectorized user copy Andy Chiu
@ 2026-09-18 21:51 ` Andy Chiu
  2026-09-18 21:51 ` [PATCH v6 5/8] riscv: clarify vector state semantics on syscall and context switch Andy Chiu
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, kvm, kvm-riscv, linux-riscv
  Cc: andybnac, Andy Chiu, dfustini, greentime.hu, daichengrong,
	Yong-Xuan Wang

Lift riscv_v_{enable,disable} out of __*vstate_{save,restore,discard} so
that we can reuse some functions without repeatedly turning on/off
vector.

Also, refactor and document about the user context save in preempt_v to
make code more readable.

Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v5:
 - rebase on top of the kvm fix[1]
Changelog v4:
 - fix an unused variable warning (Olof)
Changelog v3:
 - new patch since v3
---
 arch/riscv/include/asm/kvm_vcpu_vector.h |  8 ++++++--
 arch/riscv/include/asm/vector.h          | 15 ++++++++-------
 arch/riscv/kernel/kernel_mode_vector.c   |  4 ++++
 arch/riscv/kvm/vcpu.c                    |  2 +-
 arch/riscv/kvm/vcpu_vector.c             |  6 +++---
 5 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/arch/riscv/include/asm/kvm_vcpu_vector.h b/arch/riscv/include/asm/kvm_vcpu_vector.h
index 6371d5ea5392..f6aba7ade694 100644
--- a/arch/riscv/include/asm/kvm_vcpu_vector.h
+++ b/arch/riscv/include/asm/kvm_vcpu_vector.h
@@ -16,14 +16,18 @@
 #include <asm/vector.h>
 #include <asm/kvm_host.h>
 
-static __always_inline void __kvm_riscv_vector_save(struct kvm_cpu_context *context)
+static __always_inline void kvm_riscv_vector_save(struct kvm_cpu_context *context)
 {
+	riscv_v_enable();
 	__riscv_v_vstate_save(&context->vector, context->vector.datap);
+	riscv_v_disable();
 }
 
-static __always_inline void __kvm_riscv_vector_restore(struct kvm_cpu_context *context)
+static __always_inline void kvm_riscv_vector_restore(struct kvm_cpu_context *context)
 {
+	riscv_v_enable();
 	__riscv_v_vstate_restore(&context->vector, context->vector.datap);
+	riscv_v_disable();
 }
 
 void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu);
diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index 6aa125d66ace..b9538ebd85be 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -203,7 +203,6 @@ static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
 {
 	unsigned long vl;
 
-	riscv_v_enable();
 	__vstate_csr_save(save_to);
 	if (has_xtheadvector()) {
 		asm volatile (
@@ -232,7 +231,6 @@ static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
 			".option pop\n\t"
 			: "=&r" (vl) : "r" (datap) : "memory");
 	}
-	riscv_v_disable();
 }
 
 static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_from,
@@ -240,7 +238,6 @@ static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_
 {
 	unsigned long vl;
 
-	riscv_v_enable();
 	if (has_xtheadvector()) {
 		asm volatile (
 			"mv t0, %0\n\t"
@@ -269,14 +266,12 @@ static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_
 			: "=&r" (vl) : "r" (datap) : "memory");
 	}
 	__vstate_csr_restore(restore_from);
-	riscv_v_disable();
 }
 
 static inline void __riscv_v_vstate_discard(void)
 {
 	unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
 
-	riscv_v_enable();
 	if (has_xtheadvector())
 		asm volatile (THEAD_VSETVLI_T4X0E8M8D1 : : : "t4");
 	else
@@ -296,14 +291,14 @@ static inline void __riscv_v_vstate_discard(void)
 		"vsetvl		%0, x0, %1\n\t"
 		".option pop\n\t"
 		: "=&r" (vl) : "r" (vtype_inval));
-
-	riscv_v_disable();
 }
 
 static inline void riscv_v_vstate_discard(struct pt_regs *regs)
 {
 	if (riscv_v_vstate_query(regs)) {
+		riscv_v_enable();
 		__riscv_v_vstate_discard();
+		riscv_v_disable();
 		__riscv_v_vstate_dirty(regs);
 	}
 }
@@ -312,7 +307,9 @@ static inline void riscv_v_vstate_save(struct __riscv_v_ext_state *vstate,
 				       struct pt_regs *regs)
 {
 	if (__riscv_v_vstate_check(regs->status, DIRTY)) {
+		riscv_v_enable();
 		__riscv_v_vstate_save(vstate, vstate->datap);
+		riscv_v_disable();
 		__riscv_v_vstate_clean(regs);
 	}
 }
@@ -321,7 +318,9 @@ static inline void riscv_v_vstate_restore(struct __riscv_v_ext_state *vstate,
 					  struct pt_regs *regs)
 {
 	if (riscv_v_vstate_query(regs)) {
+		riscv_v_enable();
 		__riscv_v_vstate_restore(vstate, vstate->datap);
+		riscv_v_disable();
 		__riscv_v_vstate_clean(regs);
 	}
 }
@@ -383,8 +382,10 @@ static inline void __switch_to_vector(struct task_struct *prev,
 			riscv_v_disable();
 			prev->thread.riscv_v_flags |= RISCV_PREEMPT_V_IN_SCHEDULE;
 		} else if (riscv_preempt_v_dirty(prev)) {
+			riscv_v_enable();
 			__riscv_v_vstate_save(&prev->thread.kernel_vstate,
 					      prev->thread.kernel_vstate.datap);
+			riscv_v_disable();
 			riscv_preempt_v_clear_dirty(prev);
 		}
 	} else {
diff --git a/arch/riscv/kernel/kernel_mode_vector.c b/arch/riscv/kernel/kernel_mode_vector.c
index b9481a3e40e3..280180114899 100644
--- a/arch/riscv/kernel/kernel_mode_vector.c
+++ b/arch/riscv/kernel/kernel_mode_vector.c
@@ -171,7 +171,9 @@ static int riscv_v_start_kernel_context(void)
 		WARN_ON(riscv_v_ctx_get_depth() == 0);
 		get_cpu_vector_context();
 		if (riscv_preempt_v_dirty(current)) {
+			riscv_v_enable();
 			__riscv_v_vstate_save(kvstate, kvstate->datap);
+			riscv_v_disable();
 			riscv_preempt_v_clear_dirty(current);
 		}
 		riscv_preempt_v_set_restore(current);
@@ -215,7 +217,9 @@ asmlinkage void riscv_v_context_nesting_end(struct pt_regs *regs)
 	depth = riscv_v_ctx_get_depth();
 	if (depth == 0) {
 		if (riscv_preempt_v_restore(current)) {
+			riscv_v_enable();
 			__riscv_v_vstate_restore(vstate, vstate->datap);
+			riscv_v_disable();
 			__riscv_v_vstate_clean(regs);
 			riscv_preempt_v_reset_flags();
 		}
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index bd723e1a685d..71207288a180 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -801,7 +801,7 @@ static void noinstr kvm_riscv_vcpu_enter_exit(struct kvm_vcpu *vcpu,
 	if (current->thread.riscv_v_flags & RISCV_V_VCPU_NEED_RESTORE) {
 		current->thread.riscv_v_flags &= ~RISCV_V_VCPU_NEED_RESTORE;
 		current->thread.riscv_v_flags |= RISCV_V_VCPU_CTX;
-		__kvm_riscv_vector_restore(gcntx);
+		kvm_riscv_vector_restore(gcntx);
 		gcntx->sstatus = (gcntx->sstatus & ~SR_VS) | SR_VS_CLEAN;
 	}
 
diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c
index 536e1b9a8abf..f2d216fece81 100644
--- a/arch/riscv/kvm/vcpu_vector.c
+++ b/arch/riscv/kvm/vcpu_vector.c
@@ -47,7 +47,7 @@ void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
 {
 	if ((cntx->sstatus & SR_VS) == SR_VS_DIRTY) {
 		if (riscv_isa_extension_available(isa, V))
-			__kvm_riscv_vector_save(cntx);
+			kvm_riscv_vector_save(cntx);
 		kvm_riscv_vcpu_vector_clean(cntx);
 	}
 }
@@ -65,13 +65,13 @@ void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
 {
 	/* No need to check host sstatus as it can be modified outside */
 	if (!kvm_riscv_isa_check_host(V))
-		__kvm_riscv_vector_save(cntx);
+		kvm_riscv_vector_save(cntx);
 }
 
 void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
 {
 	if (!kvm_riscv_isa_check_host(V))
-		__kvm_riscv_vector_restore(cntx);
+		kvm_riscv_vector_restore(cntx);
 	riscv_v_flags_set(riscv_v_flags() & ~(RISCV_V_VCPU_CTX | RISCV_V_VCPU_NEED_RESTORE));
 }
 
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 5/8] riscv: clarify vector state semantics on syscall and context switch
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
                   ` (3 preceding siblings ...)
  2026-09-18 21:51 ` [PATCH v6 4/8] riscv: vector: refactor vector context operations Andy Chiu
@ 2026-09-18 21:51 ` Andy Chiu
  2026-09-18 21:51 ` [PATCH v6 6/8] riscv: vector: adjust ptrace and signal behavior for INITIAL state Andy Chiu
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	linux-riscv
  Cc: andybnac, daichengrong, Andy Chiu, dfustini, greentime.hu,
	Yong-Xuan Wang, Sergey Matyukevich

From: daichengrong <daichengrong@iscas.ac.cn>

The RISC-V vector specification states that executing a system call
causes all caller-saved vector registers (v0-v31, vl, vtype) and vstart
to become unspecified.

Currently, after calling riscv_v_vstate_discard(), the vector state
may still be marked as DIRTY, which can mislead the context switch
logic into treating the registers as containing valid user data.

This patch clarifies and tightens the kernel-side semantics:

1. On syscall entry, the kernel checks the vector state via sstatus
   and set it to INIT if not already, indicating that the vector
   registers no longer contain meaningful user data. Context
   invalidation is required on restore.

   If the state is already set to INIT, it means that the user has not
   touched vector since the last invalidation. So no further action is
   required on restore.

2. During context switch, the vector state is saved only if the state is
   DIRTY. (no change)

3. On restore, if invalidation is required, the vector registers are
   overwritten with a known initial value and the state is set to INIT.

Performance improvements on Blackhole x280:

Latency of getpid() with different status.VS upon syscall entry in ns:
status.VS	Before Patch	After Patch	Improvement
DIRTY		235.9		242.4		+6.5 (+2.7%) # regress
INIT		234.8		174.4		-60.4 (-25.7%)
OFF		178.2		174.5		-3.7 (-2.1%)

Context switch latencies in us:
Metric		Before Patch	After Patch	Improvement
Mean Latency	14.19		11.62		-2.57 (-18.1%)
Median Latency	13.51		11.01		-2.50 (-18.5%)
Max Latency	27.76		21.92		-5.84 (-21.0%)
Min Latency	8.99		8.12		-0.87 (-9.7%)

The metrics on context switch latencies are obtained by running the
following command 100 times:
$ taskset 0x2 ./lat_ctx_v -N 10000 -s 512 2

The program lat_ctx_v is a modified lat_ctx where benchmark processes
(parent/child) do a `vsetvli` to dirtify VS before making each write
syscall.

Signed-off-by: daichengrong <daichengrong@iscas.ac.cn>
Co-developed-by: Andy Chiu <tchiu@tenstorrent.com>
Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v4:
 - Provide performance meassurement (Olof)
 - Aggregate riscv_v_{enable,disable} for better performance (reduces to
   ~240 ns/call when the user touches vector)
 - Do not invalidate again if user doesn't touch vector
Changelog v3:
 - rename vstate_on to vstate_init to prevent confusion
 - set context as clean at first-use trap to return zero'ed context
 - reduce context nulling operations by defering __vstate_discard to
   exit_to_user_mode_prepare.
---
 arch/riscv/include/asm/vector.h | 44 +++++++++++++++++++--------------
 arch/riscv/kernel/vector.c      |  2 +-
 2 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index b9538ebd85be..61cd0848f661 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -40,6 +40,15 @@
 	_res;								\
 })
 
+#define __riscv_v_vstate_check_gt(_val, TYPE) ({			\
+	bool _res;							\
+	if (has_xtheadvector())						\
+		_res = ((_val) & SR_VS_THEAD) > SR_VS_##TYPE##_THEAD;	\
+	else								\
+		_res = ((_val) & SR_VS) > SR_VS_##TYPE;			\
+	_res;								\
+})
+
 extern unsigned long riscv_v_vsize;
 int riscv_v_setup_vsize(void);
 bool insn_is_vector(u32 insn_buf);
@@ -100,7 +109,7 @@ static inline void riscv_v_vstate_off(struct pt_regs *regs)
 	regs->status = __riscv_v_vstate_or(regs->status, OFF);
 }
 
-static inline void riscv_v_vstate_on(struct pt_regs *regs)
+static inline void riscv_v_vstate_init(struct pt_regs *regs)
 {
 	regs->status = __riscv_v_vstate_or(regs->status, INITIAL);
 }
@@ -293,16 +302,6 @@ static inline void __riscv_v_vstate_discard(void)
 		: "=&r" (vl) : "r" (vtype_inval));
 }
 
-static inline void riscv_v_vstate_discard(struct pt_regs *regs)
-{
-	if (riscv_v_vstate_query(regs)) {
-		riscv_v_enable();
-		__riscv_v_vstate_discard();
-		riscv_v_disable();
-		__riscv_v_vstate_dirty(regs);
-	}
-}
-
 static inline void riscv_v_vstate_save(struct __riscv_v_ext_state *vstate,
 				       struct pt_regs *regs)
 {
@@ -317,20 +316,26 @@ static inline void riscv_v_vstate_save(struct __riscv_v_ext_state *vstate,
 static inline void riscv_v_vstate_restore(struct __riscv_v_ext_state *vstate,
 					  struct pt_regs *regs)
 {
-	if (riscv_v_vstate_query(regs)) {
-		riscv_v_enable();
+	riscv_v_enable();
+	if (__riscv_v_vstate_check(regs->status, INITIAL))
+		__riscv_v_vstate_discard();
+	else if (__riscv_v_vstate_check(regs->status, CLEAN))
 		__riscv_v_vstate_restore(vstate, vstate->datap);
-		riscv_v_disable();
-		__riscv_v_vstate_clean(regs);
-	}
+	riscv_v_disable();
 }
 
 static inline void riscv_v_vstate_set_restore(struct task_struct *task,
 					      struct pt_regs *regs)
 {
-	if (riscv_v_vstate_query(regs)) {
+	if (riscv_v_vstate_query(regs))
 		set_tsk_thread_flag(task, TIF_RISCV_V_DEFER_RESTORE);
-		riscv_v_vstate_on(regs);
+}
+
+static inline void riscv_v_vstate_discard(struct pt_regs *regs)
+{
+	if (__riscv_v_vstate_check_gt(regs->status, INITIAL)) {
+		riscv_v_vstate_set_restore(current, regs);
+		riscv_v_vstate_init(regs);
 	}
 }
 
@@ -401,6 +406,7 @@ static inline void __switch_to_vector(struct task_struct *prev,
 			riscv_preempt_v_set_restore(next);
 		}
 	} else {
+		/* VS is never DIRTY at this point, there's no need to alter vstate here */
 		riscv_v_vstate_set_restore(next, task_pt_regs(next));
 	}
 }
@@ -426,7 +432,7 @@ static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
 #define riscv_v_vstate_restore(vstate, regs)	do {} while (0)
 #define __switch_to_vector(__prev, __next)	do {} while (0)
 #define riscv_v_vstate_off(regs)		do {} while (0)
-#define riscv_v_vstate_on(regs)			do {} while (0)
+#define riscv_v_vstate_init(regs)		do {} while (0)
 #define riscv_v_thread_free(tsk)		do {} while (0)
 #define  riscv_v_setup_ctx_cache()		do {} while (0)
 #define riscv_v_thread_alloc(tsk)		do {} while (0)
diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c
index b112166d51e9..4eef51f6d432 100644
--- a/arch/riscv/kernel/vector.c
+++ b/arch/riscv/kernel/vector.c
@@ -221,7 +221,7 @@ bool riscv_v_first_use_handler(struct pt_regs *regs)
 		return true;
 	}
 
-	riscv_v_vstate_on(regs);
+	__riscv_v_vstate_clean(regs);
 	riscv_v_vstate_set_restore(current, regs);
 
 	return true;
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 6/8] riscv: vector: adjust ptrace and signal behavior for INITIAL state
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
                   ` (4 preceding siblings ...)
  2026-09-18 21:51 ` [PATCH v6 5/8] riscv: clarify vector state semantics on syscall and context switch Andy Chiu
@ 2026-09-18 21:51 ` Andy Chiu
  2026-09-18 21:51 ` [PATCH v6 7/8] selftests: riscv: Extend vector tests for sigreturn and ptrace Andy Chiu
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Oleg Nesterov, linux-riscv
  Cc: andybnac, Andy Chiu, Sergey Matyukevich, gdb, dfustini,
	greentime.hu, Yong-Xuan Wang, daichengrong, Deepak Gupta

The last patch introduced the INITIAL vector state to avoid saving and
restoring vector registers across syscall boundaries. However, this
optimization did not fully account for the ptrace and signal handling
interfaces.

As a result, two issues emerged:
1. Ptrace reads at syscall stop could observe stale, non-nulled
   registers.
2. Modifications to the ucontext through signal interface during a
   syscall stop would be overwritten by the vector discaring macro.

This patch introduces riscv_v_ucontext_save() to synchronize these
paths with the INITIAL state:

- Ptrace reads during a syscall stop now explicitly execute the hardware
  discard macro and return the discarded state to prevent data leaks.
- Ptrace writes (PTRACE_SETREGSET) during a syscall stop are silently
  dropped (returning 0). Returning an error like EINVAL would break
  debbugers like GDB, which disables the optional regset on receiving
  such error.
- Signal handling (rt_sigreturn) now honor user-space modifications to
  the vector context (for user-space thread schedulers).

CC: Sergey Matyukevich <geomatsi@gmail.com>
CC: gdb@sourceware.org
Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v3:
 - new patch since v3
---
 arch/riscv/include/asm/vector.h |  2 ++
 arch/riscv/kernel/ptrace.c      | 13 ++++++------
 arch/riscv/kernel/signal.c      | 11 ++++++----
 arch/riscv/kernel/vector.c      | 37 +++++++++++++++++++++++++++++++++
 4 files changed, 53 insertions(+), 10 deletions(-)

diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index 61cd0848f661..1cc37d40cf79 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -61,6 +61,7 @@ void riscv_v_thread_free(struct task_struct *tsk);
 void __init riscv_v_setup_ctx_cache(void);
 void riscv_v_thread_alloc(struct task_struct *tsk);
 void __init update_regset_vector_info(unsigned long size);
+void riscv_v_ucontext_save(struct task_struct *tsk);
 
 static inline u32 riscv_v_flags(void)
 {
@@ -438,6 +439,7 @@ static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
 #define riscv_v_thread_alloc(tsk)		do {} while (0)
 #define get_cpu_vector_context()		do {} while (0)
 #define put_cpu_vector_context()		do {} while (0)
+#define riscv_v_ucontext_save(tsk)		do {} while (0)
 #define riscv_v_vstate_set_restore(task, regs)	do {} while (0)
 
 #endif /* CONFIG_RISCV_ISA_V */
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
index f336a183667e..9276485b6cac 100644
--- a/arch/riscv/kernel/ptrace.c
+++ b/arch/riscv/kernel/ptrace.c
@@ -109,11 +109,7 @@ static int riscv_vr_get(struct task_struct *target,
 	 * Ensure the vector registers have been saved to the memory before
 	 * copying them to membuf.
 	 */
-	if (target == current) {
-		get_cpu_vector_context();
-		riscv_v_vstate_save(&current->thread.vstate, task_pt_regs(current));
-		put_cpu_vector_context();
-	}
+	riscv_v_ucontext_save(target);
 
 	ptrace_vstate.vstart = vstate->vstart;
 	ptrace_vstate.vl = vstate->vl;
@@ -222,13 +218,18 @@ static int riscv_vr_set(struct task_struct *target,
 	int ret;
 	struct __riscv_v_ext_state *vstate = &target->thread.vstate;
 	struct __riscv_v_regset_state ptrace_vstate;
+	struct pt_regs *regs = task_pt_regs(target);
 
 	if (!(has_vector() || has_xtheadvector()))
 		return -EINVAL;
 
-	if (!riscv_v_vstate_query(task_pt_regs(target)))
+	if (!riscv_v_vstate_query(regs))
 		return -ENODATA;
 
+	/* Silently drop the modification to tracee as no vreg lives across a syscall */
+	if (__riscv_v_vstate_check(regs->status, INITIAL))
+		return 0;
+
 	/* Copy rest of the vstate except datap */
 	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0,
 				 sizeof(struct __riscv_v_regset_state));
diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c
index 59784dc117e4..0da352310b84 100644
--- a/arch/riscv/kernel/signal.c
+++ b/arch/riscv/kernel/signal.c
@@ -89,9 +89,7 @@ static long save_v_state(struct pt_regs *regs, void __user *sc_vec)
 	/* datap is designed to be 16 byte aligned for better performance */
 	WARN_ON(!IS_ALIGNED((unsigned long)datap, 16));
 
-	get_cpu_vector_context();
-	riscv_v_vstate_save(&current->thread.vstate, regs);
-	put_cpu_vector_context();
+	riscv_v_ucontext_save(current);
 
 	/* Copy everything of vstate but datap. */
 	err = __copy_to_user(&state->v_state, &current->thread.vstate,
@@ -121,9 +119,14 @@ static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec)
 	/*
 	 * Mark the vstate as clean prior performing the actual copy,
 	 * to avoid getting the vstate incorrectly clobbered by the
-	 *  discarded vector state.
+	 * discarded vector state.
+	 *
+	 * This also allows user to modify vregs through the signal
+	 * interface at a syscall stop. e.g. to support user space
+	 * context switching.
 	 */
 	riscv_v_vstate_set_restore(current, regs);
+	__riscv_v_vstate_clean(regs);
 
 	/* Copy everything of __sc_riscv_v_state except datap. */
 	err = __copy_from_user(&current->thread.vstate, &state->v_state,
diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c
index 4eef51f6d432..6fd541f5d5cb 100644
--- a/arch/riscv/kernel/vector.c
+++ b/arch/riscv/kernel/vector.c
@@ -29,6 +29,43 @@ static struct kmem_cache *riscv_v_kernel_cachep;
 unsigned long riscv_v_vsize __read_mostly;
 EXPORT_SYMBOL_GPL(riscv_v_vsize);
 
+/*
+ * Context memory is not coherent to register when sstatus.vs is set to INITIAL. This function
+ * take the INITIAL state into consideration and reflect the nulled state into context memory.
+ * Assume the target task is not actively running when tsk != current
+ */
+void riscv_v_ucontext_save(struct task_struct *tsk)
+{
+	struct __riscv_v_ext_state *vstate = &tsk->thread.vstate;
+	struct pt_regs *regs = task_pt_regs(tsk);
+
+	/*
+	 * Do not set vstate as clean when it is INITIAL, otherwise we lose track of the nulled
+	 * state in ptrace.
+	 */
+	if (tsk == current) {
+		get_cpu_vector_context();
+		if (__riscv_v_vstate_check(regs->status, INITIAL)) {
+			riscv_v_enable();
+			__riscv_v_vstate_discard();
+			__riscv_v_vstate_save(vstate, vstate->datap);
+			riscv_v_disable();
+		} else {
+			riscv_v_vstate_save(vstate, regs);
+		}
+		put_cpu_vector_context();
+	} else if (__riscv_v_vstate_check(regs->status, INITIAL)) {
+		/*
+		 * If we are not current and VS == INITIAL, null out the context memory for tsk
+		 * using kernel mode vector.
+		 */
+		kernel_vector_begin();
+		__riscv_v_vstate_discard();
+		__riscv_v_vstate_save(vstate, vstate->datap);
+		kernel_vector_end();
+	}
+}
+
 int riscv_v_setup_vsize(void)
 {
 	unsigned long this_vsize;
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 7/8] selftests: riscv: Extend vector tests for sigreturn and ptrace
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
                   ` (5 preceding siblings ...)
  2026-09-18 21:51 ` [PATCH v6 6/8] riscv: vector: adjust ptrace and signal behavior for INITIAL state Andy Chiu
@ 2026-09-18 21:51 ` Andy Chiu
  2026-09-18 21:51 ` [PATCH v6 8/8] riscv: vector: optimize vstate operations Andy Chiu
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Shuah Khan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, linux-kselftest, linux-riscv
  Cc: andybnac, Andy Chiu, dfustini, greentime.hu, Andrew Morton,
	Bala-Vignesh-Reddy, Wei Yang, Yong-Xuan Wang

Add new test cases to verify the vector state restorations at syscall
stops for ptrace and signal interfaces. Specifically:
1. Signal handler should read all ones at syscall stop and modifying
   context should success.
2. Ptrace should read all ones but any modification to NT_RISCV_VECTOR
   is silently dropped.

Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v6:
 - reformat code to slience checkpatch
Changelog v4:
 - remove timer dependency from the signal delivery (Sashiko)
 - solve 2 memory leaks in ptrace (Sashiko)
Changelog v3:
 - new patch since v3
---
 .../selftests/riscv/sigreturn/sigreturn.c     |  69 +++++++++++
 .../selftests/riscv/vector/vstate_ptrace.c    | 111 +++++++++++++++++-
 2 files changed, 176 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/riscv/sigreturn/sigreturn.c b/tools/testing/selftests/riscv/sigreturn/sigreturn.c
index e10873d95fed..386367cf28be 100644
--- a/tools/testing/selftests/riscv/sigreturn/sigreturn.c
+++ b/tools/testing/selftests/riscv/sigreturn/sigreturn.c
@@ -7,6 +7,7 @@
 #include "kselftest_harness.h"
 
 #define RISCV_V_MAGIC		0x53465457
+#define END_MAGIC		0
 #define DEFAULT_VALUE		2
 #define SIGNAL_HANDLER_OVERRIDE	3
 
@@ -61,6 +62,74 @@ static int vector_sigreturn(int data, void (*handler)(int, siginfo_t *, void *))
 	return after_sigreturn;
 }
 
+#define V_TEST_PATTERN_SIGNAL 0x98
+int nulled_val;
+static void sigalrm_handler(int sig, siginfo_t *info, void *vcontext)
+{
+	struct __riscv_extra_ext_header *ext;
+	struct __riscv_v_ext_state *v_state;
+	ucontext_t *context = vcontext;
+	struct __riscv_ctx_hdr *hdr;
+	uint8_t *ext_ptr;
+
+	/* Find the vector context */
+	ext = (void *)(&context->uc_mcontext.__fpregs);
+	ext_ptr = (uint8_t *)ext;
+	hdr = &ext->hdr;
+
+	while (hdr->magic != END_MAGIC) {
+		if (hdr->magic == RISCV_V_MAGIC) {
+			v_state = (struct __riscv_v_ext_state *)(hdr + 1);
+			/* Assume a valid datap */
+			nulled_val = *(int *)v_state->datap;
+			/* Fill all vector registers with magic pattern */
+			memset(v_state->datap, V_TEST_PATTERN_SIGNAL, v_state->vlenb * 32);
+			/*
+			 * We must also set the vector configuration so that when
+			 * userspace reads v0, it uses a valid element width (e8).
+			 */
+			v_state->vl = v_state->vlenb;
+			v_state->vtype = 0; /* e8, m1, tu, mu */
+			break;
+		}
+		/* Move to the next extension header */
+		ext_ptr += hdr->size;
+		hdr = (struct __riscv_ctx_hdr *)ext_ptr;
+	}
+}
+
+TEST(test_signal_syscall_ucontext) {
+	struct sigaction sa;
+
+	/* Make sure we get V in ucontext by executing vsetvli */
+	asm volatile (".option push\n\t"
+		      ".option		arch, +v\n\t"
+		      "vsetivli	x0, 1, e32, m1, ta, ma\n\t"
+		      ".option pop\n\t" : : :);
+
+	sa.sa_flags = SA_SIGINFO;
+	sa.sa_sigaction = sigalrm_handler;
+	sigemptyset(&sa.sa_mask);
+	if (sigaction(SIGALRM, &sa, NULL) == -1)
+		ksft_exit_fail_msg("Failed to register signal handler\n");
+
+	raise(SIGALRM);
+	/*
+	 * If the kernel successfully parsed and restored our modified ucontext,
+	 * v0 will contain V_TEST_PATTERN_SIGNAL.
+	 */
+	unsigned char v0_val;
+
+	asm volatile(".option push\n\t"
+		     ".option arch, +zve32x\n\t"
+		     "vmv.x.s %0, v0\n\t"
+		     ".option pop\n\t"
+		     : "=r" (v0_val));
+
+	EXPECT_EQ(v0_val, V_TEST_PATTERN_SIGNAL);
+	EXPECT_EQ(nulled_val, -1);
+}
+
 TEST(vector_restore)
 {
 	int result;
diff --git a/tools/testing/selftests/riscv/vector/vstate_ptrace.c b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
index 1479abc0c9cb..863a16f6e1a3 100644
--- a/tools/testing/selftests/riscv/vector/vstate_ptrace.c
+++ b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
@@ -21,6 +21,41 @@ static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t siz
 	return ptrace(op, pid, type, &v_iovec);
 }
 
+static int do_child_syscall_stop(void)
+{
+	int out;
+
+	if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) {
+		ksft_perror("PTRACE_TRACEME failed\n");
+		return EXIT_FAILURE;
+	}
+
+	raise(SIGSTOP);
+
+	asm volatile (".option push\n\t"
+		".option	arch, +v\n\t"
+		"vsetivli	x0, 1, e32, m1, ta, ma\n\t"
+		"vmv.s.x	v31, %[in]\n\t"
+		".option pop\n\t"
+		:
+		: [in] "r" (child_set_val));
+
+	getpid();
+
+	asm volatile (".option push\n\t"
+		".option	arch, +v\n\t"
+		"vsetivli	x0, 1, e32, m1, ta, ma\n\t"
+		"vmv.x.s	%[out], v31\n\t"
+		".option pop\n\t"
+		: [out] "=r" (out)
+		:);
+
+	if (out != -1)
+		return EXIT_FAILURE;
+
+	return EXIT_SUCCESS;
+}
+
 static int do_child(void)
 {
 	int out;
@@ -59,7 +94,7 @@ static void do_parent(pid_t child)
 			goto out;
 		} else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
 			size_t size;
-			void *data, *v31;
+			void *vctx, *v31;
 			struct __riscv_v_regset_state *v_regset_hdr;
 			struct user_regs_struct *gpreg;
 
@@ -73,9 +108,11 @@ static void do_parent(pid_t child)
 				goto out;
 
 			ksft_print_msg("vlenb %ld\n", v_regset_hdr->vlenb);
-			data = realloc(data, size + v_regset_hdr->vlenb * 32);
-			if (!data)
+			vctx = realloc(data, size + v_regset_hdr->vlenb * 32);
+			if (!vctx)
 				goto out;
+			data = vctx;
+
 			v_regset_hdr = (struct __riscv_v_regset_state *)data;
 			v31 = (void *)(data + size + v_regset_hdr->vlenb * 31);
 			size += v_regset_hdr->vlenb * 32;
@@ -109,11 +146,65 @@ static void do_parent(pid_t child)
 	free(data);
 }
 
+static void do_parent_syscall_stop(pid_t child)
+{
+	int status;
+	void *data = NULL;
+
+	while (waitpid(child, &status, 0)) {
+		if (WIFEXITED(status)) {
+			ksft_test_result(WEXITSTATUS(status) == 0,
+					 "SETREGSET vector at syscall stop\n");
+			goto out;
+		} else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGSTOP)) {
+			/* Attach to the child at syscall stop */
+			ptrace(PTRACE_SYSCALL, child, NULL, NULL);
+			continue;
+		} else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
+			size_t size;
+			void *vctx, *v31;
+			struct __riscv_v_regset_state *v_regset_hdr;
+
+			size = sizeof(*v_regset_hdr);
+			data = malloc(size);
+			if (!data)
+				goto out;
+			v_regset_hdr = (struct __riscv_v_regset_state *)data;
+
+			if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data))
+				goto out;
+
+			ksft_print_msg("vlenb %ld\n", v_regset_hdr->vlenb);
+			vctx = realloc(data, size + v_regset_hdr->vlenb * 32);
+			if (!vctx)
+				goto out;
+			data = vctx;
+
+			v_regset_hdr = (struct __riscv_v_regset_state *)data;
+			v31 = (void *)(data + size + v_regset_hdr->vlenb * 31);
+			size += v_regset_hdr->vlenb * 32;
+
+			if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data))
+				goto out;
+
+			ksft_test_result(*(int *)v31 == -1, "GETREGSET vector at syscall stop\n");
+
+			*(int *)v31 = parent_set_val;
+			if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data))
+				goto out;
+		}
+		ptrace(PTRACE_CONT, child, NULL, NULL);
+	}
+
+out:
+	free(data);
+}
+
 int main(void)
 {
 	pid_t child;
 
-	ksft_set_plan(2);
+	ksft_set_plan(4);
 	if (!is_vector_supported() && !is_xtheadvector_supported())
 		ksft_exit_skip("Vector not supported\n");
 
@@ -130,5 +221,17 @@ int main(void)
 
 	do_parent(child);
 
+	parent_set_val = 0x53355457;
+	child_set_val = 0x49504F21;
+
+	child = fork();
+	if (child < 0)
+		ksft_exit_fail_msg("Fork failed %d\n", child);
+
+	if (!child)
+		return do_child_syscall_stop();
+
+	do_parent_syscall_stop(child);
+
 	ksft_finished();
 }
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v6 8/8] riscv: vector: optimize vstate operations
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
                   ` (6 preceding siblings ...)
  2026-09-18 21:51 ` [PATCH v6 7/8] selftests: riscv: Extend vector tests for sigreturn and ptrace Andy Chiu
@ 2026-09-18 21:51 ` Andy Chiu
  2026-10-06  9:40 ` [PATCH v6 0/8] riscv: optimize mode switch latency for Vector patchwork-bot+linux-riscv
  2026-10-06 10:01 ` Paul Walmsley
  9 siblings, 0 replies; 15+ messages in thread
From: Andy Chiu @ 2026-09-18 21:51 UTC (permalink / raw)
  To: Jonathan Corbet, Shuah Khan, Randy Dunlap, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-doc,
	linux-riscv
  Cc: andybnac, Andy Chiu, dfustini, greentime.hu,
	Borislav Petkov (AMD), Mike Rapoport (Microsoft), Andrew Morton,
	Dapeng Mi, Marco Elver, Jeff Layton, Ethan Nelson-Moore,
	Jakub Kicinski, Eric Biggers, Djordje Todorovic,
	Aleksandar Rikalo, Aleksa Paunovic, daichengrong, Yong-Xuan Wang,
	Charlie Jenkins, Guodong Xu, Andrew Jones, Atish Patra, Hui Wang,
	Deepak Gupta, Pincheng Wang, Clément Léger, Zong Li,
	Vivian Wang, Rui Qi, Samuel Holland, Zishun Yi,
	Sergey Matyukevich

Introduce the riscv_novstateopt to control the optimization of vector
state operations in the kernel space.

The optimization enables vector in kernel mode and bypasses vector
register nulling for user space on the syscall fast path when the boot
argument "riscv_novstateopt" is not set

Enabling this optimization yields the following performance benefits:
 - Cut 80 cycles latency for getpid on Ascalon.
 - Up to 2.9% request throughput improvement on an nginx workload on a
   four-core Ascalon-S.
      86B      1KB      2KB      4KB      8KB     16KB     32KB
    +0.33%*  -0.23%   +2.88%*  +2.19%*  +1.02%   +1.14%   +1.40%*

Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>
---
Changelog v6:
 - upgrade it to use alternative and change default to enable the
   optimization
 - refresh the nginx numbers for the alternative-based implementation
Changelog v5:
 - new patch since v5
---
 .../admin-guide/kernel-parameters.txt         | 13 +++++++++++++
 arch/riscv/include/asm/alternative-macros.h   |  6 ++++++
 arch/riscv/include/asm/alternative.h          |  3 ---
 arch/riscv/include/asm/cpufeature-macros.h    | 12 ++++++++----
 arch/riscv/include/asm/vector.h               | 19 ++++++++++++++++++-
 arch/riscv/kernel/cpufeature.c                | 19 +++++++++++++++++++
 arch/riscv/kernel/entry.S                     | 15 +++++++++++++--
 arch/riscv/kernel/process.c                   |  3 +++
 arch/riscv/kernel/vector.c                    | 17 +++++++++++++++--
 9 files changed, 95 insertions(+), 12 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 4a12805a50ba..798175c902eb 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -6700,6 +6700,19 @@ Kernel parameters
 		fcfi	Disable user forward CFI ABI to userspace even if the
 			landing pad extension is available.
 
+	riscv_novstateopt [RISCV,EARLY]
+			Disable the vector state optimization. By default the
+			kernel keeps Vector enabled while running in kernel
+			space, rather than toggling sstatus.VS on every use of
+			the kernel-mode vector, and skips nulling the vector
+			registers for user space. Pass this option to restore the
+			conservative behavior, which catches illegal use of
+			Vector in kernel code. The optimization roughly cuts
+			30 ns of getpid() latency on a Blackhole x280 and gives
+			a 2.9% request throughput improvement for an nginx
+			workload serving a 2KB web page on a four-core
+			Ascalon-S.
+
 	ro		[KNL] Mount root device read-only on boot
 
 	rodata=		[KNL,EARLY]
diff --git a/arch/riscv/include/asm/alternative-macros.h b/arch/riscv/include/asm/alternative-macros.h
index 9619bd5c8eba..f6997c77e29a 100644
--- a/arch/riscv/include/asm/alternative-macros.h
+++ b/arch/riscv/include/asm/alternative-macros.h
@@ -2,6 +2,12 @@
 #ifndef __ASM_ALTERNATIVE_MACROS_H
 #define __ASM_ALTERNATIVE_MACROS_H
 
+#include <linux/wordpart.h>
+#define PATCH_ID_CPUFEATURE_ID(p)		lower_16_bits(p)
+#define PATCH_ID_CPUFEATURE_VALUE(p)		upper_16_bits(p)
+
+#define RISCV_CPUFEAT_VSTATEOPT	0x1
+
 #ifdef CONFIG_RISCV_ALTERNATIVE
 
 #ifdef __ASSEMBLER__
diff --git a/arch/riscv/include/asm/alternative.h b/arch/riscv/include/asm/alternative.h
index 8407d1d535b8..4a50a8601985 100644
--- a/arch/riscv/include/asm/alternative.h
+++ b/arch/riscv/include/asm/alternative.h
@@ -18,9 +18,6 @@
 #include <linux/stddef.h>
 #include <asm/hwcap.h>
 
-#define PATCH_ID_CPUFEATURE_ID(p)		lower_16_bits(p)
-#define PATCH_ID_CPUFEATURE_VALUE(p)		upper_16_bits(p)
-
 #define RISCV_ALTERNATIVES_BOOT		0 /* alternatives applied during regular boot */
 #define RISCV_ALTERNATIVES_MODULE	1 /* alternatives applied during module-init */
 #define RISCV_ALTERNATIVES_EARLY_BOOT	2 /* alternatives applied before mmu start */
diff --git a/arch/riscv/include/asm/cpufeature-macros.h b/arch/riscv/include/asm/cpufeature-macros.h
index a8103edbf51f..46ef64932a1c 100644
--- a/arch/riscv/include/asm/cpufeature-macros.h
+++ b/arch/riscv/include/asm/cpufeature-macros.h
@@ -45,22 +45,26 @@ static __always_inline bool __riscv_has_extension_unlikely(const unsigned long v
 
 static __always_inline bool riscv_has_extension_unlikely(const unsigned long ext)
 {
-	compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX");
+	int realext = PATCH_ID_CPUFEATURE_ID(ext);
+
+	compiletime_assert(realext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX");
 
 	if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE))
 		return __riscv_has_extension_unlikely(STANDARD_EXT, ext);
 
-	return __riscv_isa_extension_available(NULL, ext);
+	return __riscv_isa_extension_available(NULL, realext);
 }
 
 static __always_inline bool riscv_has_extension_likely(const unsigned long ext)
 {
-	compiletime_assert(ext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX");
+	int realext = PATCH_ID_CPUFEATURE_ID(ext);
+
+	compiletime_assert(realext < RISCV_ISA_EXT_MAX, "ext must be < RISCV_ISA_EXT_MAX");
 
 	if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE))
 		return __riscv_has_extension_likely(STANDARD_EXT, ext);
 
-	return __riscv_isa_extension_available(NULL, ext);
+	return __riscv_isa_extension_available(NULL, realext);
 }
 
 #endif /* _ASM_CPUFEATURE_MACROS_H */
diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index 1cc37d40cf79..b227a8252fa9 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -49,6 +49,7 @@
 	_res;								\
 })
 
+extern bool riscv_v_vstate_opt;
 extern unsigned long riscv_v_vsize;
 int riscv_v_setup_vsize(void);
 bool insn_is_vector(u32 insn_buf);
@@ -78,6 +79,14 @@ static __always_inline bool has_vector(void)
 	return riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE32X);
 }
 
+static __always_inline bool has_vstate_opt(void)
+{
+	if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE))
+		return riscv_has_extension_likely((RISCV_CPUFEAT_VSTATEOPT << 16) | RISCV_ISA_EXT_ZVE32X);
+	else
+		return false;
+}
+
 static __always_inline bool has_xtheadvector_no_alternatives(void)
 {
 	if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR))
@@ -122,6 +131,9 @@ static inline bool riscv_v_vstate_query(struct pt_regs *regs)
 
 static __always_inline void riscv_v_enable(void)
 {
+	if (has_vstate_opt())
+		return;
+
 	if (has_xtheadvector())
 		csr_set(CSR_SSTATUS, SR_VS_THEAD);
 	else
@@ -130,6 +142,9 @@ static __always_inline void riscv_v_enable(void)
 
 static __always_inline void riscv_v_disable(void)
 {
+	if (has_vstate_opt())
+		return;
+
 	if (has_xtheadvector())
 		csr_clear(CSR_SSTATUS, SR_VS_THEAD);
 	else
@@ -335,7 +350,8 @@ static inline void riscv_v_vstate_set_restore(struct task_struct *task,
 static inline void riscv_v_vstate_discard(struct pt_regs *regs)
 {
 	if (__riscv_v_vstate_check_gt(regs->status, INITIAL)) {
-		riscv_v_vstate_set_restore(current, regs);
+		if (!has_vstate_opt())
+			riscv_v_vstate_set_restore(current, regs);
 		riscv_v_vstate_init(regs);
 	}
 }
@@ -421,6 +437,7 @@ struct pt_regs;
 
 static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
 static __always_inline bool has_vector(void) { return false; }
+static __always_inline bool has_vstate_opt(void) { return false; }
 static __always_inline bool insn_is_vector(u32 insn_buf) { return false; }
 static __always_inline bool has_xtheadvector_no_alternatives(void) { return false; }
 static __always_inline bool has_xtheadvector(void) { return false; }
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index d2ec96843456..90a0b745611e 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -1217,6 +1217,20 @@ void __init riscv_user_isa_enable(void)
 		pr_warn("Zicbop disabled as it is unavailable on some harts\n");
 }
 
+/*
+ * Leaving Vector enabled while in the kernel requires the vstate alternatives
+ * to be patched in, so the optimization is only available when alternatives
+ * are built. It can be turned off on the command line to catch illegal use of
+ * Vector in kernel code.
+ */
+bool riscv_v_vstate_opt = IS_ENABLED(CONFIG_RISCV_ALTERNATIVE);
+static int __init riscv_novstateopt_setup(char *__unused)
+{
+	riscv_v_vstate_opt = false;
+	return 0;
+}
+early_param("riscv_novstateopt", riscv_novstateopt_setup);
+
 #ifdef CONFIG_RISCV_ALTERNATIVE
 /*
  * Alternative patch sites consider 48 bits when determining when to patch
@@ -1246,6 +1260,11 @@ static bool riscv_cpufeature_patch_check(u16 id, u16 value)
 		 * then the alternative cannot be applied.
 		 */
 		return riscv_cboz_block_size <= (1U << value);
+	case RISCV_ISA_EXT_ZVE32X:
+		if (value == RISCV_CPUFEAT_VSTATEOPT)
+			return riscv_v_vstate_opt;
+
+		return true;
 	}
 
 	return false;
diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
index d799c4e56f80..d9d6b4f87670 100644
--- a/arch/riscv/kernel/entry.S
+++ b/arch/riscv/kernel/entry.S
@@ -6,6 +6,7 @@
 
 #include <linux/init.h>
 #include <linux/linkage.h>
+#include <linux/stringify.h>
 
 #include <asm/alternative-macros.h>
 #include <asm/asm.h>
@@ -175,10 +176,13 @@ SYM_CODE_START(handle_exception)
 	 * Disable user-mode memory access as it should only be set in the
 	 * actual user copy routines.
 	 *
-	 * Disable the FPU/Vector to detect illegal usage of floating point
+	 * Disable the FPU to detect illegal usage of floating point
 	 * or vector in kernel space.
 	 */
-	li t0, SR_SUM | SR_FS_VS
+	li t0, SR_SUM | SR_FS
+	ALTERNATIVE(__stringify(ori t0, t0, SR_VS), "nop", 0,
+		    (RISCV_CPUFEAT_VSTATEOPT << 16) | RISCV_ISA_EXT_ZVE32X,
+		    CONFIG_RISCV_ISA_V)
 #ifdef CONFIG_64BIT
 	li t1, SR_ELP
 	or t0, t0, t1
@@ -186,6 +190,13 @@ SYM_CODE_START(handle_exception)
 
 	REG_L s0, TASK_TI_USER_SP(tp)
 	csrrc s1, CSR_STATUS, t0
+	ALTERNATIVE("j .Lskip_v_enable", __stringify(andi t0, s1, SR_VS), 0,
+		    (RISCV_CPUFEAT_VSTATEOPT << 16) | RISCV_ISA_EXT_ZVE32X,
+		    CONFIG_RISCV_ISA_V)
+	bnez t0, .Lskip_v_enable
+	li a0, SR_VS_INITIAL
+	csrs CSR_STATUS, a0
+.Lskip_v_enable:
 	save_userssp s2, s1
 	csrr s2, CSR_EPC
 	csrr s3, CSR_TVAL
diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
index b2df7f72241a..5c127855be54 100644
--- a/arch/riscv/kernel/process.c
+++ b/arch/riscv/kernel/process.c
@@ -258,6 +258,9 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 		/* Supervisor/Machine, irqs on: */
 		childregs->status = SR_PP | SR_PIE;
 
+		if (has_vstate_opt())
+			riscv_v_vstate_init(childregs);
+
 		p->thread.s[0] = (unsigned long)args->fn;
 		p->thread.s[1] = (unsigned long)args->fn_arg;
 		p->thread.ra = (unsigned long)ret_from_fork_kernel_asm;
diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c
index 6fd541f5d5cb..eec9deff8da7 100644
--- a/arch/riscv/kernel/vector.c
+++ b/arch/riscv/kernel/vector.c
@@ -12,6 +12,7 @@
 #include <linux/prctl.h>
 
 #include <asm/thread_info.h>
+#include <asm/cpufeature.h>
 #include <asm/processor.h>
 #include <asm/insn.h>
 #include <asm/vector.h>
@@ -69,6 +70,16 @@ void riscv_v_ucontext_save(struct task_struct *tsk)
 int riscv_v_setup_vsize(void)
 {
 	unsigned long this_vsize;
+	bool v_always_on = false;
+
+	/*
+	 * has_vstate_opt() cannot be used here if called from riscv_fill_hwcap(), before
+	 * apply_boot_alternatives(),
+	 */
+	if (__riscv_isa_extension_available(NULL, RISCV_ISA_EXT_ZVE32X) && riscv_v_vstate_opt) {
+		v_always_on = true;
+		csr_set(CSR_SSTATUS, SR_VS_INITIAL);
+	}
 
 	/*
 	 * There are 32 vector registers with vlenb length.
@@ -81,9 +92,11 @@ int riscv_v_setup_vsize(void)
 		return 0;
 	}
 
-	riscv_v_enable();
+	if (!v_always_on)
+		riscv_v_enable();
 	this_vsize = csr_read(CSR_VLENB) * 32;
-	riscv_v_disable();
+	if (!v_always_on)
+		riscv_v_disable();
 
 	if (!riscv_v_vsize) {
 		riscv_v_vsize = this_vsize;
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 0/8] riscv: optimize mode switch latency for Vector
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
                   ` (7 preceding siblings ...)
  2026-09-18 21:51 ` [PATCH v6 8/8] riscv: vector: optimize vstate operations Andy Chiu
@ 2026-10-06  9:40 ` patchwork-bot+linux-riscv
  2026-10-07 14:20   ` Paul Walmsley
  2026-10-06 10:01 ` Paul Walmsley
  9 siblings, 1 reply; 15+ messages in thread
From: patchwork-bot+linux-riscv @ 2026-10-06  9:40 UTC (permalink / raw)
  To: Andy Chiu
  Cc: linux-riscv, pjw, palmer, aou, alex, andybnac, dfustini,
	greentime.hu, linux-kernel, olof

Hello:

This series was applied to riscv/linux.git (for-next)
by Paul Walmsley <pjw@kernel.org>:

On Fri, 18 Sep 2026 16:51:14 -0500 you wrote:
> This series provide several optimizations targeting system call latency
> regarding vector context management. Before the series, the kernel
> handled user's vector context in an conservative way, where registers
> were null out and VS is tracked as DIRTY. This introduce excess context
> saving and restoring when there is a context swicth.  Also, the kernel
> turned off Vector at the exception entry, making all in-kernel vector
> usecase take the serialization cost, which includes context switch and
> user copies. The cost is not easy to hide as vector code are usually sit
> right after enabling V.
> 
> [...]

Here is the summary with links:
  - [v6,1/8] riscv: do not read csr in vector context switch
    https://git.kernel.org/riscv/c/5fd018c630e2
  - [v6,2/8] riscv: vector: refactor context switch for kernel-mode vector
    https://git.kernel.org/riscv/c/4740b934ebe7
  - [v6,3/8] selftest: riscv: test vectorized user copy
    https://git.kernel.org/riscv/c/52318cf0fa6e
  - [v6,4/8] riscv: vector: refactor vector context operations
    https://git.kernel.org/riscv/c/1a5c75ee3d09
  - [v6,5/8] riscv: clarify vector state semantics on syscall and context switch
    https://git.kernel.org/riscv/c/cc12e55016ba
  - [v6,6/8] riscv: vector: adjust ptrace and signal behavior for INITIAL state
    https://git.kernel.org/riscv/c/0d4b43a7734d
  - [v6,7/8] selftests: riscv: Extend vector tests for sigreturn and ptrace
    https://git.kernel.org/riscv/c/6740ec489dd7
  - [v6,8/8] riscv: vector: optimize vstate operations
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 0/8] riscv: optimize mode switch latency for Vector
  2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
                   ` (8 preceding siblings ...)
  2026-10-06  9:40 ` [PATCH v6 0/8] riscv: optimize mode switch latency for Vector patchwork-bot+linux-riscv
@ 2026-10-06 10:01 ` Paul Walmsley
  9 siblings, 0 replies; 15+ messages in thread
From: Paul Walmsley @ 2026-10-06 10:01 UTC (permalink / raw)
  To: Andy Chiu
  Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	linux-riscv, andybnac, dfustini, greentime.hu, linux-kernel, olof

On Fri, 18 Sep 2026, Andy Chiu wrote:

> This series provide several optimizations targeting system call latency
> regarding vector context management. Before the series, the kernel
> handled user's vector context in an conservative way, where registers
> were null out and VS is tracked as DIRTY. This introduce excess context
> saving and restoring when there is a context swicth.  Also, the kernel
> turned off Vector at the exception entry, making all in-kernel vector
> usecase take the serialization cost, which includes context switch and
> user copies. The cost is not easy to hide as vector code are usually sit
> right after enabling V.
> 
> Since vector register are set to a known state at syscall exit, the
> series set VS to INIT at syscall entry and null out the vector register
> at the exit, skipping unnecessary saves and restores. The series also
> introduce riscv_novstateopt, when unset, enables vector in the kernel
> mode, and do not perform register nulling on the syscall fast path,
> where there is no context switch or kernel-mode vector during the
> syscall.
> 
> With the whole series, nginx request throughput vs base on a four-core
> Ascalon-S, by served page size (* = statistically distinct):
> 
>        86B     1KB     2KB     4KB     8KB     16KB    32KB
>   v5   +0.79%* +1.36%* +2.33%* +2.02%* +0.14%  +0.93%  +2.42%*
>   v6   +0.33%* -0.23%  +2.88%* +2.19%* +1.02%  +1.14%  +1.40%*
> 
> This series depends on [1], which is now queued in the KVM RISC-V tree
> [2]. For those who prefer git, the series is also available at [3].

Thanks.  Given the KVM fixes dependency, it's probably best to wait to 
merge most of these until v7.5.  But I think it's OK to pick up 1, 2, and 
3?  Will queue those for v7.4.


- Paul

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 1/8] riscv: do not read csr in vector context switch
  2026-09-18 21:51 ` [PATCH v6 1/8] riscv: do not read csr in vector context switch Andy Chiu
@ 2026-10-06 10:01   ` Paul Walmsley
  0 siblings, 0 replies; 15+ messages in thread
From: Paul Walmsley @ 2026-10-06 10:01 UTC (permalink / raw)
  To: Andy Chiu
  Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	linux-riscv, andybnac, dfustini, greentime.hu, daichengrong,
	Yong-Xuan Wang

On Fri, 18 Sep 2026, Andy Chiu wrote:

> CSR operation can be costly as it may introduces serialization. Instead
> of reading from sstatus.vs to the detect voluntary context switch in
> kernel-mode vector, we can read the context depth from riscv_v_flags, as
> it is always non-zero on a trap-introduced context switch.
> 
> Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>

Thanks, queued for v7.4.


- Paul

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 2/8] riscv: vector: refactor context switch for kernel-mode vector
  2026-09-18 21:51 ` [PATCH v6 2/8] riscv: vector: refactor context switch for kernel-mode vector Andy Chiu
@ 2026-10-06 10:02   ` Paul Walmsley
  0 siblings, 0 replies; 15+ messages in thread
From: Paul Walmsley @ 2026-10-06 10:02 UTC (permalink / raw)
  To: Andy Chiu
  Cc: Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	linux-riscv, andybnac, dfustini, greentime.hu, daichengrong,
	Yong-Xuan Wang

On Fri, 18 Sep 2026, Andy Chiu wrote:

> When context switching out a thread in preemptible kernel-mode vector,
> there are 2 distinct cases: One is voluntary switch and the other is
> trap-introduced switch. We don't need to save vector context for
> voluntary switch and the context is always !dirty as long as we clear
> the dirty bit at riscv_v_context_nesting_end().
> 
> Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>

Thanks, queued for v7.4.


- Paul

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 3/8] selftest: riscv: test vectorized user copy
  2026-09-18 21:51 ` [PATCH v6 3/8] selftest: riscv: test vectorized user copy Andy Chiu
@ 2026-10-06 10:02   ` Paul Walmsley
  0 siblings, 0 replies; 15+ messages in thread
From: Paul Walmsley @ 2026-10-06 10:02 UTC (permalink / raw)
  To: Andy Chiu
  Cc: Shuah Khan, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Nathan Chancellor, Nick Desaulniers,
	Bill Wendling, Justin Stitt, linux-kselftest, linux-riscv, llvm,
	andybnac, dfustini, greentime.hu, Sergey Matyukevich, Zong Li,
	Yong-Xuan Wang

On Fri, 18 Sep 2026, Andy Chiu wrote:

> Add a test that pushes a 1KiB pseudo-random stream through a pipe, so
> write() and read() copy it in and out of the kernel on the vectorized
> uaccess path, and compares the result against the original. Both buffers
> shift every iteration to sweep all relative alignments, and 16 processes
> per online CPU keep the vector unit contended. The compare is a byte at a
> time loop through volatile pointers rather than memcmp(), keeping it scalar
> and independent of the user vector state.
> 
> Assisted-by: Claude-Code:claude-opus-5
> Signed-off-by: Andy Chiu <tchiu@tenstorrent.com>

Thanks, queued for v7.4.


- Paul

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v6 0/8] riscv: optimize mode switch latency for Vector
  2026-10-06  9:40 ` [PATCH v6 0/8] riscv: optimize mode switch latency for Vector patchwork-bot+linux-riscv
@ 2026-10-07 14:20   ` Paul Walmsley
  0 siblings, 0 replies; 15+ messages in thread
From: Paul Walmsley @ 2026-10-07 14:20 UTC (permalink / raw)
  To: patchwork-bot+linux-riscv
  Cc: Andy Chiu, linux-riscv, pjw, palmer, aou, alex, andybnac,
	dfustini, greentime.hu, linux-kernel, olof

On Tue, 6 Oct 2026, patchwork-bot+linux-riscv@kernel.org wrote:

> Hello:
> 
> This series was applied to riscv/linux.git (for-next)
> by Paul Walmsley <pjw@kernel.org>:
> 

[ ... ]

> Here is the summary with links:
>   - [v6,1/8] riscv: do not read csr in vector context switch
>     https://git.kernel.org/riscv/c/5fd018c630e2
>   - [v6,2/8] riscv: vector: refactor context switch for kernel-mode vector
>     https://git.kernel.org/riscv/c/4740b934ebe7
>   - [v6,3/8] selftest: riscv: test vectorized user copy
>     https://git.kernel.org/riscv/c/52318cf0fa6e
>   - [v6,4/8] riscv: vector: refactor vector context operations
>     https://git.kernel.org/riscv/c/1a5c75ee3d09
>   - [v6,5/8] riscv: clarify vector state semantics on syscall and context switch
>     https://git.kernel.org/riscv/c/cc12e55016ba
>   - [v6,6/8] riscv: vector: adjust ptrace and signal behavior for INITIAL state
>     https://git.kernel.org/riscv/c/0d4b43a7734d
>   - [v6,7/8] selftests: riscv: Extend vector tests for sigreturn and ptrace
>     https://git.kernel.org/riscv/c/6740ec489dd7
>   - [v6,8/8] riscv: vector: optimize vstate operations
>     (no matching commit)

Just to clarify, I only applied the first three patches, for the reason
mentioned earlier.


- Paul

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-10-07 14:20 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-18 21:51 [PATCH v6 0/8] riscv: optimize mode switch latency for Vector Andy Chiu
2026-09-18 21:51 ` [PATCH v6 1/8] riscv: do not read csr in vector context switch Andy Chiu
2026-10-06 10:01   ` Paul Walmsley
2026-09-18 21:51 ` [PATCH v6 2/8] riscv: vector: refactor context switch for kernel-mode vector Andy Chiu
2026-10-06 10:02   ` Paul Walmsley
2026-09-18 21:51 ` [PATCH v6 3/8] selftest: riscv: test vectorized user copy Andy Chiu
2026-10-06 10:02   ` Paul Walmsley
2026-09-18 21:51 ` [PATCH v6 4/8] riscv: vector: refactor vector context operations Andy Chiu
2026-09-18 21:51 ` [PATCH v6 5/8] riscv: clarify vector state semantics on syscall and context switch Andy Chiu
2026-09-18 21:51 ` [PATCH v6 6/8] riscv: vector: adjust ptrace and signal behavior for INITIAL state Andy Chiu
2026-09-18 21:51 ` [PATCH v6 7/8] selftests: riscv: Extend vector tests for sigreturn and ptrace Andy Chiu
2026-09-18 21:51 ` [PATCH v6 8/8] riscv: vector: optimize vstate operations Andy Chiu
2026-10-06  9:40 ` [PATCH v6 0/8] riscv: optimize mode switch latency for Vector patchwork-bot+linux-riscv
2026-10-07 14:20   ` Paul Walmsley
2026-10-06 10:01 ` Paul Walmsley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox