Unverified Commit 7df56cbc authored by Andy Chiu's avatar Andy Chiu Committed by Palmer Dabbelt
Browse files

riscv: sched: defer restoring Vector context for user



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: default avatarAndy Chiu <andy.chiu@sifive.com>
Acked-by: default avatarConor Dooley <conor.dooley@microchip.com>
Tested-by: default avatarBjörn Töpel <bjorn@rivosinc.com>
Tested-by: default avatarLad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Link: https://lore.kernel.org/r/20240115055929.4736-5-andy.chiu@sifive.com


Signed-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
parent c5674d00
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -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 riscv_v_flags.
		 */
		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);
+2 −0
Original line number Diff line number Diff line
@@ -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 | \
+10 −1
Original line number Diff line number Diff line
@@ -193,6 +193,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)
{
@@ -200,7 +209,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);
+1 −1
Original line number Diff line number Diff line
@@ -117,7 +117,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();

+2 −0
Original line number Diff line number Diff line
@@ -171,6 +171,7 @@ void flush_thread(void)
	riscv_v_vstate_off(task_pt_regs(current));
	kfree(current->thread.vstate.datap);
	memset(&current->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;
}
Loading