Skip to content

Commit 45b575c

Browse files
nicstangeKAGA-KOKO
authored andcommitted
x86/KVM/VMX: Introduce per-host-cpu analogue of l1tf_flush_l1d
Part of the L1TF mitigation for vmx includes flushing the L1D cache upon VMENTRY. L1D flushes are costly and two modes of operations are provided to users: "always" and the more selective "conditional" mode. If operating in the latter, the cache would get flushed only if a host side code path considered unconfined had been traversed. "Unconfined" in this context means that it might have pulled in sensitive data like user data or kernel crypto keys. The need for L1D flushes is tracked by means of the per-vcpu flag l1tf_flush_l1d. KVM exit handlers considered unconfined set it. A vmx_l1d_flush() subsequently invoked before the next VMENTER will conduct a L1d flush based on its value and reset that flag again. Currently, interrupts delivered "normally" while in root operation between VMEXIT and VMENTER are not taken into account. Part of the reason is that these don't leave any traces and thus, the vmx code is unable to tell if any such has happened. As proposed by Paolo Bonzini, prepare for tracking all interrupts by introducing a new per-cpu flag, "kvm_cpu_l1tf_flush_l1d". It will be in strong analogy to the per-vcpu ->l1tf_flush_l1d. A later patch will make interrupt handlers set it. For the sake of cache locality, group kvm_cpu_l1tf_flush_l1d into x86' per-cpu irq_cpustat_t as suggested by Peter Zijlstra. Provide the helpers kvm_set_cpu_l1tf_flush_l1d(), kvm_clear_cpu_l1tf_flush_l1d() and kvm_get_cpu_l1tf_flush_l1d(). Make them trivial resp. non-existent for !CONFIG_KVM_INTEL as appropriate. Let vmx_l1d_flush() handle kvm_cpu_l1tf_flush_l1d in the same way as l1tf_flush_l1d. Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Nicolai Stange <nstange@suse.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 9aee5f8 commit 45b575c

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

arch/x86/include/asm/hardirq.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
typedef struct {
99
u16 __softirq_pending;
10+
#if IS_ENABLED(CONFIG_KVM_INTEL)
11+
u8 kvm_cpu_l1tf_flush_l1d;
12+
#endif
1013
unsigned int __nmi_count; /* arch dependent */
1114
#ifdef CONFIG_X86_LOCAL_APIC
1215
unsigned int apic_timer_irqs; /* arch dependent */
@@ -58,4 +61,24 @@ extern u64 arch_irq_stat_cpu(unsigned int cpu);
5861
extern u64 arch_irq_stat(void);
5962
#define arch_irq_stat arch_irq_stat
6063

64+
65+
#if IS_ENABLED(CONFIG_KVM_INTEL)
66+
static inline void kvm_set_cpu_l1tf_flush_l1d(void)
67+
{
68+
__this_cpu_write(irq_stat.kvm_cpu_l1tf_flush_l1d, 1);
69+
}
70+
71+
static inline void kvm_clear_cpu_l1tf_flush_l1d(void)
72+
{
73+
__this_cpu_write(irq_stat.kvm_cpu_l1tf_flush_l1d, 0);
74+
}
75+
76+
static inline bool kvm_get_cpu_l1tf_flush_l1d(void)
77+
{
78+
return __this_cpu_read(irq_stat.kvm_cpu_l1tf_flush_l1d);
79+
}
80+
#else /* !IS_ENABLED(CONFIG_KVM_INTEL) */
81+
static inline void kvm_set_cpu_l1tf_flush_l1d(void) { }
82+
#endif /* IS_ENABLED(CONFIG_KVM_INTEL) */
83+
6184
#endif /* _ASM_X86_HARDIRQ_H */

arch/x86/kvm/vmx.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9693,14 +9693,23 @@ static void vmx_l1d_flush(struct kvm_vcpu *vcpu)
96939693
* 'always'
96949694
*/
96959695
if (static_branch_likely(&vmx_l1d_flush_cond)) {
9696-
bool flush_l1d = vcpu->arch.l1tf_flush_l1d;
9696+
bool flush_l1d;
96979697

96989698
/*
9699-
* Clear the flush bit, it gets set again either from
9700-
* vcpu_run() or from one of the unsafe VMEXIT
9701-
* handlers.
9699+
* Clear the per-vcpu flush bit, it gets set again
9700+
* either from vcpu_run() or from one of the unsafe
9701+
* VMEXIT handlers.
97029702
*/
9703+
flush_l1d = vcpu->arch.l1tf_flush_l1d;
97039704
vcpu->arch.l1tf_flush_l1d = false;
9705+
9706+
/*
9707+
* Clear the per-cpu flush bit, it gets set again from
9708+
* the interrupt handlers.
9709+
*/
9710+
flush_l1d |= kvm_get_cpu_l1tf_flush_l1d();
9711+
kvm_clear_cpu_l1tf_flush_l1d();
9712+
97049713
if (!flush_l1d)
97059714
return;
97069715
}

0 commit comments

Comments
 (0)