[PATCH -next v13 09/19] riscv: Add task switch support for vector
Andy Chiu
andy.chiu at sifive.com
Wed Jan 25 06:20:46 PST 2023
From: Greentime Hu <greentime.hu at sifive.com>
This patch adds task switch support for vector. It also supports all
lengths of vlen.
[guoren at linux.alibaba.com: First available porting to support vector
context switching]
[nick.knight at sifive.com: Rewrite vector.S to support dynamic vlen, xlen and
code refine]
[vincent.chen at sifive.com: Fix the might_sleep issue in vstate_save,
vstate_restore]
[andrew at sifive.com: Optimize task switch codes of vector]
[ruinland.tsai at sifive.com: Fix the arch_release_task_struct free wrong
datap issue]
[vineetg: Fixed lkp warning with W=1 build]
[andy.chiu: Use inline asm for task switches]
Suggested-by: Andrew Waterman <andrew at sifive.com>
Co-developed-by: Nick Knight <nick.knight at sifive.com>
Signed-off-by: Nick Knight <nick.knight at sifive.com>
Co-developed-by: Guo Ren <guoren at linux.alibaba.com>
Signed-off-by: Guo Ren <guoren at linux.alibaba.com>
Co-developed-by: Vincent Chen <vincent.chen at sifive.com>
Signed-off-by: Vincent Chen <vincent.chen at sifive.com>
Co-developed-by: Ruinland Tsai <ruinland.tsai at sifive.com>
Signed-off-by: Ruinland Tsai <ruinland.tsai at sifive.com>
Signed-off-by: Greentime Hu <greentime.hu at sifive.com>
Signed-off-by: Vineet Gupta <vineetg at rivosinc.com>
Signed-off-by: Andy Chiu <andy.chiu at sifive.com>
---
arch/riscv/include/asm/processor.h | 1 +
arch/riscv/include/asm/switch_to.h | 18 ++++++++++++++++++
arch/riscv/include/asm/thread_info.h | 3 +++
arch/riscv/include/asm/vector.h | 26 ++++++++++++++++++++++++++
arch/riscv/kernel/process.c | 18 ++++++++++++++++++
arch/riscv/kernel/traps.c | 14 ++++++++++++--
6 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h
index 94a0590c6971..44d2eb381ca6 100644
--- a/arch/riscv/include/asm/processor.h
+++ b/arch/riscv/include/asm/processor.h
@@ -39,6 +39,7 @@ struct thread_struct {
unsigned long s[12]; /* s[0]: frame pointer */
struct __riscv_d_ext_state fstate;
unsigned long bad_cause;
+ struct __riscv_v_state vstate;
};
/* Whitelist the fstate from the task_struct for hardened usercopy */
diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h
index df1aa589b7fd..69e24140195d 100644
--- a/arch/riscv/include/asm/switch_to.h
+++ b/arch/riscv/include/asm/switch_to.h
@@ -8,6 +8,7 @@
#include <linux/jump_label.h>
#include <linux/sched/task_stack.h>
+#include <asm/vector.h>
#include <asm/hwcap.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
@@ -68,6 +69,21 @@ static __always_inline bool has_fpu(void) { return false; }
#define __switch_to_fpu(__prev, __next) do { } while (0)
#endif
+#ifdef CONFIG_RISCV_ISA_V
+static inline void __switch_to_vector(struct task_struct *prev,
+ struct task_struct *next)
+{
+ struct pt_regs *regs;
+
+ regs = task_pt_regs(prev);
+ if (unlikely(regs->status & SR_SD))
+ vstate_save(prev, regs);
+ vstate_restore(next, task_pt_regs(next));
+}
+#else /* ! CONFIG_RISCV_ISA_V */
+#define __switch_to_vector(__prev, __next) do { } while (0)
+#endif /* CONFIG_RISCV_ISA_V */
+
extern struct task_struct *__switch_to(struct task_struct *,
struct task_struct *);
@@ -77,6 +93,8 @@ do { \
struct task_struct *__next = (next); \
if (has_fpu()) \
__switch_to_fpu(__prev, __next); \
+ if (has_vector()) \
+ __switch_to_vector(__prev, __next); \
((last) = __switch_to(__prev, __next)); \
} while (0)
diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h
index 67322f878e0d..2f0f0d7d0fc0 100644
--- a/arch/riscv/include/asm/thread_info.h
+++ b/arch/riscv/include/asm/thread_info.h
@@ -79,6 +79,9 @@ struct thread_info {
.preempt_count = INIT_PREEMPT_COUNT, \
}
+void arch_release_task_struct(struct task_struct *tsk);
+int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);
+
#endif /* !__ASSEMBLY__ */
/*
diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
index 842a859609b5..f8a9e37c4374 100644
--- a/arch/riscv/include/asm/vector.h
+++ b/arch/riscv/include/asm/vector.h
@@ -10,6 +10,8 @@
#ifdef CONFIG_RISCV_ISA_V
+#include <linux/sched.h>
+#include <asm/ptrace.h>
#include <asm/hwcap.h>
#include <asm/csr.h>
#include <asm/asm.h>
@@ -109,6 +111,28 @@ static inline void __vstate_restore(struct __riscv_v_state *restore_from,
rvv_disable();
}
+static inline void vstate_save(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ if ((regs->status & SR_VS) == SR_VS_DIRTY) {
+ struct __riscv_v_state *vstate = &task->thread.vstate;
+
+ __vstate_save(vstate, vstate->datap);
+ __vstate_clean(regs);
+ }
+}
+
+static inline void vstate_restore(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ if ((regs->status & SR_VS) != SR_VS_OFF) {
+ struct __riscv_v_state *vstate = &task->thread.vstate;
+
+ __vstate_restore(vstate, vstate->datap);
+ __vstate_clean(regs);
+ }
+}
+
#else /* ! CONFIG_RISCV_ISA_V */
struct pt_regs;
@@ -116,6 +140,8 @@ struct pt_regs;
static __always_inline bool has_vector(void) { return false; }
static inline bool vstate_query(struct pt_regs *regs) { return false; }
#define riscv_vsize (0)
+#define vstate_save(task, regs) do {} while (0)
+#define vstate_restore(task, regs) do {} while (0)
#define vstate_off(regs) do {} while (0)
#define vstate_on(regs) do {} while (0)
diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
index 8955f2432c2d..d4860c6c5197 100644
--- a/arch/riscv/kernel/process.c
+++ b/arch/riscv/kernel/process.c
@@ -24,6 +24,7 @@
#include <asm/switch_to.h>
#include <asm/thread_info.h>
#include <asm/cpuidle.h>
+#include <asm/vector.h>
register unsigned long gp_in_global __asm__("gp");
@@ -148,12 +149,28 @@ void flush_thread(void)
fstate_off(current, task_pt_regs(current));
memset(¤t->thread.fstate, 0, sizeof(current->thread.fstate));
#endif
+#ifdef CONFIG_RISCV_ISA_V
+ /* Reset vector state */
+ vstate_off(task_pt_regs(current));
+ kfree(current->thread.vstate.datap);
+ memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_state));
+#endif
+}
+
+void arch_release_task_struct(struct task_struct *tsk)
+{
+ /* Free the vector context of datap. */
+ if (has_vector() && tsk->thread.vstate.datap)
+ kfree(tsk->thread.vstate.datap);
}
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
fstate_save(src, task_pt_regs(src));
*dst = *src;
+ /* clear entire V context, including datap for a new task */
+ memset(&dst->thread.vstate, 0, sizeof(struct __riscv_v_state));
+
return 0;
}
@@ -186,6 +203,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
childregs->a0 = 0; /* Return value of fork() */
p->thread.ra = (unsigned long)ret_from_fork;
}
+ vstate_off(childregs);
p->thread.sp = (unsigned long)childregs; /* kernel sp */
return 0;
}
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 549bde5c970a..1a48ff89b2b5 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -24,6 +24,7 @@
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
+#include <asm/vector.h>
int show_unhandled_signals = 1;
@@ -111,8 +112,17 @@ DO_ERROR_INFO(do_trap_insn_misaligned,
SIGBUS, BUS_ADRALN, "instruction address misaligned");
DO_ERROR_INFO(do_trap_insn_fault,
SIGSEGV, SEGV_ACCERR, "instruction access fault");
-DO_ERROR_INFO(do_trap_insn_illegal,
- SIGILL, ILL_ILLOPC, "illegal instruction");
+
+asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *regs)
+{
+ if (has_vector() && user_mode(regs)) {
+ if (rvv_first_use_handler(regs))
+ return;
+ }
+ do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc,
+ "Oops - illegal instruction");
+}
+
DO_ERROR_INFO(do_trap_load_fault,
SIGSEGV, SEGV_ACCERR, "load access fault");
#ifndef CONFIG_RISCV_M_MODE
--
2.17.1
More information about the linux-riscv
mailing list