Skip to content

Commit 2080ff9

Browse files
AndybnACTpalmer-dabbelt
authored andcommitted
riscv: vector: allow kernel-mode Vector with preemption
Add kernel_vstate to keep track of kernel-mode Vector registers when trap introduced context switch happens. Also, provide riscv_v_flags to let context save/restore routine track context status. Context tracking happens whenever the core starts its in-kernel Vector executions. An active (dirty) kernel task's V contexts will be saved to memory whenever a trap-introduced context switch happens. Or, when a softirq, which happens to nest on top of it, uses Vector. Context retoring happens when the execution transfer back to the original Kernel context where it first enable preempt_v. Also, provide a config CONFIG_RISCV_ISA_V_PREEMPTIVE to give users an option to disable preemptible kernel-mode Vector at build time. Users with constraint memory may want to disable this config as preemptible kernel-mode Vector needs extra space for tracking of per thread's kernel-mode V context. Or, users might as well want to disable it if all kernel-mode Vector code is time sensitive and cannot tolerate context switch overhead. Signed-off-by: Andy Chiu <[email protected]> Tested-by: Björn Töpel <[email protected]> Tested-by: Lad Prabhakar <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent bd446f5 commit 2080ff9

File tree

9 files changed

+286
-22
lines changed

9 files changed

+286
-22
lines changed

arch/riscv/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,20 @@ config RISCV_ISA_V_UCOPY_THRESHOLD
533533
Prefer using vectorized copy_to_user()/copy_from_user() when the
534534
workload size exceeds this value.
535535

536+
config RISCV_ISA_V_PREEMPTIVE
537+
bool "Run kernel-mode Vector with kernel preemption"
538+
depends on PREEMPTION
539+
depends on RISCV_ISA_V
540+
default y
541+
help
542+
Usually, in-kernel SIMD routines are run with preemption disabled.
543+
Functions which envoke long running SIMD thus must yield core's
544+
vector unit to prevent blocking other tasks for too long.
545+
546+
This config allows kernel to run SIMD without explicitly disable
547+
preemption. Enabling this config will result in higher memory
548+
consumption due to the allocation of per-task's kernel Vector context.
549+
536550
config TOOLCHAIN_HAS_ZBB
537551
bool
538552
default y

arch/riscv/include/asm/asm-prototypes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ void xor_regs_5_(unsigned long bytes, unsigned long *__restrict p1,
3030
const unsigned long *__restrict p4,
3131
const unsigned long *__restrict p5);
3232

33+
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
34+
asmlinkage void riscv_v_context_nesting_start(struct pt_regs *regs);
35+
asmlinkage void riscv_v_context_nesting_end(struct pt_regs *regs);
36+
#endif /* CONFIG_RISCV_ISA_V_PREEMPTIVE */
37+
3338
#endif /* CONFIG_RISCV_ISA_V */
3439

3540
#define DECLARE_DO_ERROR_INFO(name) asmlinkage void name(struct pt_regs *regs)

arch/riscv/include/asm/processor.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,35 @@ struct pt_regs;
8080
* - bit 0: indicates whether the in-kernel Vector context is active. The
8181
* activation of this state disables the preemption. On a non-RT kernel, it
8282
* also disable bh.
83+
* - bits 8: is used for tracking preemptible kernel-mode Vector, when
84+
* RISCV_ISA_V_PREEMPTIVE is enabled. Calling kernel_vector_begin() does not
85+
* disable the preemption if the thread's kernel_vstate.datap is allocated.
86+
* Instead, the kernel set this bit field. Then the trap entry/exit code
87+
* knows if we are entering/exiting the context that owns preempt_v.
88+
* - 0: the task is not using preempt_v
89+
* - 1: the task is actively using preempt_v. But whether does the task own
90+
* the preempt_v context is decided by bits in RISCV_V_CTX_DEPTH_MASK.
91+
* - bit 16-23 are RISCV_V_CTX_DEPTH_MASK, used by context tracking routine
92+
* when preempt_v starts:
93+
* - 0: the task is actively using, and own preempt_v context.
94+
* - non-zero: the task was using preempt_v, but then took a trap within.
95+
* Thus, the task does not own preempt_v. Any use of Vector will have to
96+
* save preempt_v, if dirty, and fallback to non-preemptible kernel-mode
97+
* Vector.
98+
* - bit 30: The in-kernel preempt_v context is saved, and requries to be
99+
* restored when returning to the context that owns the preempt_v.
100+
* - bit 31: The in-kernel preempt_v context is dirty, as signaled by the
101+
* trap entry code. Any context switches out-of current task need to save
102+
* it to the task's in-kernel V context. Also, any traps nesting on-top-of
103+
* preempt_v requesting to use V needs a save.
83104
*/
84-
#define RISCV_KERNEL_MODE_V 0x1
105+
#define RISCV_V_CTX_DEPTH_MASK 0x00ff0000
106+
107+
#define RISCV_V_CTX_UNIT_DEPTH 0x00010000
108+
#define RISCV_KERNEL_MODE_V 0x00000001
109+
#define RISCV_PREEMPT_V 0x00000100
110+
#define RISCV_PREEMPT_V_DIRTY 0x80000000
111+
#define RISCV_PREEMPT_V_NEED_RESTORE 0x40000000
85112

