x86/entry: Set FRED RSP0 on return to userspace instead of context switch
authorXin Li (Intel) <xin@zytor.com>
Thu, 22 Aug 2024 07:39:06 +0000 (00:39 -0700)
committerThomas Gleixner <tglx@linutronix.de>
Sun, 25 Aug 2024 17:23:00 +0000 (19:23 +0200)
The FRED RSP0 MSR points to the top of the kernel stack for user level
event delivery. As this is the task stack it needs to be updated when a
task is scheduled in.

The update is done at context switch. That means it's also done when
switching to kernel threads, which is pointless as those never go out to
user space. For KVM threads this means there are two writes to FRED_RSP0 as
KVM has to switch to the guest value before VMENTER.

Defer the update to the exit to user space path and cache the per CPU
FRED_RSP0 value, so redundant writes can be avoided.

Provide fred_sync_rsp0() for KVM to keep the cache in sync with the actual
MSR value after returning from guest to host mode.

[ tglx: Massage change log ]

Suggested-by: Sean Christopherson <seanjc@google.com>
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Xin Li (Intel) <xin@zytor.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240822073906.2176342-4-xin@zytor.com
arch/x86/include/asm/entry-common.h
arch/x86/include/asm/fred.h
arch/x86/include/asm/switch_to.h
arch/x86/kernel/fred.c

index db970828f3856226a77316b4b09d8146c9842cb9..77d20555e04de5765b392f639e0611eeaf7e423d 100644 (file)
@@ -8,6 +8,7 @@
 #include <asm/nospec-branch.h>
 #include <asm/io_bitmap.h>
 #include <asm/fpu/api.h>
+#include <asm/fred.h>
 
 /* Check that the stack and regs on entry from user mode are sane. */
 static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs)
@@ -63,6 +64,8 @@ static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs,
        if (IS_ENABLED(CONFIG_X86_DEBUG_FPU) || unlikely(ti_work))
                arch_exit_work(ti_work);
 
+       fred_update_rsp0();
+
 #ifdef CONFIG_COMPAT
        /*
         * Compat syscalls set TS_COMPAT.  Make sure we clear it before
index 66d7dbe2d314c1318006dfe84057358213c205c8..25ca00bd70e8346793a7d53ace432952802e6d57 100644 (file)
@@ -36,6 +36,7 @@
 
 #ifdef CONFIG_X86_FRED
 #include <linux/kernel.h>
+#include <linux/sched/task_stack.h>
 
 #include <asm/ptrace.h>
 
@@ -87,12 +88,30 @@ void cpu_init_fred_exceptions(void);
 void cpu_init_fred_rsps(void);
 void fred_complete_exception_setup(void);
 
+DECLARE_PER_CPU(unsigned long, fred_rsp0);
+
+static __always_inline void fred_sync_rsp0(unsigned long rsp0)
+{
+       __this_cpu_write(fred_rsp0, rsp0);
+}
+
+static __always_inline void fred_update_rsp0(void)
+{
+       unsigned long rsp0 = (unsigned long) task_stack_page(current) + THREAD_SIZE;
+
+       if (cpu_feature_enabled(X86_FEATURE_FRED) && (__this_cpu_read(fred_rsp0) != rsp0)) {
+               wrmsrns(MSR_IA32_FRED_RSP0, rsp0);
+               __this_cpu_write(fred_rsp0, rsp0);
+       }
+}
 #else /* CONFIG_X86_FRED */
 static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { return 0; }
 static inline void cpu_init_fred_exceptions(void) { }
 static inline void cpu_init_fred_rsps(void) { }
 static inline void fred_complete_exception_setup(void) { }
-static __always_inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) { }
+static inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) { }
+static inline void fred_sync_rsp0(unsigned long rsp0) { }
+static inline void fred_update_rsp0(void) { }
 #endif /* CONFIG_X86_FRED */
 #endif /* !__ASSEMBLY__ */
 
index e9ded149a9e3987ccc4df5ba631cbace7dfc2307..75248546403d79ecc15d783ee1df1c97801bbf9e 100644 (file)
@@ -70,12 +70,9 @@ static inline void update_task_stack(struct task_struct *task)
 #ifdef CONFIG_X86_32
        this_cpu_write(cpu_tss_rw.x86_tss.sp1, task->thread.sp0);
 #else
-       if (cpu_feature_enabled(X86_FEATURE_FRED)) {
-               wrmsrns(MSR_IA32_FRED_RSP0, (unsigned long)task_stack_page(task) + THREAD_SIZE);
-       } else if (cpu_feature_enabled(X86_FEATURE_XENPV)) {
+       if (!cpu_feature_enabled(X86_FEATURE_FRED) && cpu_feature_enabled(X86_FEATURE_XENPV))
                /* Xen PV enters the kernel on the thread stack. */
                load_sp0(task_top_of_stack(task));
-       }
 #endif
 }
 
index 266c69e332a43cccda6169c8c2f141549a32781b..8d32c3f48abc0cb57964328d5f9520df1c6342e3 100644 (file)
@@ -21,6 +21,9 @@
 
 #define FRED_STKLVL(vector, lvl)       ((lvl) << (2 * (vector)))
 
+DEFINE_PER_CPU(unsigned long, fred_rsp0);
+EXPORT_PER_CPU_SYMBOL(fred_rsp0);
+
 void cpu_init_fred_exceptions(void)
 {
        /* When FRED is enabled by default, remove this log message */