* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-21 14:26 ` Björn Töpel
@ 2023-06-21 14:44 ` Darius Rad
2023-06-21 18:16 ` Palmer Dabbelt
2023-06-21 14:50 ` Andy Chiu
2023-06-21 16:47 ` Rémi Denis-Courmont
2 siblings, 1 reply; 27+ messages in thread
From: Darius Rad @ 2023-06-21 14:44 UTC (permalink / raw)
To: Björn Töpel; +Cc: Palmer Dabbelt, linux-riscv, Andy Chiu
On Wed, Jun 21, 2023 at 04:26:14PM +0200, Björn Töpel wrote:
> Palmer Dabbelt <palmer@rivosinc.com> writes:
>
> > On Mon, 19 Jun 2023 12:01:20 PDT (-0700), bjorn@kernel.org wrote:
> >> Palmer Dabbelt <palmer@rivosinc.com> writes:
> >>
> >> [...]
> >>
> >>>>> + riscv_v_vstate_off(regs);
> >>>>> +
> >>>>
> >>>> Not off, right? Isn't it __riscv_v_vstate_clean() that you'd like to
> >>>> call? Something like:
> >>>>
> >>>> static void vstate_discard(struct pt_regs *regs)
> >>>> {
> >>>> if ((regs->status & SR_VS) == SR_VS_DIRTY)
> >>>> __riscv_v_vstate_clean(regs);
> >>>> }
> >>>>
> >>>> Complemented by a !V config variant.
> >>>
> >>> I think it's just a question of what we're trying to do here: clean
> >>> avoids the kernel V state save, but unless the kernel decides to use V
> >>> during the syscall the register contents will still be usable by
> >>> userspace. Maybe that's fine and we can just rely on the ISA spec,
> >>> though? I sent another patch to just document it in Linux, even if it's
> >>> in the ISA spec it seems worth having in the kernel as well.
> >>>
> >>> That said, I think the right thing to do here might be to zero the V
> >>> register state and set it to initial: that way we can prevent userspace
> >>> from accidentally relying on the state save, but we can also avoid the
> >>> trap that would come from turning it off. That lets us give the
> >>> hardware a nice clean indication when the V state isn't in use, which
> >>> will hopefully help us avoid the save/restore performance issues that
> >>> other ports have hit.
> >>
> >> FWIW, I think that's a much better idea than turning V off. I also like
> >> that it'll preventing userland to rely on pre-ecall state.
> >
> > OK, anyone else opposed?
> >
> > We're kind of in the weeds on performance, I think we'd need HW to know
> > for sure if either is an issue. Seems best to just play it safe WRT the
> > uABI for now, we can always deal with any performance issues if the
> > exist.
>
> Here's the patch you mentioned at the PW synchup; I've kept the Subject
> and such if you wan't to apply it. LMK if you'd like a proper one.
>
> --
>
> Subject: [PATCH] riscv: Discard vector state on syscalls
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> The RISC-V vector specification states:
> Executing a system call causes all caller-saved vector registers
> (v0-v31, vl, vtype) and vstart to become unspecified.
>
> The vector status is set to Initial, and the vector state is
> explicitly zeroed. That way we can prevent userspace from accidentally
> relying on the stated save.
Is it worth clobbering with all 1s, rather than zero, for consistency with
other vector behavior (i.e., tail/mask agnostic) and for the reasons given
in the vector spec for not doing so with zero?
>
> Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
> ---
> arch/riscv/include/asm/vector.h | 24 ++++++++++++++++++++++++
> arch/riscv/kernel/traps.c | 2 ++
> 2 files changed, 26 insertions(+)
>
> diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
> index 04c0b07bf6cd..b3020d064f42 100644
> --- a/arch/riscv/include/asm/vector.h
> +++ b/arch/riscv/include/asm/vector.h
> @@ -163,6 +163,29 @@ 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;
> +
> + if (!riscv_v_vstate_query(regs))
> + return;
> +
> + riscv_v_vstate_on(regs);
> +
> + 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,7 @@ 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_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 05ffdcd1424e..00c68b57ff88 100644
> --- a/arch/riscv/kernel/traps.c
> +++ b/arch/riscv/kernel/traps.c
> @@ -295,6 +295,8 @@ asmlinkage __visible __trap_section void do_trap_ecall_u(struct pt_regs *regs)
> regs->epc += 4;
> regs->orig_a0 = regs->a0;
>
> + riscv_v_vstate_discard(regs);
> +
> syscall = syscall_enter_from_user_mode(regs, syscall);
>
> if (syscall < NR_syscalls)
>
> base-commit: abd6152d6046ddc4be1040b6206bee2e025e8a79
> --
> 2.39.2
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-21 14:44 ` Darius Rad
@ 2023-06-21 18:16 ` Palmer Dabbelt
0 siblings, 0 replies; 27+ messages in thread
From: Palmer Dabbelt @ 2023-06-21 18:16 UTC (permalink / raw)
To: Darius Rad; +Cc: bjorn, linux-riscv, andy.chiu
On Wed, 21 Jun 2023 07:44:51 PDT (-0700), Darius Rad wrote:
> On Wed, Jun 21, 2023 at 04:26:14PM +0200, Björn Töpel wrote:
>> Palmer Dabbelt <palmer@rivosinc.com> writes:
>>
>> > On Mon, 19 Jun 2023 12:01:20 PDT (-0700), bjorn@kernel.org wrote:
>> >> Palmer Dabbelt <palmer@rivosinc.com> writes:
>> >>
>> >> [...]
>> >>
>> >>>>> + riscv_v_vstate_off(regs);
>> >>>>> +
>> >>>>
>> >>>> Not off, right? Isn't it __riscv_v_vstate_clean() that you'd like to
>> >>>> call? Something like:
>> >>>>
>> >>>> static void vstate_discard(struct pt_regs *regs)
>> >>>> {
>> >>>> if ((regs->status & SR_VS) == SR_VS_DIRTY)
>> >>>> __riscv_v_vstate_clean(regs);
>> >>>> }
>> >>>>
>> >>>> Complemented by a !V config variant.
>> >>>
>> >>> I think it's just a question of what we're trying to do here: clean
>> >>> avoids the kernel V state save, but unless the kernel decides to use V
>> >>> during the syscall the register contents will still be usable by
>> >>> userspace. Maybe that's fine and we can just rely on the ISA spec,
>> >>> though? I sent another patch to just document it in Linux, even if it's
>> >>> in the ISA spec it seems worth having in the kernel as well.
>> >>>
>> >>> That said, I think the right thing to do here might be to zero the V
>> >>> register state and set it to initial: that way we can prevent userspace
>> >>> from accidentally relying on the state save, but we can also avoid the
>> >>> trap that would come from turning it off. That lets us give the
>> >>> hardware a nice clean indication when the V state isn't in use, which
>> >>> will hopefully help us avoid the save/restore performance issues that
>> >>> other ports have hit.
>> >>
>> >> FWIW, I think that's a much better idea than turning V off. I also like
>> >> that it'll preventing userland to rely on pre-ecall state.
>> >
>> > OK, anyone else opposed?
>> >
>> > We're kind of in the weeds on performance, I think we'd need HW to know
>> > for sure if either is an issue. Seems best to just play it safe WRT the
>> > uABI for now, we can always deal with any performance issues if the
>> > exist.
>>
>> Here's the patch you mentioned at the PW synchup; I've kept the Subject
>> and such if you wan't to apply it. LMK if you'd like a proper one.
>>
>> --
>>
>> Subject: [PATCH] riscv: Discard vector state on syscalls
>> MIME-Version: 1.0
>> Content-Type: text/plain; charset=UTF-8
>> Content-Transfer-Encoding: 8bit
>>
>> The RISC-V vector specification states:
>> Executing a system call causes all caller-saved vector registers
>> (v0-v31, vl, vtype) and vstart to become unspecified.
>>
>> The vector status is set to Initial, and the vector state is
>> explicitly zeroed. That way we can prevent userspace from accidentally
>> relying on the stated save.
>
> Is it worth clobbering with all 1s, rather than zero, for consistency with
> other vector behavior (i.e., tail/mask agnostic) and for the reasons given
> in the vector spec for not doing so with zero?
Might be. I guess the assumption was that vs==initial means all 0's,
but unless I'm missing something there's no rules for what initial means
in the spec.
>
>>
>> Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
>> ---
>> arch/riscv/include/asm/vector.h | 24 ++++++++++++++++++++++++
>> arch/riscv/kernel/traps.c | 2 ++
>> 2 files changed, 26 insertions(+)
>>
>> diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
>> index 04c0b07bf6cd..b3020d064f42 100644
>> --- a/arch/riscv/include/asm/vector.h
>> +++ b/arch/riscv/include/asm/vector.h
>> @@ -163,6 +163,29 @@ 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;
>> +
>> + if (!riscv_v_vstate_query(regs))
>> + return;
>> +
>> + riscv_v_vstate_on(regs);
>> +
>> + 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,7 @@ 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_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 05ffdcd1424e..00c68b57ff88 100644
>> --- a/arch/riscv/kernel/traps.c
>> +++ b/arch/riscv/kernel/traps.c
>> @@ -295,6 +295,8 @@ asmlinkage __visible __trap_section void do_trap_ecall_u(struct pt_regs *regs)
>> regs->epc += 4;
>> regs->orig_a0 = regs->a0;
>>
>> + riscv_v_vstate_discard(regs);
>> +
>> syscall = syscall_enter_from_user_mode(regs, syscall);
>>
>> if (syscall < NR_syscalls)
>>
>> base-commit: abd6152d6046ddc4be1040b6206bee2e025e8a79
>> --
>> 2.39.2
>>
>> _______________________________________________
>> linux-riscv mailing list
>> linux-riscv@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-riscv
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-21 14:26 ` Björn Töpel
2023-06-21 14:44 ` Darius Rad
@ 2023-06-21 14:50 ` Andy Chiu
2023-06-21 21:40 ` Björn Töpel
2023-06-21 16:47 ` Rémi Denis-Courmont
2 siblings, 1 reply; 27+ messages in thread
From: Andy Chiu @ 2023-06-21 14:50 UTC (permalink / raw)
To: Björn Töpel; +Cc: Palmer Dabbelt, linux-riscv
On Wed, Jun 21, 2023 at 10:26 PM Björn Töpel <bjorn@kernel.org> wrote:
>
> Palmer Dabbelt <palmer@rivosinc.com> writes:
>
> > On Mon, 19 Jun 2023 12:01:20 PDT (-0700), bjorn@kernel.org wrote:
> >> Palmer Dabbelt <palmer@rivosinc.com> writes:
> >>
> >> [...]
> >>
> >>>>> + riscv_v_vstate_off(regs);
> >>>>> +
> >>>>
> >>>> Not off, right? Isn't it __riscv_v_vstate_clean() that you'd like to
> >>>> call? Something like:
> >>>>
> >>>> static void vstate_discard(struct pt_regs *regs)
> >>>> {
> >>>> if ((regs->status & SR_VS) == SR_VS_DIRTY)
> >>>> __riscv_v_vstate_clean(regs);
> >>>> }
> >>>>
> >>>> Complemented by a !V config variant.
> >>>
> >>> I think it's just a question of what we're trying to do here: clean
> >>> avoids the kernel V state save, but unless the kernel decides to use V
> >>> during the syscall the register contents will still be usable by
> >>> userspace. Maybe that's fine and we can just rely on the ISA spec,
> >>> though? I sent another patch to just document it in Linux, even if it's
> >>> in the ISA spec it seems worth having in the kernel as well.
> >>>
> >>> That said, I think the right thing to do here might be to zero the V
> >>> register state and set it to initial: that way we can prevent userspace
> >>> from accidentally relying on the state save, but we can also avoid the
> >>> trap that would come from turning it off. That lets us give the
> >>> hardware a nice clean indication when the V state isn't in use, which
> >>> will hopefully help us avoid the save/restore performance issues that
> >>> other ports have hit.
> >>
> >> FWIW, I think that's a much better idea than turning V off. I also like
> >> that it'll preventing userland to rely on pre-ecall state.
> >
> > OK, anyone else opposed?
> >
> > We're kind of in the weeds on performance, I think we'd need HW to know
> > for sure if either is an issue. Seems best to just play it safe WRT the
> > uABI for now, we can always deal with any performance issues if the
> > exist.
>
> Here's the patch you mentioned at the PW synchup; I've kept the Subject
> and such if you wan't to apply it. LMK if you'd like a proper one.
>
> --
>
> Subject: [PATCH] riscv: Discard vector state on syscalls
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> The RISC-V vector specification states:
> Executing a system call causes all caller-saved vector registers
> (v0-v31, vl, vtype) and vstart to become unspecified.
>
> The vector status is set to Initial, and the vector state is
> explicitly zeroed. That way we can prevent userspace from accidentally
> relying on the stated save.
>
> Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
> ---
> arch/riscv/include/asm/vector.h | 24 ++++++++++++++++++++++++
> arch/riscv/kernel/traps.c | 2 ++
> 2 files changed, 26 insertions(+)
>
> diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
> index 04c0b07bf6cd..b3020d064f42 100644
> --- a/arch/riscv/include/asm/vector.h
> +++ b/arch/riscv/include/asm/vector.h
> @@ -163,6 +163,29 @@ 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;
> +
> + 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_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. Or, maybe we could set vstate as
off during syscall and discard V-reg + restore status.VS when
returning back to userspace?
> +}
> +
> #else /* ! CONFIG_RISCV_ISA_V */
>
> struct pt_regs;
> @@ -178,6 +201,7 @@ 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_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 05ffdcd1424e..00c68b57ff88 100644
> --- a/arch/riscv/kernel/traps.c
> +++ b/arch/riscv/kernel/traps.c
> @@ -295,6 +295,8 @@ asmlinkage __visible __trap_section void do_trap_ecall_u(struct pt_regs *regs)
> regs->epc += 4;
> regs->orig_a0 = regs->a0;
>
> + riscv_v_vstate_discard(regs);
> +
> syscall = syscall_enter_from_user_mode(regs, syscall);
>
> if (syscall < NR_syscalls)
>
> base-commit: abd6152d6046ddc4be1040b6206bee2e025e8a79
> --
> 2.39.2
Agree. It is better to clean V registers instead of turning off Vector.
Regards,
Andy
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-21 14:50 ` Andy Chiu
@ 2023-06-21 21:40 ` Björn Töpel
2023-06-22 15:47 ` Andy Chiu
0 siblings, 1 reply; 27+ messages in thread
From: Björn Töpel @ 2023-06-21 21:40 UTC (permalink / raw)
To: Andy Chiu; +Cc: Palmer Dabbelt, linux-riscv
Andy Chiu <andy.chiu@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?
> 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...
Björn
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-21 21:40 ` Björn Töpel
@ 2023-06-22 15:47 ` Andy Chiu
2023-06-22 16:38 ` Björn Töpel
0 siblings, 1 reply; 27+ messages in thread
From: Andy Chiu @ 2023-06-22 15:47 UTC (permalink / raw)
To: bjorn; +Cc: andy.chiu, linux-riscv, palmer
On Thu, Jun 22, 2023 at 5:40 AM Björn Töpel <bjorn@kernel.org> wrote:
>
> Andy Chiu <andy.chiu@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);
+ }
} else {
irqentry_state_t state = irqentry_nmi_enter(regs);
>
>
> Björn
Thanks,
Andy
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-22 15:47 ` Andy Chiu
@ 2023-06-22 16:38 ` Björn Töpel
2023-06-24 6:54 ` Andy Chiu
2023-06-24 8:41 ` Andy Chiu
0 siblings, 2 replies; 27+ messages in thread
From: Björn Töpel @ 2023-06-22 16:38 UTC (permalink / raw)
To: Andy Chiu; +Cc: andy.chiu, linux-riscv, palmer
Andy Chiu <andy.chiu@sifive.com> writes:
> On Thu, Jun 22, 2023 at 5:40 AM Björn Töpel <bjorn@kernel.org> wrote:
>>
>> Andy Chiu <andy.chiu@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
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-22 16:38 ` Björn Töpel
@ 2023-06-24 6:54 ` Andy Chiu
2023-06-26 15:36 ` Björn Töpel
2023-06-24 8:41 ` Andy Chiu
1 sibling, 1 reply; 27+ messages in thread
From: Andy Chiu @ 2023-06-24 6:54 UTC (permalink / raw)
To: Björn Töpel; +Cc: linux-riscv, palmer
On Fri, Jun 23, 2023 at 12:38 AM Björn Töpel <bjorn@kernel.org> wrote:
>
> Andy Chiu <andy.chiu@sifive.com> writes:
>
> > On Thu, Jun 22, 2023 at 5:40 AM Björn Töpel <bjorn@kernel.org> wrote:
> >>
> >> Andy Chiu <andy.chiu@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?
Both work, I think. But here if we set it to "on" after discarding
V-regs, then take a context switch before executing any V instructions
in user space (does not change future vstate to dirty). Then we will
leak V-regs previously set into its vstate.datap after switching back,
because we only save V context if vstate is dirty. So, I think setting
vstate to dirty is a safer option.
In your diff case, V-regs may be restored back to the previously-saved
state if the syscall caused a context switch.
I have not had a chance to test it yet because we are having a
vacation in Taiwan, and I have some other stuff to keep me busy :)
Please correct me if my thinking was wrong and I forgot some important
idea again...
>
> 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
Thanks,
Andy
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-24 6:54 ` Andy Chiu
@ 2023-06-26 15:36 ` Björn Töpel
2023-06-27 1:07 ` Andy Chiu
0 siblings, 1 reply; 27+ messages in thread
From: Björn Töpel @ 2023-06-26 15:36 UTC (permalink / raw)
To: Andy Chiu; +Cc: linux-riscv, palmer
Andy Chiu <andy.chiu@sifive.com> writes:
>> > 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?
>
> Both work, I think. But here if we set it to "on" after discarding
> V-regs, then take a context switch before executing any V instructions
> in user space (does not change future vstate to dirty). Then we will
> leak V-regs previously set into its vstate.datap after switching back,
> because we only save V context if vstate is dirty. So, I think setting
> vstate to dirty is a safer option.
Ah, yes, good point. An alternative variant is this:
---
diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index 04c0b07bf6cd..32b6115a54a5 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -139,15 +139,51 @@ static inline void riscv_v_vstate_save(struct task_struct *task,
}
}
+static inline void __riscv_v_vstate_discard(void)
+{
+ unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
+
+ 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"
+ "vsetvl %0, x0, %1\n\t"
+ ".option pop\n\t"
+ : "=&r" (vl) : "r" (vtype_inval) : "memory");
+ riscv_v_disable();
+}
+
+static inline void riscv_v_vstate_discard(struct pt_regs *regs)
+{
+ if (!riscv_v_vstate_query(regs))
+ return;
+
+ __riscv_v_vstate_discard();
+ riscv_v_vstate_on(regs);
+}
+
static inline void riscv_v_vstate_restore(struct task_struct *task,
struct pt_regs *regs)
{
- if ((regs->status & SR_VS) != SR_VS_OFF) {
+ unsigned long status = regs->status & SR_VS;
+
+ WARN_ON(status == SR_VS_DIRTY);
+
+ if (status == SR_VS_CLEAN) {
struct __riscv_v_ext_state *vstate = &task->thread.vstate;
__riscv_v_vstate_restore(vstate, vstate->datap);
__riscv_v_vstate_clean(regs);
+ return;
}
+
+ if (status == SR_VS_INITIAL)
+ __riscv_v_vstate_discard();
}
static inline void __switch_to_vector(struct task_struct *prev,
@@ -178,6 +214,7 @@ 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_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 5158961ea977..5ff63a784a6d 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -296,6 +296,8 @@ asmlinkage __visible __trap_section void do_trap_ecall_u(struct pt_regs *regs)
regs->epc += 4;
regs->orig_a0 = regs->a0;
+ riscv_v_vstate_discard(regs);
+
syscall = syscall_enter_from_user_mode(regs, syscall);
if (syscall < NR_syscalls)
---
Here, we simply discard the regs if the state is Initial. Thoughts?
Björn
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-26 15:36 ` Björn Töpel
@ 2023-06-27 1:07 ` Andy Chiu
2023-06-27 6:33 ` Björn Töpel
0 siblings, 1 reply; 27+ messages in thread
From: Andy Chiu @ 2023-06-27 1:07 UTC (permalink / raw)
To: Björn Töpel; +Cc: linux-riscv, palmer
On Mon, Jun 26, 2023 at 11:36 PM Björn Töpel <bjorn@kernel.org> wrote:
>
> Andy Chiu <andy.chiu@sifive.com> writes:
>
> >> > 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?
> >
> > Both work, I think. But here if we set it to "on" after discarding
> > V-regs, then take a context switch before executing any V instructions
> > in user space (does not change future vstate to dirty). Then we will
> > leak V-regs previously set into its vstate.datap after switching back,
> > because we only save V context if vstate is dirty. So, I think setting
> > vstate to dirty is a safer option.
>
> Ah, yes, good point. An alternative variant is this:
>
> ---
> diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
> index 04c0b07bf6cd..32b6115a54a5 100644
> --- a/arch/riscv/include/asm/vector.h
> +++ b/arch/riscv/include/asm/vector.h
> @@ -139,15 +139,51 @@ static inline void riscv_v_vstate_save(struct task_struct *task,
> }
> }
>
> +static inline void __riscv_v_vstate_discard(void)
> +{
> + unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
> +
> + 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"
> + "vsetvl %0, x0, %1\n\t"
> + ".option pop\n\t"
> + : "=&r" (vl) : "r" (vtype_inval) : "memory");
> + riscv_v_disable();
> +}
> +
> +static inline void riscv_v_vstate_discard(struct pt_regs *regs)
> +{
> + if (!riscv_v_vstate_query(regs))
> + return;
> +
> + __riscv_v_vstate_discard();
> + riscv_v_vstate_on(regs);
> +}
> +
> static inline void riscv_v_vstate_restore(struct task_struct *task,
> struct pt_regs *regs)
> {
> - if ((regs->status & SR_VS) != SR_VS_OFF) {
> + unsigned long status = regs->status & SR_VS;
> +
> + WARN_ON(status == SR_VS_DIRTY);
> +
> + if (status == SR_VS_CLEAN) {
> struct __riscv_v_ext_state *vstate = &task->thread.vstate;
>
> __riscv_v_vstate_restore(vstate, vstate->datap);
> __riscv_v_vstate_clean(regs);
> + return;
> }
> +
> + if (status == SR_VS_INITIAL)
> + __riscv_v_vstate_discard();
> }
>
> static inline void __switch_to_vector(struct task_struct *prev,
> @@ -178,6 +214,7 @@ 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_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 5158961ea977..5ff63a784a6d 100644
> --- a/arch/riscv/kernel/traps.c
> +++ b/arch/riscv/kernel/traps.c
> @@ -296,6 +296,8 @@ asmlinkage __visible __trap_section void do_trap_ecall_u(struct pt_regs *regs)
> regs->epc += 4;
> regs->orig_a0 = regs->a0;
>
> + riscv_v_vstate_discard(regs);
> +
> syscall = syscall_enter_from_user_mode(regs, syscall);
>
> if (syscall < NR_syscalls)
>
> ---
>
>
> Here, we simply discard the regs if the state is Initial. Thoughts?
>
>
> Björn
Yes, it makes sense to me to handle the initial state in vstate_restore.
Thanks,
Andy
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-27 1:07 ` Andy Chiu
@ 2023-06-27 6:33 ` Björn Töpel
0 siblings, 0 replies; 27+ messages in thread
From: Björn Töpel @ 2023-06-27 6:33 UTC (permalink / raw)
To: Andy Chiu; +Cc: linux-riscv, palmer
Andy Chiu <andy.chiu@sifive.com> writes:
> On Mon, Jun 26, 2023 at 11:36 PM Björn Töpel <bjorn@kernel.org> wrote:
>>
>> Andy Chiu <andy.chiu@sifive.com> writes:
>>
>> >> > 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?
>> >
>> > Both work, I think. But here if we set it to "on" after discarding
>> > V-regs, then take a context switch before executing any V instructions
>> > in user space (does not change future vstate to dirty). Then we will
>> > leak V-regs previously set into its vstate.datap after switching back,
>> > because we only save V context if vstate is dirty. So, I think setting
>> > vstate to dirty is a safer option.
>>
>> Ah, yes, good point. An alternative variant is this:
>>
>> ---
>> diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
>> index 04c0b07bf6cd..32b6115a54a5 100644
>> --- a/arch/riscv/include/asm/vector.h
>> +++ b/arch/riscv/include/asm/vector.h
>> @@ -139,15 +139,51 @@ static inline void riscv_v_vstate_save(struct task_struct *task,
>> }
>> }
>>
>> +static inline void __riscv_v_vstate_discard(void)
>> +{
>> + unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
>> +
>> + 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"
>> + "vsetvl %0, x0, %1\n\t"
>> + ".option pop\n\t"
>> + : "=&r" (vl) : "r" (vtype_inval) : "memory");
>> + riscv_v_disable();
>> +}
>> +
>> +static inline void riscv_v_vstate_discard(struct pt_regs *regs)
>> +{
>> + if (!riscv_v_vstate_query(regs))
>> + return;
>> +
>> + __riscv_v_vstate_discard();
>> + riscv_v_vstate_on(regs);
>> +}
>> +
>> static inline void riscv_v_vstate_restore(struct task_struct *task,
>> struct pt_regs *regs)
>> {
>> - if ((regs->status & SR_VS) != SR_VS_OFF) {
>> + unsigned long status = regs->status & SR_VS;
>> +
>> + WARN_ON(status == SR_VS_DIRTY);
>> +
>> + if (status == SR_VS_CLEAN) {
>> struct __riscv_v_ext_state *vstate = &task->thread.vstate;
>>
>> __riscv_v_vstate_restore(vstate, vstate->datap);
>> __riscv_v_vstate_clean(regs);
>> + return;
>> }
>> +
>> + if (status == SR_VS_INITIAL)
>> + __riscv_v_vstate_discard();
>> }
>>
>> static inline void __switch_to_vector(struct task_struct *prev,
>> @@ -178,6 +214,7 @@ 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_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 5158961ea977..5ff63a784a6d 100644
>> --- a/arch/riscv/kernel/traps.c
>> +++ b/arch/riscv/kernel/traps.c
>> @@ -296,6 +296,8 @@ asmlinkage __visible __trap_section void do_trap_ecall_u(struct pt_regs *regs)
>> regs->epc += 4;
>> regs->orig_a0 = regs->a0;
>>
>> + riscv_v_vstate_discard(regs);
>> +
>> syscall = syscall_enter_from_user_mode(regs, syscall);
>>
>> if (syscall < NR_syscalls)
>>
>> ---
>>
>>
>> Here, we simply discard the regs if the state is Initial. Thoughts?
>>
>>
>> Björn
>
> Yes, it makes sense to me to handle the initial state in vstate_restore.
Ok! I sent out a proper v2, but without the WARN_ON to match the
behavior of the the original code.
PTAL, and let me know what you think.
Björn
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-22 16:38 ` Björn Töpel
2023-06-24 6:54 ` Andy Chiu
@ 2023-06-24 8:41 ` Andy Chiu
2023-06-26 14:54 ` Björn Töpel
1 sibling, 1 reply; 27+ messages in thread
From: Andy Chiu @ 2023-06-24 8:41 UTC (permalink / raw)
To: Björn Töpel; +Cc: linux-riscv, palmer
On Fri, Jun 23, 2023 at 12:38 AM Björn Töpel <bjorn@kernel.org> wrote:
> 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. ;-)
>
Sure, do you suggest any kinds of measurement, experiment, or
benchmarking that could give out a figure on how things are different?
>
> Björn
Thanks,
Andy
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-24 8:41 ` Andy Chiu
@ 2023-06-26 14:54 ` Björn Töpel
0 siblings, 0 replies; 27+ messages in thread
From: Björn Töpel @ 2023-06-26 14:54 UTC (permalink / raw)
To: Andy Chiu; +Cc: linux-riscv, palmer
Andy Chiu <andy.chiu@sifive.com> writes:
> On Fri, Jun 23, 2023 at 12:38 AM Björn Töpel <bjorn@kernel.org> wrote:
>> 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. ;-)
>>
>
> Sure, do you suggest any kinds of measurement, experiment, or
> benchmarking that could give out a figure on how things are different?
My take was; If you have access to actual V 1.0 hardware, and just not
Qemu, then we could do some actual real tests, measuring context switch
costs etc!
Björn
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-21 14:26 ` Björn Töpel
2023-06-21 14:44 ` Darius Rad
2023-06-21 14:50 ` Andy Chiu
@ 2023-06-21 16:47 ` Rémi Denis-Courmont
2023-06-21 18:16 ` Palmer Dabbelt
2 siblings, 1 reply; 27+ messages in thread
From: Rémi Denis-Courmont @ 2023-06-21 16:47 UTC (permalink / raw)
To: Palmer Dabbelt, linux-riscv, Andy Chiu; +Cc: Björn Töpel
Le keskiviikkona 21. kesäkuuta 2023, 17.26.14 EEST Björn Töpel a écrit :
> Palmer Dabbelt <palmer@rivosinc.com> writes:
> > On Mon, 19 Jun 2023 12:01:20 PDT (-0700), bjorn@kernel.org wrote:
> >> Palmer Dabbelt <palmer@rivosinc.com> writes:
> >>
> >> [...]
> >>
> >>>>> + riscv_v_vstate_off(regs);
> >>>>> +
> >>>>
> >>>> Not off, right? Isn't it __riscv_v_vstate_clean() that you'd like to
> >>>> call? Something like:
> >>>>
> >>>> static void vstate_discard(struct pt_regs *regs)
> >>>> {
> >>>>
> >>>> if ((regs->status & SR_VS) == SR_VS_DIRTY)
> >>>>
> >>>> __riscv_v_vstate_clean(regs);
> >>>>
> >>>> }
> >>>>
> >>>> Complemented by a !V config variant.
> >>>
> >>> I think it's just a question of what we're trying to do here: clean
> >>> avoids the kernel V state save, but unless the kernel decides to use V
> >>> during the syscall the register contents will still be usable by
> >>> userspace. Maybe that's fine and we can just rely on the ISA spec,
> >>> though? I sent another patch to just document it in Linux, even if it's
> >>> in the ISA spec it seems worth having in the kernel as well.
> >>>
> >>> That said, I think the right thing to do here might be to zero the V
> >>> register state and set it to initial: that way we can prevent userspace
> >>> from accidentally relying on the state save, but we can also avoid the
> >>> trap that would come from turning it off. That lets us give the
> >>> hardware a nice clean indication when the V state isn't in use, which
> >>> will hopefully help us avoid the save/restore performance issues that
> >>> other ports have hit.
> >>
> >> FWIW, I think that's a much better idea than turning V off. I also like
> >> that it'll preventing userland to rely on pre-ecall state.
> >
> > OK, anyone else opposed?
> >
> > We're kind of in the weeds on performance, I think we'd need HW to know
> > for sure if either is an issue. Seems best to just play it safe WRT the
> > uABI for now, we can always deal with any performance issues if the
> > exist.
>
> Here's the patch you mentioned at the PW synchup; I've kept the Subject
> and such if you wan't to apply it. LMK if you'd like a proper one.
>
> --
>
> Subject: [PATCH] riscv: Discard vector state on syscalls
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> The RISC-V vector specification states:
> Executing a system call causes all caller-saved vector registers
> (v0-v31, vl, vtype) and vstart to become unspecified.
>
> The vector status is set to Initial, and the vector state is
> explicitly zeroed. That way we can prevent userspace from accidentally
> relying on the stated save.
>
> Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
> ---
> arch/riscv/include/asm/vector.h | 24 ++++++++++++++++++++++++
> arch/riscv/kernel/traps.c | 2 ++
> 2 files changed, 26 insertions(+)
>
> diff --git a/arch/riscv/include/asm/vector.h
> b/arch/riscv/include/asm/vector.h index 04c0b07bf6cd..b3020d064f42 100644
> --- a/arch/riscv/include/asm/vector.h
> +++ b/arch/riscv/include/asm/vector.h
> @@ -163,6 +163,29 @@ 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;
> +
> + if (!riscv_v_vstate_query(regs))
> + return;
> +
> + riscv_v_vstate_on(regs);
> +
> + 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();
Shouldn't this also set `vill` to 1 using `vsetvl`?
In fact, a faster alternative may yet be to *only* set an invalid vector
configuration. It's rather unlikely that user-space code would set a valid
configuration and use vectors without loading them first. If it ever does, then
it's so broken that the kernel probably doesn't need to care.
--
雷米‧德尼-库尔蒙
http://www.remlab.net/
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-21 16:47 ` Rémi Denis-Courmont
@ 2023-06-21 18:16 ` Palmer Dabbelt
2023-06-21 21:42 ` Björn Töpel
0 siblings, 1 reply; 27+ messages in thread
From: Palmer Dabbelt @ 2023-06-21 18:16 UTC (permalink / raw)
To: remi, Darius Rad; +Cc: linux-riscv, andy.chiu, bjorn
On Wed, 21 Jun 2023 09:47:37 PDT (-0700), remi@remlab.net wrote:
> Le keskiviikkona 21. kesäkuuta 2023, 17.26.14 EEST Björn Töpel a écrit :
>> Palmer Dabbelt <palmer@rivosinc.com> writes:
>> > On Mon, 19 Jun 2023 12:01:20 PDT (-0700), bjorn@kernel.org wrote:
>> >> Palmer Dabbelt <palmer@rivosinc.com> writes:
>> >>
>> >> [...]
>> >>
>> >>>>> + riscv_v_vstate_off(regs);
>> >>>>> +
>> >>>>
>> >>>> Not off, right? Isn't it __riscv_v_vstate_clean() that you'd like to
>> >>>> call? Something like:
>> >>>>
>> >>>> static void vstate_discard(struct pt_regs *regs)
>> >>>> {
>> >>>>
>> >>>> if ((regs->status & SR_VS) == SR_VS_DIRTY)
>> >>>>
>> >>>> __riscv_v_vstate_clean(regs);
>> >>>>
>> >>>> }
>> >>>>
>> >>>> Complemented by a !V config variant.
>> >>>
>> >>> I think it's just a question of what we're trying to do here: clean
>> >>> avoids the kernel V state save, but unless the kernel decides to use V
>> >>> during the syscall the register contents will still be usable by
>> >>> userspace. Maybe that's fine and we can just rely on the ISA spec,
>> >>> though? I sent another patch to just document it in Linux, even if it's
>> >>> in the ISA spec it seems worth having in the kernel as well.
>> >>>
>> >>> That said, I think the right thing to do here might be to zero the V
>> >>> register state and set it to initial: that way we can prevent userspace
>> >>> from accidentally relying on the state save, but we can also avoid the
>> >>> trap that would come from turning it off. That lets us give the
>> >>> hardware a nice clean indication when the V state isn't in use, which
>> >>> will hopefully help us avoid the save/restore performance issues that
>> >>> other ports have hit.
>> >>
>> >> FWIW, I think that's a much better idea than turning V off. I also like
>> >> that it'll preventing userland to rely on pre-ecall state.
>> >
>> > OK, anyone else opposed?
>> >
>> > We're kind of in the weeds on performance, I think we'd need HW to know
>> > for sure if either is an issue. Seems best to just play it safe WRT the
>> > uABI for now, we can always deal with any performance issues if the
>> > exist.
>>
>> Here's the patch you mentioned at the PW synchup; I've kept the Subject
>> and such if you wan't to apply it. LMK if you'd like a proper one.
>>
>> --
>>
>> Subject: [PATCH] riscv: Discard vector state on syscalls
>> MIME-Version: 1.0
>> Content-Type: text/plain; charset=UTF-8
>> Content-Transfer-Encoding: 8bit
>>
>> The RISC-V vector specification states:
>> Executing a system call causes all caller-saved vector registers
>> (v0-v31, vl, vtype) and vstart to become unspecified.
>>
>> The vector status is set to Initial, and the vector state is
>> explicitly zeroed. That way we can prevent userspace from accidentally
>> relying on the stated save.
>>
>> Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
>> ---
>> arch/riscv/include/asm/vector.h | 24 ++++++++++++++++++++++++
>> arch/riscv/kernel/traps.c | 2 ++
>> 2 files changed, 26 insertions(+)
>>
>> diff --git a/arch/riscv/include/asm/vector.h
>> b/arch/riscv/include/asm/vector.h index 04c0b07bf6cd..b3020d064f42 100644
>> --- a/arch/riscv/include/asm/vector.h
>> +++ b/arch/riscv/include/asm/vector.h
>> @@ -163,6 +163,29 @@ 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;
>> +
>> + if (!riscv_v_vstate_query(regs))
>> + return;
>> +
>> + riscv_v_vstate_on(regs);
>> +
>> + 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();
>
> Shouldn't this also set `vill` to 1 using `vsetvl`?
That seems reasonable to me.
> In fact, a faster alternative may yet be to *only* set an invalid vector
> configuration. It's rather unlikely that user-space code would set a valid
> configuration and use vectors without loading them first. If it ever does, then
> it's so broken that the kernel probably doesn't need to care.
I think that's sufficient to force userspace to trap on a bad value?
Most of the unsupported value writes in RISC-V are just WARL, but as far
as I can tell the V spec requires vill handling. Specifically
Implementations must consider all bits of the vtype value to
determine if the configuration is supported. An unsupported value in
any location within the vtype value must result in vill being set.
which seems pretty concrete about this being required. That's from the
current draft of the V spec, the wording in 1.0 isn't quite as clear: it
sort of allows for the WARL-type behavior, but that's probably splitting
hairs.
That said, it provides a slightly different cost curve: we'd need to
save/restore the V registers on non-syscall traps even when vill is set
in userspace, as they've still got state in them (userspace could be in
the middle of some probing routine, for example).
Also from Darius' fork of the thread: IIUC there's nothing saying 0 is
initial, or that initial even needs to work. So I think we're just
splitting hairs here, as long as we clobber enough state that userspace
doesn't accidentally depend on is fine with me.
> --
> 雷米‧德尼-库尔蒙
> http://www.remlab.net/
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [PATCH] RISC-V: Clobber V registers on syscalls
2023-06-21 18:16 ` Palmer Dabbelt
@ 2023-06-21 21:42 ` Björn Töpel
0 siblings, 0 replies; 27+ messages in thread
From: Björn Töpel @ 2023-06-21 21:42 UTC (permalink / raw)
To: Palmer Dabbelt, remi, Darius Rad; +Cc: linux-riscv, andy.chiu
Palmer Dabbelt <palmer@rivosinc.com> writes:
> On Wed, 21 Jun 2023 09:47:37 PDT (-0700), remi@remlab.net wrote:
>> Le keskiviikkona 21. kesäkuuta 2023, 17.26.14 EEST Björn Töpel a écrit :
>>> Palmer Dabbelt <palmer@rivosinc.com> writes:
>>> > On Mon, 19 Jun 2023 12:01:20 PDT (-0700), bjorn@kernel.org wrote:
>>> >> Palmer Dabbelt <palmer@rivosinc.com> writes:
>>> >>
>>> >> [...]
>>> >>
>>> >>>>> + riscv_v_vstate_off(regs);
>>> >>>>> +
>>> >>>>
>>> >>>> Not off, right? Isn't it __riscv_v_vstate_clean() that you'd like to
>>> >>>> call? Something like:
>>> >>>>
>>> >>>> static void vstate_discard(struct pt_regs *regs)
>>> >>>> {
>>> >>>>
>>> >>>> if ((regs->status & SR_VS) == SR_VS_DIRTY)
>>> >>>>
>>> >>>> __riscv_v_vstate_clean(regs);
>>> >>>>
>>> >>>> }
>>> >>>>
>>> >>>> Complemented by a !V config variant.
>>> >>>
>>> >>> I think it's just a question of what we're trying to do here: clean
>>> >>> avoids the kernel V state save, but unless the kernel decides to use V
>>> >>> during the syscall the register contents will still be usable by
>>> >>> userspace. Maybe that's fine and we can just rely on the ISA spec,
>>> >>> though? I sent another patch to just document it in Linux, even if it's
>>> >>> in the ISA spec it seems worth having in the kernel as well.
>>> >>>
>>> >>> That said, I think the right thing to do here might be to zero the V
>>> >>> register state and set it to initial: that way we can prevent userspace
>>> >>> from accidentally relying on the state save, but we can also avoid the
>>> >>> trap that would come from turning it off. That lets us give the
>>> >>> hardware a nice clean indication when the V state isn't in use, which
>>> >>> will hopefully help us avoid the save/restore performance issues that
>>> >>> other ports have hit.
>>> >>
>>> >> FWIW, I think that's a much better idea than turning V off. I also like
>>> >> that it'll preventing userland to rely on pre-ecall state.
>>> >
>>> > OK, anyone else opposed?
>>> >
>>> > We're kind of in the weeds on performance, I think we'd need HW to know
>>> > for sure if either is an issue. Seems best to just play it safe WRT the
>>> > uABI for now, we can always deal with any performance issues if the
>>> > exist.
>>>
>>> Here's the patch you mentioned at the PW synchup; I've kept the Subject
>>> and such if you wan't to apply it. LMK if you'd like a proper one.
>>>
>>> --
>>>
>>> Subject: [PATCH] riscv: Discard vector state on syscalls
>>> MIME-Version: 1.0
>>> Content-Type: text/plain; charset=UTF-8
>>> Content-Transfer-Encoding: 8bit
>>>
>>> The RISC-V vector specification states:
>>> Executing a system call causes all caller-saved vector registers
>>> (v0-v31, vl, vtype) and vstart to become unspecified.
>>>
>>> The vector status is set to Initial, and the vector state is
>>> explicitly zeroed. That way we can prevent userspace from accidentally
>>> relying on the stated save.
>>>
>>> Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
>>> ---
>>> arch/riscv/include/asm/vector.h | 24 ++++++++++++++++++++++++
>>> arch/riscv/kernel/traps.c | 2 ++
>>> 2 files changed, 26 insertions(+)
>>>
>>> diff --git a/arch/riscv/include/asm/vector.h
>>> b/arch/riscv/include/asm/vector.h index 04c0b07bf6cd..b3020d064f42 100644
>>> --- a/arch/riscv/include/asm/vector.h
>>> +++ b/arch/riscv/include/asm/vector.h
>>> @@ -163,6 +163,29 @@ 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;
>>> +
>>> + if (!riscv_v_vstate_query(regs))
>>> + return;
>>> +
>>> + riscv_v_vstate_on(regs);
>>> +
>>> + 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();
>>
>> Shouldn't this also set `vill` to 1 using `vsetvl`?
>
> That seems reasonable to me.
Something like this?
---
diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index b3020d064f42..d5f7853936d5 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -165,7 +165,7 @@ bool riscv_v_vstate_ctrl_user_allowed(void);
static inline void riscv_v_vstate_discard(struct pt_regs *regs)
{
- unsigned long vl;
+ unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
if (!riscv_v_vstate_query(regs))
return;
@@ -181,8 +181,9 @@ static inline void riscv_v_vstate_discard(struct pt_regs *regs)
"vmv.v.i v8, 0\n\t"
"vmv.v.i v16, 0\n\t"
"vmv.v.i v24, 0\n\t"
+ "vsetvl %0, x0, %1\n\t"
".option pop\n\t"
- : "=&r" (vl) : : "memory");
+ : "=&r" (vl) : "r" (vtype_inval) : "memory");
riscv_v_disable();
}
---
Björn
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 27+ messages in thread