86113
/* CPU-specific state of a task */
87114
struct thread_struct {
@@ -95,6 +122,7 @@ struct thread_struct {
95122
u32 vstate_ctrl;
96123
struct __riscv_v_ext_state vstate;
97124
unsigned long align_ctl;
125+
struct __riscv_v_ext_state kernel_vstate;
98126
};
99127

100128
/* Whitelist the fstate from the task_struct for hardened usercopy */

arch/riscv/include/asm/simd.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/percpu.h>
1313
#include <linux/preempt.h>
1414
#include <linux/types.h>
15+
#include <linux/thread_info.h>
1516

1617
#include <asm/vector.h>
1718

@@ -28,12 +29,27 @@ static __must_check inline bool may_use_simd(void)
2829
/*
2930
* RISCV_KERNEL_MODE_V is only set while preemption is disabled,
3031
* and is clear whenever preemption is enabled.
31-
*
32-
* Kernel-mode Vector temporarily disables bh. So we must not return
33-
* true on irq_disabled(). Otherwise we would fail the lockdep check
34-
* calling local_bh_enable()
3532
*/
36-
return !in_hardirq() && !in_nmi() && !irqs_disabled() && !(riscv_v_flags() & RISCV_KERNEL_MODE_V);
33+
if (in_hardirq() || in_nmi())
34+
return false;
35+
36+
/*
37+
* Nesting is acheived in preempt_v by spreading the control for
38+
* preemptible and non-preemptible kernel-mode Vector into two fields.
39+
* Always try to match with prempt_v if kernel V-context exists. Then,
40+
* fallback to check non preempt_v if nesting happens, or if the config
41+
* is not set.
42+
*/
43+
if (IS_ENABLED(CONFIG_RISCV_ISA_V_PREEMPTIVE) && current->thread.kernel_vstate.datap) {
44+
if (!riscv_preempt_v_started(current))
45+
return true;
46+
}
47+
/*
48+
* Non-preemptible kernel-mode Vector temporarily disables bh. So we
49+
* must not return true on irq_disabled(). Otherwise we would fail the
50+
* lockdep check calling local_bh_enable()
51+
*/
52+
return !irqs_disabled() && !(riscv_v_flags() & RISCV_KERNEL_MODE_V);
3753
}
3854

3955
#else /* ! CONFIG_RISCV_ISA_V */

arch/riscv/include/asm/vector.h

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ void get_cpu_vector_context(void);
2828
void put_cpu_vector_context(void);
2929
void riscv_v_thread_free(struct task_struct *tsk);
3030
void __init riscv_v_setup_ctx_cache(void);
31+
void riscv_v_thread_alloc(struct task_struct *tsk);
3132

3233
static inline u32 riscv_v_flags(void)
3334
{
34-
return current->thread.riscv_v_flags;
35+
return READ_ONCE(current->thread.riscv_v_flags);
3536
}
3637

3738
static __always_inline bool has_vector(void)
@@ -200,14 +201,62 @@ static inline void riscv_v_vstate_set_restore(struct task_struct *task,
200201
}
201202
}
202203

