diff mbox series

[v8,04/10] riscv: sched: defer restoring Vector context for user

Message ID 20231223042914.18599-5-andy.chiu@sifive.com (mailing list archive)
State Superseded
Headers show
Series riscv: support kernel-mode Vector | expand

Checks

Context Check Description
conchuod/vmtest-for-next-PR fail PR summary
conchuod/patch-4-test-1 success .github/scripts/patches/build_rv32_defconfig.sh
conchuod/patch-4-test-2 success .github/scripts/patches/build_rv64_clang_allmodconfig.sh
conchuod/patch-4-test-3 success .github/scripts/patches/build_rv64_gcc_allmodconfig.sh
conchuod/patch-4-test-4 success .github/scripts/patches/build_rv64_nommu_k210_defconfig.sh
conchuod/patch-4-test-5 success .github/scripts/patches/build_rv64_nommu_virt_defconfig.sh
conchuod/patch-4-test-6 warning .github/scripts/patches/checkpatch.sh
conchuod/patch-4-test-7 success .github/scripts/patches/dtb_warn_rv64.sh
conchuod/patch-4-test-8 success .github/scripts/patches/header_inline.sh
conchuod/patch-4-test-9 success .github/scripts/patches/kdoc.sh
conchuod/patch-4-test-10 success .github/scripts/patches/module_param.sh
conchuod/patch-4-test-11 success .github/scripts/patches/verify_fixes.sh
conchuod/patch-4-test-12 success .github/scripts/patches/verify_signedoff.sh

Commit Message

Andy Chiu Dec. 23, 2023, 4:29 a.m. UTC
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(-)

Comments

Song Shuai Dec. 27, 2023, 12:07 p.m. UTC | #1
在 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(&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;
>   }
> 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, &current->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 mbox series

Patch

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(&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;
 }
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, &current->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;
 }