summaryrefslogtreecommitdiff
path: root/arch/riscv/kernel/kernel_mode_vector.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/riscv/kernel/kernel_mode_vector.c')
-rw-r--r--arch/riscv/kernel/kernel_mode_vector.c88
1 files changed, 63 insertions, 25 deletions
diff --git a/arch/riscv/kernel/kernel_mode_vector.c b/arch/riscv/kernel/kernel_mode_vector.c
index 99972a48e86b..77e98b504485 100644
--- a/arch/riscv/kernel/kernel_mode_vector.c
+++ b/arch/riscv/kernel/kernel_mode_vector.c
@@ -10,18 +10,34 @@
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <linux/types.h>
+#include <linux/kvm_types.h>
#include <asm/vector.h>
+#include <asm/kvm_vcpu_vector.h>
#include <asm/switch_to.h>
#include <asm/simd.h>
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
#include <asm/asm-prototypes.h>
#endif
-static inline void riscv_v_flags_set(u32 flags)
+static void (* __rcu kvm_flush_vector_ctx_callback)(void);
+
+void kvm_riscv_register_vctx_callback(void (*func)(void))
+{
+ if (WARN_ON_ONCE(rcu_access_pointer(kvm_flush_vector_ctx_callback)))
+ return;
+
+ rcu_assign_pointer(kvm_flush_vector_ctx_callback, func);
+}
+EXPORT_SYMBOL_GPL(kvm_riscv_register_vctx_callback);
+
+void kvm_riscv_unregister_vctx_callback(void)
{
- WRITE_ONCE(current->thread.riscv_v_flags, flags);
+ rcu_assign_pointer(kvm_flush_vector_ctx_callback, NULL);
+ synchronize_rcu();
}
+EXPORT_SYMBOL_GPL(kvm_riscv_unregister_vctx_callback);
+
static inline void riscv_v_start(u32 flags)
{
@@ -55,13 +71,16 @@ void get_cpu_vector_context(void)
* disable softirqs so it is impossible for softirqs to nest
* get_cpu_vector_context() when kernel is actively using Vector.
*/
- if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_bh_disable();
- else
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+ if (!irqs_disabled())
+ local_bh_disable();
+ } else {
preempt_disable();
+ }
riscv_v_start(RISCV_KERNEL_MODE_V);
}
+EXPORT_SYMBOL_FOR_KVM(get_cpu_vector_context);
/*
* Release the CPU vector context.
@@ -74,10 +93,29 @@ void put_cpu_vector_context(void)
{
riscv_v_stop(RISCV_KERNEL_MODE_V);
- if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_bh_enable();
- else
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+ if (!irqs_disabled())
+ local_bh_enable();
+ } else {
preempt_enable();
+ }
+}
+EXPORT_SYMBOL_FOR_KVM(put_cpu_vector_context);
+
+static void __riscv_flush_vector_context(void)
+{
+ void (*vcpu_flush_v_callback)(void);
+
+ if (riscv_v_flags() & RISCV_V_VCPU_CTX) {
+ rcu_read_lock();
+ vcpu_flush_v_callback = rcu_dereference(kvm_flush_vector_ctx_callback);
+ vcpu_flush_v_callback();
+ rcu_read_unlock();
+ return;
+ }
+
+ riscv_v_vstate_save(&current->thread.vstate, task_pt_regs(current));
+ riscv_v_vstate_set_restore(current, task_pt_regs(current));
}
#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
@@ -121,9 +159,9 @@ static int riscv_v_stop_kernel_context(void)
return 0;
}
-static int riscv_v_start_kernel_context(bool *is_nested)
+static int riscv_v_start_kernel_context(void)
{
- struct __riscv_v_ext_state *kvstate, *uvstate;
+ struct __riscv_v_ext_state *kvstate;
kvstate = &current->thread.kernel_vstate;
if (!kvstate->datap)
@@ -131,7 +169,6 @@ static int riscv_v_start_kernel_context(bool *is_nested)
if (riscv_preempt_v_started(current)) {
WARN_ON(riscv_v_ctx_get_depth() == 0);
- *is_nested = true;
get_cpu_vector_context();
if (riscv_preempt_v_dirty(current)) {
__riscv_v_vstate_save(kvstate, kvstate->datap);
@@ -142,12 +179,18 @@ static int riscv_v_start_kernel_context(bool *is_nested)
}
/* Transfer the ownership of V from user to kernel, then save */
- riscv_v_start(RISCV_PREEMPT_V | RISCV_PREEMPT_V_DIRTY);
- if (__riscv_v_vstate_check(task_pt_regs(current)->status, DIRTY)) {
- uvstate = &current->thread.vstate;
- __riscv_v_vstate_save(uvstate, uvstate->datap);
- }
- riscv_preempt_v_clear_dirty(current);
+ get_cpu_vector_context();
+ __riscv_flush_vector_context();
+ put_cpu_vector_context();
+ /*
+ * A voluntary context switch caused by put_cpu_vector_context() can
+ * raise the NEED_RESTORE flag if preempt_v starts too early due to a
+ * failed risv_v_is_on() check.
+ *
+ * This causes the next context_nesting_end pollute the v-reg from
+ * the stale context memory in kernel-mode vector.
+ */
+ riscv_v_start(RISCV_PREEMPT_V);
return 0;
}
@@ -187,7 +230,7 @@ asmlinkage void riscv_v_context_nesting_end(struct pt_regs *regs)
}
}
#else
-#define riscv_v_start_kernel_context(nested) (-ENOENT)
+#define riscv_v_start_kernel_context() (-ENOENT)
#define riscv_v_stop_kernel_context() (-ENOENT)
#endif /* CONFIG_RISCV_ISA_V_PREEMPTIVE */
@@ -206,21 +249,16 @@ asmlinkage void riscv_v_context_nesting_end(struct pt_regs *regs)
*/
void kernel_vector_begin(void)
{
- bool nested = false;
-
if (WARN_ON(!(has_vector() || has_xtheadvector())))
return;
BUG_ON(!may_use_simd());
- if (riscv_v_start_kernel_context(&nested)) {
+ if (riscv_v_start_kernel_context()) {
get_cpu_vector_context();
- riscv_v_vstate_save(&current->thread.vstate, task_pt_regs(current));
+ __riscv_flush_vector_context();
}
- if (!nested)
- riscv_v_vstate_set_restore(current, task_pt_regs(current));
-
riscv_v_enable();
}
EXPORT_SYMBOL_GPL(kernel_vector_begin);