204+
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
205+
static inline bool riscv_preempt_v_dirty(struct task_struct *task)
206+
{
207+
return !!(task->thread.riscv_v_flags & RISCV_PREEMPT_V_DIRTY);
208+
}
209+
210+
static inline bool riscv_preempt_v_restore(struct task_struct *task)
211+
{
212+
return !!(task->thread.riscv_v_flags & RISCV_PREEMPT_V_NEED_RESTORE);
213+
}
214+
215+
static inline void riscv_preempt_v_clear_dirty(struct task_struct *task)
216+
{
217+
barrier();
218+
task->thread.riscv_v_flags &= ~RISCV_PREEMPT_V_DIRTY;
219+
}
220+
221+
static inline void riscv_preempt_v_set_restore(struct task_struct *task)
222+
{
223+
barrier();
224+
task->thread.riscv_v_flags |= RISCV_PREEMPT_V_NEED_RESTORE;
225+
}
226+
227+
static inline bool riscv_preempt_v_started(struct task_struct *task)
228+
{
229+
return !!(task->thread.riscv_v_flags & RISCV_PREEMPT_V);
230+
}
231+
232+
#else /* !CONFIG_RISCV_ISA_V_PREEMPTIVE */
233+
static inline bool riscv_preempt_v_dirty(struct task_struct *task) { return false; }
234+
static inline bool riscv_preempt_v_restore(struct task_struct *task) { return false; }
235+
static inline bool riscv_preempt_v_started(struct task_struct *task) { return false; }
236+
#define riscv_preempt_v_clear_dirty(tsk) do {} while (0)
237+
#define riscv_preempt_v_set_restore(tsk) do {} while (0)
238+
#endif /* CONFIG_RISCV_ISA_V_PREEMPTIVE */
239+
203240
static inline void __switch_to_vector(struct task_struct *prev,
204241
struct task_struct *next)
205242
{
206243
struct pt_regs *regs;
207244

208-
regs = task_pt_regs(prev);
209-
riscv_v_vstate_save(&prev->thread.vstate, regs);
210-
riscv_v_vstate_set_restore(next, task_pt_regs(next));
245+
if (riscv_preempt_v_started(prev)) {
246+
if (riscv_preempt_v_dirty(prev)) {
247+
__riscv_v_vstate_save(&prev->thread.kernel_vstate,
248+
prev->thread.kernel_vstate.datap);
249+
riscv_preempt_v_clear_dirty(prev);
250+
}
251+
} else {
252+
regs = task_pt_regs(prev);
253+
riscv_v_vstate_save(&prev->thread.vstate, regs);
254+
}
255+
256+
if (riscv_preempt_v_started(next))
257+
riscv_preempt_v_set_restore(next);
258+
else
259+
riscv_v_vstate_set_restore(next, task_pt_regs(next));
211260
}
212261

213262
void riscv_v_vstate_ctrl_init(struct task_struct *tsk);
@@ -231,6 +280,7 @@ static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
231280
#define riscv_v_vstate_on(regs) do {} while (0)
232281
#define riscv_v_thread_free(tsk) do {} while (0)
233282
#define riscv_v_setup_ctx_cache() do {} while (0)
283+
#define riscv_v_thread_alloc(tsk) do {} while (0)
234284

235285
#endif /* CONFIG_RISCV_ISA_V */
236286

arch/riscv/kernel/entry.S

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ SYM_CODE_START(handle_exception)
8383
/* Load the kernel shadow call stack pointer if coming from userspace */
8484
scs_load_current_if_task_changed s5
8585

86+
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
87+
move a0, sp
88+
call riscv_v_context_nesting_start
89+
#endif
8690
move a0, sp /* pt_regs */
8791
la ra, ret_from_exception
8892

@@ -138,6 +142,10 @@ SYM_CODE_START_NOALIGN(ret_from_exception)
138142
*/
139143
csrw CSR_SCRATCH, tp
140144
1:
145+
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
146+
move a0, sp
147+
call riscv_v_context_nesting_end
148+
#endif
141149
REG_L a0, PT_STATUS(sp)
142150
/*
143151
* The current load reservation is effectively part of the processor's

0 commit comments

Comments
 (0)