aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuresh Siddha <suresh.b.siddha@intel.com>2012-09-20 14:01:49 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2012-09-21 19:59:04 -0400
commitb1a74bf8212367be2b1d6685c11a84e056eaaaf1 (patch)
tree598eafb9d082cd7ba4d6299dc63bd3511963186d
parenta8615af4bc3621cb01096541dafa6f68352ec2d9 (diff)
x86, kvm: fix kvm's usage of kernel_fpu_begin/end()
Preemption is disabled between kernel_fpu_begin/end() and as such it is not a good idea to use these routines in kvm_load/put_guest_fpu() which can be very far apart. kvm_load/put_guest_fpu() routines are already called with preemption disabled and KVM already uses the preempt notifier to save the guest fpu state using kvm_put_guest_fpu(). So introduce __kernel_fpu_begin/end() routines which don't touch preemption and use them instead of kernel_fpu_begin/end() for KVM's use model of saving/restoring guest FPU state. Also with this change (and with eagerFPU model), fix the host cr0.TS vm-exit state in the case of VMX. For eagerFPU case, host cr0.TS is always clear. So no need to worry about it. For the traditional lazyFPU restore case, change the cr0.TS bit for the host state during vm-exit to be always clear and cr0.TS bit is set in the __vmx_load_host_state() when the FPU (guest FPU or the host task's FPU) state is not active. This ensures that the host/guest FPU state is properly saved, restored during context-switch and with interrupts (using irq_fpu_usable()) not stomping on the active FPU state. Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com> Link: http://lkml.kernel.org/r/1348164109.26695.338.camel@sbsiddha-desk.sc.intel.com Cc: Avi Kivity <avi@redhat.com> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--arch/x86/include/asm/i387.h28
-rw-r--r--arch/x86/kernel/i387.c13
-rw-r--r--arch/x86/kvm/vmx.c10
-rw-r--r--arch/x86/kvm/x86.c4
4 files changed, 40 insertions, 15 deletions
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h
index 6c3bd3782818..ed8089d69094 100644
--- a/arch/x86/include/asm/i387.h
+++ b/arch/x86/include/asm/i387.h
@@ -24,8 +24,32 @@ extern int dump_fpu(struct pt_regs *, struct user_i387_struct *);
24extern void math_state_restore(void); 24extern void math_state_restore(void);
25 25
26extern bool irq_fpu_usable(void); 26extern bool irq_fpu_usable(void);
27extern void kernel_fpu_begin(void); 27
28extern void kernel_fpu_end(void); 28/*
29 * Careful: __kernel_fpu_begin/end() must be called with preempt disabled
30 * and they don't touch the preempt state on their own.
31 * If you enable preemption after __kernel_fpu_begin(), preempt notifier
32 * should call the __kernel_fpu_end() to prevent the kernel/user FPU
33 * state from getting corrupted. KVM for example uses this model.
34 *
35 * All other cases use kernel_fpu_begin/end() which disable preemption
36 * during kernel FPU usage.
37 */
38extern void __kernel_fpu_begin(void);
39extern void __kernel_fpu_end(void);
40
41static inline void kernel_fpu_begin(void)
42{
43 WARN_ON_ONCE(!irq_fpu_usable());
44 preempt_disable();
45 __kernel_fpu_begin();
46}
47
48static inline void kernel_fpu_end(void)
49{
50 __kernel_fpu_end();
51 preempt_enable();
52}
29 53
30/* 54/*
31 * Some instructions like VIA's padlock instructions generate a spurious 55 * Some instructions like VIA's padlock instructions generate a spurious
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
index 6782e3983865..675a05012449 100644
--- a/arch/x86/kernel/i387.c
+++ b/arch/x86/kernel/i387.c
@@ -73,32 +73,29 @@ bool irq_fpu_usable(void)
73} 73}
74EXPORT_SYMBOL(irq_fpu_usable); 74EXPORT_SYMBOL(irq_fpu_usable);
75 75
76void kernel_fpu_begin(void) 76void __kernel_fpu_begin(void)
77{ 77{
78 struct task_struct *me = current; 78 struct task_struct *me = current;
79 79
80 WARN_ON_ONCE(!irq_fpu_usable());
81 preempt_disable();
82 if (__thread_has_fpu(me)) { 80 if (__thread_has_fpu(me)) {
83 __save_init_fpu(me); 81 __save_init_fpu(me);
84 __thread_clear_has_fpu(me); 82 __thread_clear_has_fpu(me);
85 /* We do 'stts()' in kernel_fpu_end() */ 83 /* We do 'stts()' in __kernel_fpu_end() */
86 } else if (!use_eager_fpu()) { 84 } else if (!use_eager_fpu()) {
87 this_cpu_write(fpu_owner_task, NULL); 85 this_cpu_write(fpu_owner_task, NULL);
88 clts(); 86 clts();
89 } 87 }
90} 88}
91EXPORT_SYMBOL(kernel_fpu_begin); 89EXPORT_SYMBOL(__kernel_fpu_begin);
92 90
93void kernel_fpu_end(void) 91void __kernel_fpu_end(void)
94{ 92{
95 if (use_eager_fpu()) 93 if (use_eager_fpu())
96 math_state_restore(); 94 math_state_restore();
97 else 95 else
98 stts(); 96 stts();
99 preempt_enable();
100} 97}
101EXPORT_SYMBOL(kernel_fpu_end); 98EXPORT_SYMBOL(__kernel_fpu_end);
102 99
103void unlazy_fpu(struct task_struct *tsk) 100void unlazy_fpu(struct task_struct *tsk)
104{ 101{
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index c00f03de1b79..70dfcec3c463 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -1493,8 +1493,12 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx)
1493#ifdef CONFIG_X86_64 1493#ifdef CONFIG_X86_64
1494 wrmsrl(MSR_KERNEL_GS_BASE, vmx->msr_host_kernel_gs_base); 1494 wrmsrl(MSR_KERNEL_GS_BASE, vmx->msr_host_kernel_gs_base);
1495#endif 1495#endif
1496 if (user_has_fpu()) 1496 /*
1497 clts(); 1497 * If the FPU is not active (through the host task or
1498 * the guest vcpu), then restore the cr0.TS bit.
1499 */
1500 if (!user_has_fpu() && !vmx->vcpu.guest_fpu_loaded)
1501 stts();
1498 load_gdt(&__get_cpu_var(host_gdt)); 1502 load_gdt(&__get_cpu_var(host_gdt));
1499} 1503}
1500 1504
@@ -3730,7 +3734,7 @@ static void vmx_set_constant_host_state(void)
3730 unsigned long tmpl; 3734 unsigned long tmpl;
3731 struct desc_ptr dt; 3735 struct desc_ptr dt;
3732 3736
3733 vmcs_writel(HOST_CR0, read_cr0() | X86_CR0_TS); /* 22.2.3 */ 3737 vmcs_writel(HOST_CR0, read_cr0() & ~X86_CR0_TS); /* 22.2.3 */
3734 vmcs_writel(HOST_CR4, read_cr4()); /* 22.2.3, 22.2.5 */ 3738 vmcs_writel(HOST_CR4, read_cr4()); /* 22.2.3, 22.2.5 */
3735 vmcs_writel(HOST_CR3, read_cr3()); /* 22.2.3 FIXME: shadow tables */ 3739 vmcs_writel(HOST_CR3, read_cr3()); /* 22.2.3 FIXME: shadow tables */
3736 3740
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index cf637f572bdd..02b2cd520693 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5972,7 +5972,7 @@ void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
5972 */ 5972 */
5973 kvm_put_guest_xcr0(vcpu); 5973 kvm_put_guest_xcr0(vcpu);
5974 vcpu->guest_fpu_loaded = 1; 5974 vcpu->guest_fpu_loaded = 1;
5975 kernel_fpu_begin(); 5975 __kernel_fpu_begin();
5976 fpu_restore_checking(&vcpu->arch.guest_fpu); 5976 fpu_restore_checking(&vcpu->arch.guest_fpu);
5977 trace_kvm_fpu(1); 5977 trace_kvm_fpu(1);
5978} 5978}
@@ -5986,7 +5986,7 @@ void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
5986 5986
5987 vcpu->guest_fpu_loaded = 0; 5987 vcpu->guest_fpu_loaded = 0;
5988 fpu_save_init(&vcpu->arch.guest_fpu); 5988 fpu_save_init(&vcpu->arch.guest_fpu);
5989 kernel_fpu_end(); 5989 __kernel_fpu_end();
5990 ++vcpu->stat.fpu_reload; 5990 ++vcpu->stat.fpu_reload;
5991 kvm_make_request(KVM_REQ_DEACTIVATE_FPU, vcpu); 5991 kvm_make_request(KVM_REQ_DEACTIVATE_FPU, vcpu);
5992 trace_kvm_fpu(0); 5992 trace_kvm_fpu(0);