[[PATCH] v4 2/4] riscv: clarify vector state semantics on syscall and context switch
Andy Chiu
tchiu at tenstorrent.com
Thu May 28 12:09:13 PDT 2026
From: daichengrong <daichengrong at 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 at iscas.ac.cn>
Co-developed-by: Andy Chiu <tchiu at tenstorrent.com>
Signed-off-by: Andy Chiu <tchiu at 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 45820cd900d6..c699cec27c79 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);
@@ -95,7 +104,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);
}
@@ -288,16 +297,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)
{
@@ -312,20 +311,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);
}
}
@@ -396,6 +401,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));
}
}
@@ -421,7 +427,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
More information about the linux-riscv
mailing list