[PATCH] RISC-V: Clobber V registers on syscalls

Björn Töpel bjorn at kernel.org
Thu Jun 22 09:38:36 PDT 2023


Andy Chiu <andy.chiu at sifive.com> writes:

> On Thu, Jun 22, 2023 at 5:40 AM Björn Töpel <bjorn at kernel.org> wrote:
>>
>> Andy Chiu <andy.chiu at sifive.com> writes:
>>
>> >> +static inline void riscv_v_vstate_discard(struct pt_regs *regs)
>> >> +{
>> >> +       unsigned long vl;
>> >> +
>> >> +       if (!riscv_v_vstate_query(regs))
>> >> +               return;
>> >> +
>> >> +       riscv_v_vstate_on(regs);
>> >
>> > Do we need this riscv_v_vstate_on()?  If it is not on we'd return
>> > early in the previous "if" statement, right?
>>
>> riscv_v_vstate_on() just set the state to Initial, right? Or do you mean
>> that riscv_v_vstate_query() is too much, and we should only check if the
>> state is dirty?
>>
>> >> +
>> >> +       riscv_v_enable();
>> >> +       asm volatile (
>> >> +               ".option push\n\t"
>> >> +               ".option arch, +v\n\t"
>> >> +               "vsetvli        %0, x0, e8, m8, ta, ma\n\t"
>> >> +               "vmv.v.i        v0, 0\n\t"
>> >> +               "vmv.v.i        v8, 0\n\t"
>> >> +               "vmv.v.i        v16, 0\n\t"
>> >> +               "vmv.v.i        v24, 0\n\t"
>> >> +               ".option pop\n\t"
>> >> +               : "=&r" (vl) : : "memory");
>> >> +       riscv_v_disable();
>> >
>> > Maybe consider cleaning the vstate (status.vs) here. As such we don't
>> > have to save V during context switch.
>>
>> It's late, and I'm slower than usual. The regs are cleared, and the
>> state is Initial. No save on context switch, but restore, right?
>
> Yes, it's my bad, you are right. I sometime messed around the "real"
> status.VS with the one in the userspace context :P
>
>>
>> > Or, maybe we could set vstate as off during syscall and discard V-reg
>> > + restore status.VS when returning back to userspace?
>>
>> Hmm, interesting. We need to track the status.VS to restore somewhere...
>
> Maybe something like this?
>
> diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
> index 04c0b07bf6cd..79de9ca83391 100644
> --- a/arch/riscv/include/asm/vector.h
> +++ b/arch/riscv/include/asm/vector.h
> @@ -43,6 +43,11 @@ static inline void riscv_v_vstate_on(struct pt_regs *regs)
>  	regs->status = (regs->status & ~SR_VS) | SR_VS_INITIAL;
>  }
>  
> +static inline void riscv_v_vstate_dirty(struct pt_regs *regs)
> +{
> +	regs->status = (regs->status & ~SR_VS) | SR_VS_DIRTY;
> +}
> +
>  static inline bool riscv_v_vstate_query(struct pt_regs *regs)
>  {
>  	return (regs->status & SR_VS) != 0;
> @@ -163,6 +168,24 @@ static inline void __switch_to_vector(struct task_struct *prev,
>  void riscv_v_vstate_ctrl_init(struct task_struct *tsk);
>  bool riscv_v_vstate_ctrl_user_allowed(void);
>  
> +static inline void riscv_v_vstate_discard(struct pt_regs *regs)
> +{
> +	unsigned long vl;
> +
> +	riscv_v_enable();
> +	asm volatile (
> +			".option push\n\t"
> +			".option arch, +v\n\t"
> +			"vsetvli        %0, x0, e8, m8, ta, ma\n\t"
> +			"vmv.v.i        v0, 0\n\t"
> +			"vmv.v.i        v8, 0\n\t"
> +			"vmv.v.i        v16, 0\n\t"
> +			"vmv.v.i        v24, 0\n\t"
> +			".option pop\n\t"
> +			: "=&r" (vl) : : "memory");
> +	riscv_v_disable();
> +}
> +
>  #else /* ! CONFIG_RISCV_ISA_V  */
>  
>  struct pt_regs;
> @@ -178,6 +201,8 @@ static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
>  #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_dirty(regs)		do {} while (0)
> +#define riscv_v_vstate_discard(regs)		do {} while (0)
>  
>  #endif /* CONFIG_RISCV_ISA_V */
>  
> diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
> index 24d309c6ab8d..e36b69c9b07f 100644
> --- a/arch/riscv/kernel/traps.c
> +++ b/arch/riscv/kernel/traps.c
> @@ -291,10 +291,14 @@ asmlinkage __visible __trap_section void do_trap_ecall_u(struct pt_regs *regs)
>  {
>  	if (user_mode(regs)) {
>  		ulong syscall = regs->a7;
> +		bool v_is_on;
>  
>  		regs->epc += 4;
>  		regs->orig_a0 = regs->a0;
>  
> +		v_is_on = riscv_v_vstate_query(regs);
> +		riscv_v_vstate_off(regs);
> +
>  		syscall = syscall_enter_from_user_mode(regs, syscall);
>  
>  		if (syscall < NR_syscalls)
> @@ -303,6 +307,10 @@ asmlinkage __visible __trap_section void do_trap_ecall_u(struct pt_regs *regs)
>  			regs->a0 = -ENOSYS;
>  
>  		syscall_exit_to_user_mode(regs);
> +		if (v_is_on) {
> +			riscv_v_vstate_discard(regs);
> +			riscv_v_vstate_dirty(regs);

Ah! Neat! Why dirty, instead of just keeping the "set to Initial" from
my diff?

This flow does avoid some context switch costs, but I wonder if this is
some that can be added later, when we can more reliable measure the
overhead. Premature optimization, and all that. ;-)


Björn



More information about the linux-riscv mailing list