mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
fe85ee3919
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
91 lines
2.4 KiB
C
91 lines
2.4 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_X86_SWITCH_TO_H
|
|
#define _ASM_X86_SWITCH_TO_H
|
|
|
|
#include <linux/sched/task_stack.h>
|
|
|
|
struct task_struct; /* one of the stranger aspects of C forward declarations */
|
|
|
|
struct task_struct *__switch_to_asm(struct task_struct *prev,
|
|
struct task_struct *next);
|
|
|
|
__visible struct task_struct *__switch_to(struct task_struct *prev,
|
|
struct task_struct *next);
|
|
|
|
asmlinkage void ret_from_fork_asm(void);
|
|
__visible void ret_from_fork(struct task_struct *prev, struct pt_regs *regs,
|
|
int (*fn)(void *), void *fn_arg);
|
|
|
|
/*
|
|
* This is the structure pointed to by thread.sp for an inactive task. The
|
|
* order of the fields must match the code in __switch_to_asm().
|
|
*/
|
|
struct inactive_task_frame {
|
|
#ifdef CONFIG_X86_64
|
|
unsigned long r15;
|
|
unsigned long r14;
|
|
unsigned long r13;
|
|
unsigned long r12;
|
|
#else
|
|
unsigned long flags;
|
|
unsigned long si;
|
|
unsigned long di;
|
|
#endif
|
|
unsigned long bx;
|
|
|
|
/*
|
|
* These two fields must be together. They form a stack frame header,
|
|
* needed by get_frame_pointer().
|
|
*/
|
|
unsigned long bp;
|
|
unsigned long ret_addr;
|
|
};
|
|
|
|
struct fork_frame {
|
|
struct inactive_task_frame frame;
|
|
struct pt_regs regs;
|
|
};
|
|
|
|
#define switch_to(prev, next, last) \
|
|
do { \
|
|
((last) = __switch_to_asm((prev), (next))); \
|
|
} while (0)
|
|
|
|
#ifdef CONFIG_X86_32
|
|
static inline void refresh_sysenter_cs(struct thread_struct *thread)
|
|
{
|
|
/* Only happens when SEP is enabled, no need to test "SEP"arately: */
|
|
if (unlikely(this_cpu_read(cpu_tss_rw.x86_tss.ss1) == thread->sysenter_cs))
|
|
return;
|
|
|
|
this_cpu_write(cpu_tss_rw.x86_tss.ss1, thread->sysenter_cs);
|
|
wrmsr(MSR_IA32_SYSENTER_CS, thread->sysenter_cs, 0);
|
|
}
|
|
#endif
|
|
|
|
/* This is used when switching tasks or entering/exiting vm86 mode. */
|
|
static inline void update_task_stack(struct task_struct *task)
|
|
{
|
|
/* sp0 always points to the entry trampoline stack, which is constant: */
|
|
#ifdef CONFIG_X86_32
|
|
this_cpu_write(cpu_tss_rw.x86_tss.sp1, task->thread.sp0);
|
|
#else
|
|
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
|
|
}
|
|
|
|
static inline void kthread_frame_init(struct inactive_task_frame *frame,
|
|
int (*fun)(void *), void *arg)
|
|
{
|
|
frame->bx = (unsigned long)fun;
|
|
#ifdef CONFIG_X86_32
|
|
frame->di = (unsigned long)arg;
|
|
#else
|
|
frame->r12 = (unsigned long)arg;
|
|
#endif
|
|
}
|
|
|
|
#endif /* _ASM_X86_SWITCH_TO_H */
|