Message ID | 20231223042914.18599-5-andy.chiu@sifive.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | riscv: support kernel-mode Vector | expand |
在 2023/12/23 12:29, Andy Chiu 写道: > User will use its Vector registers only after the kernel really returns > to the userspace. So we can delay restoring Vector registers as long as > we are still running in kernel mode. So, add a thread flag to indicates > the need of restoring Vector and do the restore at the last > arch-specific exit-to-user hook. This save the context restoring cost > when we switch over multiple processes that run V in kernel mode. For > example, if the kernel performs a context swicth from A->B->C, and > returns to C's userspace, then there is no need to restore B's > V-register. > > Besides, this also prevents us from repeatedly restoring V context when > executing kernel-mode Vector multiple times. > > The cost of this is that we must disable preemption and mark vector as > busy during vstate_{save,restore}. Because then the V context will not > get restored back immediately when a trap-causing context switch happens > in the middle of vstate_{save,restore}. > > Signed-off-by: Andy Chiu <andy.chiu@sifive.com> > Acked-by: Conor Dooley <conor.dooley@microchip.com> > --- > Changelog v4: > - fix typos and re-add Conor's A-b. > Changelog v3: > - Guard {get,put}_cpu_vector_context between vstate_* operation and > explain it in the commit msg. > - Drop R-b from Björn and A-b from Conor. > Changelog v2: > - rename and add comment for the new thread flag (Conor) > --- > arch/riscv/include/asm/entry-common.h | 17 +++++++++++++++++ > arch/riscv/include/asm/thread_info.h | 2 ++ > arch/riscv/include/asm/vector.h | 11 ++++++++++- > arch/riscv/kernel/kernel_mode_vector.c | 2 +- > arch/riscv/kernel/process.c | 2 ++ > arch/riscv/kernel/ptrace.c | 5 ++++- > arch/riscv/kernel/signal.c | 5 ++++- > arch/riscv/kernel/vector.c | 2 +- > 8 files changed, 41 insertions(+), 5 deletions(-) > > diff --git a/arch/riscv/include/asm/entry-common.h b/arch/riscv/include/asm/entry-common.h > index 7ab5e34318c8..6361a8488642 100644 > --- a/arch/riscv/include/asm/entry-common.h > +++ b/arch/riscv/include/asm/entry-common.h > @@ -4,6 +4,23 @@ > #define _ASM_RISCV_ENTRY_COMMON_H > > #include <asm/stacktrace.h> > +#include <asm/thread_info.h> > +#include <asm/vector.h> > + > +static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs, > + unsigned long ti_work) > +{ > + if (ti_work & _TIF_RISCV_V_DEFER_RESTORE) { > + clear_thread_flag(TIF_RISCV_V_DEFER_RESTORE); > + /* > + * We are already called with irq disabled, so go without > + * keeping track of vector_context_busy. "vector_context_busy" here should mean the flag used to track in-kernel Vector context -- riscv_v_flags in this version, please update it. > + */ > + riscv_v_vstate_restore(current, regs); > + } > +} > + > +#define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare > > void handle_page_fault(struct pt_regs *regs); > void handle_break(struct pt_regs *regs); > diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h > index 574779900bfb..1047a97ddbc8 100644 > --- a/arch/riscv/include/asm/thread_info.h > +++ b/arch/riscv/include/asm/thread_info.h > @@ -103,12 +103,14 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); > #define TIF_NOTIFY_SIGNAL 9 /* signal notifications exist */ > #define TIF_UPROBE 10 /* uprobe breakpoint or singlestep */ > #define TIF_32BIT 11 /* compat-mode 32bit process */ > +#define TIF_RISCV_V_DEFER_RESTORE 12 /* restore Vector before returing to user */ > > #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) > #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) > #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) > #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) > #define _TIF_UPROBE (1 << TIF_UPROBE) > +#define _TIF_RISCV_V_DEFER_RESTORE (1 << TIF_RISCV_V_DEFER_RESTORE) > > #define _TIF_WORK_MASK \ > (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | \ > diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h > index 6254830c0668..e706613aae2c 100644 > --- a/arch/riscv/include/asm/vector.h > +++ b/arch/riscv/include/asm/vector.h > @@ -205,6 +205,15 @@ static inline void riscv_v_vstate_restore(struct task_struct *task, > } > } > > +static inline void riscv_v_vstate_set_restore(struct task_struct *task, > + struct pt_regs *regs) > +{ > + if ((regs->status & SR_VS) != SR_VS_OFF) { > + set_tsk_thread_flag(task, TIF_RISCV_V_DEFER_RESTORE); > + riscv_v_vstate_on(regs); > + } > +} > + > static inline void __switch_to_vector(struct task_struct *prev, > struct task_struct *next) > { > @@ -212,7 +221,7 @@ static inline void __switch_to_vector(struct task_struct *prev, > > regs = task_pt_regs(prev); > riscv_v_vstate_save(prev, regs); > - riscv_v_vstate_restore(next, task_pt_regs(next)); > + riscv_v_vstate_set_restore(next, task_pt_regs(next)); > } > > void riscv_v_vstate_ctrl_init(struct task_struct *tsk); > diff --git a/arch/riscv/kernel/kernel_mode_vector.c b/arch/riscv/kernel/kernel_mode_vector.c > index 385d9b4d8cc6..63814e780c28 100644 > --- a/arch/riscv/kernel/kernel_mode_vector.c > +++ b/arch/riscv/kernel/kernel_mode_vector.c > @@ -96,7 +96,7 @@ void kernel_vector_end(void) > if (WARN_ON(!has_vector())) > return; > > - riscv_v_vstate_restore(current, task_pt_regs(current)); > + riscv_v_vstate_set_restore(current, task_pt_regs(current)); > > riscv_v_disable(); > > diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c > index 4a1275db1146..36993f408de4 100644 > --- a/arch/riscv/kernel/process.c > +++ b/arch/riscv/kernel/process.c > @@ -171,6 +171,7 @@ void flush_thread(void) > riscv_v_vstate_off(task_pt_regs(current)); > kfree(current->thread.vstate.datap); > memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); > + clear_tsk_thread_flag(current, TIF_RISCV_V_DEFER_RESTORE); > #endif > } > > @@ -187,6 +188,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) > *dst = *src; > /* clear entire V context, including datap for a new task */ > memset(&dst->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); > + clear_tsk_thread_flag(dst, TIF_RISCV_V_DEFER_RESTORE); > > return 0; > } > diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c > index 2afe460de16a..7b93bcbdf9fa 100644 > --- a/arch/riscv/kernel/ptrace.c > +++ b/arch/riscv/kernel/ptrace.c > @@ -99,8 +99,11 @@ 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) > + if (target == current) { > + get_cpu_vector_context(); > riscv_v_vstate_save(current, task_pt_regs(current)); > + put_cpu_vector_context(); > + } > > ptrace_vstate.vstart = vstate->vstart; > ptrace_vstate.vl = vstate->vl; > diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c > index 88b6220b2608..aca4a12c8416 100644 > --- a/arch/riscv/kernel/signal.c > +++ b/arch/riscv/kernel/signal.c > @@ -86,7 +86,10 @@ 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(unlikely(!IS_ALIGNED((unsigned long)datap, 16))); > > + get_cpu_vector_context(); > riscv_v_vstate_save(current, regs); > + put_cpu_vector_context(); > + > /* Copy everything of vstate but datap. */ > err = __copy_to_user(&state->v_state, ¤t->thread.vstate, > offsetof(struct __riscv_v_ext_state, datap)); > @@ -134,7 +137,7 @@ static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec) > if (unlikely(err)) > return err; > > - riscv_v_vstate_restore(current, regs); > + riscv_v_vstate_set_restore(current, regs); > > return err; > } > diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c > index 578b6292487e..66e8c6ab09d2 100644 > --- a/arch/riscv/kernel/vector.c > +++ b/arch/riscv/kernel/vector.c > @@ -167,7 +167,7 @@ bool riscv_v_first_use_handler(struct pt_regs *regs) > return true; > } > riscv_v_vstate_on(regs); > - riscv_v_vstate_restore(current, regs); > + riscv_v_vstate_set_restore(current, regs); > return true; > } >
diff --git a/arch/riscv/include/asm/entry-common.h b/arch/riscv/include/asm/entry-common.h index 7ab5e34318c8..6361a8488642 100644 --- a/arch/riscv/include/asm/entry-common.h +++ b/arch/riscv/include/asm/entry-common.h @@ -4,6 +4,23 @@ #define _ASM_RISCV_ENTRY_COMMON_H #include <asm/stacktrace.h> +#include <asm/thread_info.h> +#include <asm/vector.h> + +static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs, + unsigned long ti_work) +{ + if (ti_work & _TIF_RISCV_V_DEFER_RESTORE) { + clear_thread_flag(TIF_RISCV_V_DEFER_RESTORE); + /* + * We are already called with irq disabled, so go without + * keeping track of vector_context_busy. + */ + riscv_v_vstate_restore(current, regs); + } +} + +#define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare void handle_page_fault(struct pt_regs *regs); void handle_break(struct pt_regs *regs); diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h index 574779900bfb..1047a97ddbc8 100644 --- a/arch/riscv/include/asm/thread_info.h +++ b/arch/riscv/include/asm/thread_info.h @@ -103,12 +103,14 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); #define TIF_NOTIFY_SIGNAL 9 /* signal notifications exist */ #define TIF_UPROBE 10 /* uprobe breakpoint or singlestep */ #define TIF_32BIT 11 /* compat-mode 32bit process */ +#define TIF_RISCV_V_DEFER_RESTORE 12 /* restore Vector before returing to user */ #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) #define _TIF_UPROBE (1 << TIF_UPROBE) +#define _TIF_RISCV_V_DEFER_RESTORE (1 << TIF_RISCV_V_DEFER_RESTORE) #define _TIF_WORK_MASK \ (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | \ diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index 6254830c0668..e706613aae2c 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -205,6 +205,15 @@ static inline void riscv_v_vstate_restore(struct task_struct *task, } } +static inline void riscv_v_vstate_set_restore(struct task_struct *task, + struct pt_regs *regs) +{ + if ((regs->status & SR_VS) != SR_VS_OFF) { + set_tsk_thread_flag(task, TIF_RISCV_V_DEFER_RESTORE); + riscv_v_vstate_on(regs); + } +} + static inline void __switch_to_vector(struct task_struct *prev, struct task_struct *next) { @@ -212,7 +221,7 @@ static inline void __switch_to_vector(struct task_struct *prev, regs = task_pt_regs(prev); riscv_v_vstate_save(prev, regs); - riscv_v_vstate_restore(next, task_pt_regs(next)); + riscv_v_vstate_set_restore(next, task_pt_regs(next)); } void riscv_v_vstate_ctrl_init(struct task_struct *tsk); diff --git a/arch/riscv/kernel/kernel_mode_vector.c b/arch/riscv/kernel/kernel_mode_vector.c index 385d9b4d8cc6..63814e780c28 100644 --- a/arch/riscv/kernel/kernel_mode_vector.c +++ b/arch/riscv/kernel/kernel_mode_vector.c @@ -96,7 +96,7 @@ void kernel_vector_end(void) if (WARN_ON(!has_vector())) return; - riscv_v_vstate_restore(current, task_pt_regs(current)); + riscv_v_vstate_set_restore(current, task_pt_regs(current)); riscv_v_disable(); diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 4a1275db1146..36993f408de4 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -171,6 +171,7 @@ void flush_thread(void) riscv_v_vstate_off(task_pt_regs(current)); kfree(current->thread.vstate.datap); memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); + clear_tsk_thread_flag(current, TIF_RISCV_V_DEFER_RESTORE); #endif } @@ -187,6 +188,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) *dst = *src; /* clear entire V context, including datap for a new task */ memset(&dst->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); + clear_tsk_thread_flag(dst, TIF_RISCV_V_DEFER_RESTORE); return 0; } diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 2afe460de16a..7b93bcbdf9fa 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -99,8 +99,11 @@ 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) + if (target == current) { + get_cpu_vector_context(); riscv_v_vstate_save(current, task_pt_regs(current)); + put_cpu_vector_context(); + } ptrace_vstate.vstart = vstate->vstart; ptrace_vstate.vl = vstate->vl; diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 88b6220b2608..aca4a12c8416 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -86,7 +86,10 @@ 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(unlikely(!IS_ALIGNED((unsigned long)datap, 16))); + get_cpu_vector_context(); riscv_v_vstate_save(current, regs); + put_cpu_vector_context(); + /* Copy everything of vstate but datap. */ err = __copy_to_user(&state->v_state, ¤t->thread.vstate, offsetof(struct __riscv_v_ext_state, datap)); @@ -134,7 +137,7 @@ static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec) if (unlikely(err)) return err; - riscv_v_vstate_restore(current, regs); + riscv_v_vstate_set_restore(current, regs); return err; } diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c index 578b6292487e..66e8c6ab09d2 100644 --- a/arch/riscv/kernel/vector.c +++ b/arch/riscv/kernel/vector.c @@ -167,7 +167,7 @@ bool riscv_v_first_use_handler(struct pt_regs *regs) return true; } riscv_v_vstate_on(regs); - riscv_v_vstate_restore(current, regs); + riscv_v_vstate_set_restore(current, regs); return true; }