Skip to contentSkip to conversation

x86, KVM: Optimize SEV cache flushing

PATCH Applied Sean Christopherson 5 participants 27 messages May 22, 2025 – Jul 11, 2025

Subsystems (5)
DRM drivers x86 KVM DRM misc +1 more subsystems
lore
Share & tools
lore

Sean Christopherson

author maintainer
#0 lore raw

v3 cover x86, KVM: Optimize SEV cache flushing

This is the combination of Kevin's WBNOINVD series[1] with Zheyun's targeted flushing series[2]. The combined goal is to use WBNOINVD instead of WBINVD when doing cached maintenance to prevent data corruption due to C-bit aliasing, and to reduce the number of cache invalidations by only performing flushes on CPUs that have entered the relevant VM since the last cache flush.

All of the non-KVM patches are frontloaded and based on v6.15-rc7, so that they can go through the tip tree (in a stable branch, please :-) ).

v3:
- Move the non-KVM patches to the front. [Ingo]
- Add comments to document WBINVD vs. WBNOINVD. [Ingo]
- Collect acks/reviews. [Ingo, Tom]
- Rename xx_wbinvd_on_many_cpus() to xx_on_cpus_mask(). [Ingo]
- Fix a repeated "be" in a comment. [Francesco]

v2:
- https://lore.kernel.org/all/[email protected]
- Add a missing -ENOMEM in __sev_guest_init(). [Tom]
- Collect reviews. [Kai, Tom]
- Fix stub prototypes. [Zheyun]
- Kill off dead pr_err() code on DRM's wbinvd_on_all_cpus() usage.

v1: https://lore.kernel.org/all/[email protected]

[1] https://lore.kernel.org/all/[email protected]
[2] https://lore.kernel.org/all/[email protected]

Kevin Loughlin (2):
x86, lib: Add WBNOINVD helper functions
KVM: SEV: Prefer WBNOINVD over WBINVD for cache maintenance efficiency

Sean Christopherson (3):
drm/gpu: Remove dead checks on wbinvd_on_all_cpus()'s return value x86, lib: Drop the unused return value from wbinvd_on_all_cpus()
KVM: x86: Use wbinvd_on_cpu() instead of an open-coded equivalent

Zheyun Shen (3):
x86, lib: Add wbinvd and wbnoinvd helpers to target multiple CPUs
KVM: SVM: Remove wbinvd in sev_vm_destroy()
KVM: SVM: Flush cache only on CPUs running SEV guest

 arch/x86/include/asm/smp.h           | 23 +++++++-
 arch/x86/include/asm/special_insns.h | 32 ++++++++++-
 arch/x86/kvm/svm/sev.c               | 85 +++++++++++++++++++---------
 arch/x86/kvm/svm/svm.h               |  1 +
 arch/x86/kvm/x86.c                   | 11 +---
 arch/x86/lib/cache-smp.c             | 26 ++++++++-
 drivers/gpu/drm/drm_cache.c          |  9 +--
 7 files changed, 140 insertions(+), 47 deletions(-)

base-commit: a5806cd506af5a7c19bcd596e4708b5c464bfd21

Signature · 1 line
2.49.0.1151.ga128411c76-goog
author maintainer
#1 lore raw

PATCH v3 1/8 drm/gpu: Remove dead checks on wbinvd_on_all_cpus()'s return value

Remove the checks and associated pr_err() on wbinvd_on_all_cpus() failure, as the helper has unconditionally returned 0/success since commit caa759323c73 ("smp: Remove smp_call_function() and on_each_cpu() return values").

---
 drivers/gpu/drm/drm_cache.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
drivers/gpu/drm/drm_cache.c +3 -6
index 7051c9c909c2..ea1d2d5d2c66 100644
@@ -93,8 +93,7 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 
 #elif defined(__powerpc__)
 	unsigned long i;
@@ -139,8 +138,7 @@ drm_clflush_sg(struct sg_table *st)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 #else
 	WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
 #endif
@@ -172,8 +170,7 @@ drm_clflush_virt_range(void *addr, unsigned long length)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 #else
 	WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
 #endif
Signature · 1 line
2.49.0.1151.ga128411c76-goog
#2 lore raw

[tip: x86/core] drm/gpu: Remove dead checks on wbinvd_on_all_cpus()'s return value

The following commit has been merged into the x86/core branch of tip:

The following commit has been merged into the x86/core branch of tip:

Commit-ID:     1d738dbb252f66dd909a662d85c67b93314d7ae7
Gitweb:        https://git.kernel.org/tip/1d738dbb252f66dd909a662d85c67b93314d7ae7
Author:        Sean Christopherson <[email protected]>
AuthorDate:    Thu, 22 May 2025 16:37:25 -07:00
Committer:     Borislav Petkov (AMD) <[email protected]>
CommitterDate: Thu, 10 Jul 2025 13:07:26 +02:00

drm/gpu: Remove dead checks on wbinvd_on_all_cpus()'s return value

Remove the checks and associated pr_err() on wbinvd_on_all_cpus() failure, as the helper has unconditionally returned 0/success since commit

caa759323c73 ("smp: Remove smp_call_function() and on_each_cpu() return values").

---
 drivers/gpu/drm/drm_cache.c |  9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
drivers/gpu/drm/drm_cache.c +3 -6
index 7051c9c..ea1d2d5 100644
@@ -93,8 +93,7 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 
 #elif defined(__powerpc__)
 	unsigned long i;
@@ -139,8 +138,7 @@ drm_clflush_sg(struct sg_table *st)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 #else
 	WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
 #endif
@@ -172,8 +170,7 @@ drm_clflush_virt_range(void *addr, unsigned long length)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 #else
 	WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
 #endif
Acked-by on v3 1/8
#3 lore raw

Re: [PATCH v3 1/8] drm/gpu: Remove dead checks on wbinvd_on_all_cpus()'s return value

In reply to Sean Christopherson
Quoted 6 lines · Sean Christopherson

Remove the checks and associated pr_err() on wbinvd_on_all_cpus() failure, as the helper has unconditionally returned 0/success since commit caa759323c73 ("smp: Remove smp_call_function() and on_each_cpu() return values").

I guess this'll all land through x86 trees, for that on this and the patch from Peter to adjust the module exports to include drm and i915:

Cheers, Sima

Quoted 41 lines
---
 drivers/gpu/drm/drm_cache.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)
drivers/gpu/drm/drm_cache.c +3 -6
index 7051c9c909c2..ea1d2d5d2c66 100644
@@ -93,8 +93,7 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 
 #elif defined(__powerpc__)
 	unsigned long i;
@@ -139,8 +138,7 @@ drm_clflush_sg(struct sg_table *st)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 #else
 	WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
 #endif
@@ -172,8 +170,7 @@ drm_clflush_virt_range(void *addr, unsigned long length)
 		return;
 	}
 
-	if (wbinvd_on_all_cpus())
-		pr_err("Timed out waiting for cache flush\n");
+	wbinvd_on_all_cpus();
 #else
 	WARN_ONCE(1, "Architecture has no drm_cache.c support\n");
 #endif
Signature · 1 line
2.49.0.1151.ga128411c76-goog
Signature · 3 lines
Simona Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
author maintainer
#4 lore raw

PATCH v3 2/8 x86, lib: Drop the unused return value from wbinvd_on_all_cpus()

In reply to Sean Christopherson

Drop wbinvd_on_all_cpus()'s return value; both the "real" version and the stub always return '0', and none of the callers check the return.

---
 arch/x86/include/asm/smp.h | 5 ++---
 arch/x86/lib/cache-smp.c   | 3 +--
 2 files changed, 3 insertions(+), 5 deletions(-)
arch/x86/include/asm/smp.h +2 -3
index 0c1c68039d6f..028f126018c9 100644
@@ -112,7 +112,7 @@ void __noreturn hlt_play_dead(void);
 void native_play_dead(void);
 void play_dead_common(void);
 void wbinvd_on_cpu(int cpu);
-int wbinvd_on_all_cpus(void);
+void wbinvd_on_all_cpus(void);
 
 void smp_kick_mwait_play_dead(void);
 void __noreturn mwait_play_dead(unsigned int eax_hint);
@@ -148,10 +148,9 @@ static inline struct cpumask *cpu_l2c_shared_mask(int cpu)
 
 #else /* !CONFIG_SMP */
 #define wbinvd_on_cpu(cpu)     wbinvd()
-static inline int wbinvd_on_all_cpus(void)
+static inline void wbinvd_on_all_cpus(void)
 {
 	wbinvd();
-	return 0;
 }
 
 static inline struct cpumask *cpu_llc_shared_mask(int cpu)
arch/x86/lib/cache-smp.c +1 -2
index 7af743bd3b13..079c3f3cd32c 100644
@@ -14,9 +14,8 @@ void wbinvd_on_cpu(int cpu)
 }
 EXPORT_SYMBOL(wbinvd_on_cpu);
 
-int wbinvd_on_all_cpus(void)
+void wbinvd_on_all_cpus(void)
 {
 	on_each_cpu(__wbinvd, NULL, 1);
-	return 0;
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
Signature · 1 line
2.49.0.1151.ga128411c76-goog
#5 lore raw

[tip: x86/core] x86/lib: Drop the unused return value from wbinvd_on_all_cpus()

The following commit has been merged into the x86/core branch of tip:

The following commit has been merged into the x86/core branch of tip:

Commit-ID:     e638081751a292560a8aed36ec72e8f65b057892
Gitweb:        https://git.kernel.org/tip/e638081751a292560a8aed36ec72e8f65b057892
Author:        Sean Christopherson <[email protected]>
AuthorDate:    Thu, 22 May 2025 16:37:26 -07:00
Committer:     Borislav Petkov (AMD) <[email protected]>
CommitterDate: Thu, 10 Jul 2025 13:13:26 +02:00

x86/lib: Drop the unused return value from wbinvd_on_all_cpus()

Drop wbinvd_on_all_cpus()'s return value; both the "real" version and the stub always return '0', and none of the callers check the return.

---
 arch/x86/include/asm/smp.h | 5 ++---
 arch/x86/lib/cache-smp.c   | 3 +--
 2 files changed, 3 insertions(+), 5 deletions(-)
arch/x86/include/asm/smp.h +2 -3
index 0c1c680..028f126 100644
@@ -112,7 +112,7 @@ void __noreturn hlt_play_dead(void);
 void native_play_dead(void);
 void play_dead_common(void);
 void wbinvd_on_cpu(int cpu);
-int wbinvd_on_all_cpus(void);
+void wbinvd_on_all_cpus(void);
 
 void smp_kick_mwait_play_dead(void);
 void __noreturn mwait_play_dead(unsigned int eax_hint);
@@ -148,10 +148,9 @@ static inline struct cpumask *cpu_l2c_shared_mask(int cpu)
 
 #else /* !CONFIG_SMP */
 #define wbinvd_on_cpu(cpu)     wbinvd()
-static inline int wbinvd_on_all_cpus(void)
+static inline void wbinvd_on_all_cpus(void)
 {
 	wbinvd();
-	return 0;
 }
 
 static inline struct cpumask *cpu_llc_shared_mask(int cpu)
arch/x86/lib/cache-smp.c +1 -2
index 7af743b..079c3f3 100644
@@ -14,9 +14,8 @@ void wbinvd_on_cpu(int cpu)
 }
 EXPORT_SYMBOL(wbinvd_on_cpu);
 
-int wbinvd_on_all_cpus(void)
+void wbinvd_on_all_cpus(void)
 {
 	on_each_cpu(__wbinvd, NULL, 1);
-	return 0;
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
author maintainer
#6 lore raw

PATCH v3 3/8 x86, lib: Add WBNOINVD helper functions

In reply to Sean Christopherson

From: Kevin Loughlin <[email protected]>

In line with WBINVD usage, add WBONINVD helper functions. Explicitly fall back to WBINVD (via alternative()) if WBNOINVD isn't supported even though the instruction itself is backwards compatible (WBNOINVD is WBINVD with an ignored REP prefix), so that disabling X86_FEATURE_WBNOINVD behaves as one would expect, e.g. in case there's a hardware issue that affects WBNOINVD.

Opportunsitically add comments explaining the architectural behavior of WBINVD and WBNOINVD, and to provide hints and pointers to uarch-specific behavior.

Note, alternative() ensures compatibility with early boot code as needed.

---
 arch/x86/include/asm/smp.h           |  6 ++++++
 arch/x86/include/asm/special_insns.h | 32 +++++++++++++++++++++++++++-
 arch/x86/lib/cache-smp.c             | 11 ++++++++++
 3 files changed, 48 insertions(+), 1 deletion(-)
arch/x86/include/asm/smp.h +6 -0
index 028f126018c9..e08f1ae25401 100644
@@ -113,6 +113,7 @@ void native_play_dead(void);
 void play_dead_common(void);
 void wbinvd_on_cpu(int cpu);
 void wbinvd_on_all_cpus(void);
+void wbnoinvd_on_all_cpus(void);
 
 void smp_kick_mwait_play_dead(void);
 void __noreturn mwait_play_dead(unsigned int eax_hint);
@@ -153,6 +154,11 @@ static inline void wbinvd_on_all_cpus(void)
 	wbinvd();
 }
 
+static inline void wbnoinvd_on_all_cpus(void)
+{
+	wbnoinvd();
+}
+
 static inline struct cpumask *cpu_llc_shared_mask(int cpu)
 {
 	return (struct cpumask *)cpumask_of(0);
arch/x86/include/asm/special_insns.h +31 -1
index 6266d6b9e0b8..f2240c4ac0ea 100644
@@ -115,9 +115,39 @@ static inline void wrpkru(u32 pkru)
 }
 #endif
 
+/*
+ * Write back all modified lines in all levels of cache associated with this
+ * logical processor to main memory, and then invalidate all caches.  Depending
+ * on the micro-architecture, WBINVD (and WBNOINVD below) may or may not affect
+ * lower level caches associated with another logical processor that shares any
+ * level of this processor’s cache hierarchy.
+ *
+ * Note, AMD CPUs enumerate the behavior or WB{NO}{INVD} with respect to other
+ * logical, non-originating processors in CPUID 0x8000001D.EAX[N:0].
+ */
 static __always_inline void wbinvd(void)
 {
-	asm volatile("wbinvd": : :"memory");
+	asm volatile("wbinvd" : : : "memory");
+}
+
+/* Instruction encoding provided for binutils backwards compatibility. */
+#define ASM_WBNOINVD _ASM_BYTES(0xf3,0x0f,0x09)
+
+/*
+ * Write back all modified lines in all levels of cache associated with this
+ * logical processor to main memory, but do NOT explicitly invalidate caches,
+ * i.e. leave all/most cache lines in the hierarchy in non-modified state.
+ */
+static __always_inline void wbnoinvd(void)
+{
+	/*
+	 * Explicitly encode WBINVD if X86_FEATURE_WBNOINVD is unavailable even
+	 * though WBNOINVD is backwards compatible (it's simply WBINVD with an
+	 * ignored REP prefix), to guarantee that WBNOINVD isn't used if it
+	 * needs to be avoided for any reason.  For all supported usage in the
+	 * kernel, WBINVD is functionally a superset of WBNOINVD.
+	 */
+	alternative("wbinvd", ASM_WBNOINVD, X86_FEATURE_WBNOINVD);
 }
 
 static inline unsigned long __read_cr4(void)
arch/x86/lib/cache-smp.c +11 -0
index 079c3f3cd32c..1789db5d8825 100644
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
 	on_each_cpu(__wbinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
+
+static void __wbnoinvd(void *dummy)
+{
+	wbnoinvd();
+}
+
+void wbnoinvd_on_all_cpus(void)
+{
+	on_each_cpu(__wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL(wbnoinvd_on_all_cpus);
Signature · 1 line
2.49.0.1151.ga128411c76-goog
maintainer
#7 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

Quoted 9 lines · Sean Christopherson
arch/x86/lib/cache-smp.c +1 -0
index 079c3f3cd32c..1789db5d8825 100644
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
 	on_each_cpu(__wbinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
+

arch/x86/lib/cache-smp.c void wbinvd_on_all_cpus(void) · line 32

Quoted 10 lines
arch/x86/lib/cache-smp.c +10 -0
+static void __wbnoinvd(void *dummy)
+{
+	wbnoinvd();
+}
+
+void wbnoinvd_on_all_cpus(void)
+{
+	on_each_cpu(__wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL(wbnoinvd_on_all_cpus);

If there's no particular reason for the non-GPL export besides being consistent with the rest - yes, I did the change for wbinvd_on_all_cpus() but that was loooong time ago - I'd simply make this export _GPL.

Thx.

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
author maintainer
#8 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

Quoted 16 lines · Borislav Petkov
Quoted 15 lines · Sean Christopherson
arch/x86/lib/cache-smp.c +7 -0
index 079c3f3cd32c..1789db5d8825 100644
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
 	on_each_cpu(__wbinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
+
+static void __wbnoinvd(void *dummy)
+{
+	wbnoinvd();
+}
+
+void wbnoinvd_on_all_cpus(void)
Quoted 8 lines

arch/x86/lib/cache-smp.c void wbinvd_on_all_cpus(void) · line 32

Quoted 4 lines
arch/x86/lib/cache-smp.c +4 -0
+{
+	on_each_cpu(__wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL(wbnoinvd_on_all_cpus);

If there's no particular reason for the non-GPL export besides being consistent with the rest - yes, I did the change for wbinvd_on_all_cpus() but that was loooong time ago - I'd simply make this export _GPL.

Yeah, AFAIK, no reason other than consistency. GPL it.

maintainer
#9 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

Quoted 1 line · Sean Christopherson

Yeah, AFAIK, no reason other than consistency. GPL it.

Done. Tag x86_core_for_kvm on tip.

It should appear on the mirrors soon, I hope.

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
#10 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

In reply to Borislav Petkov
Quoted 16 lines · Borislav Petkov
Quoted 15 lines · Sean Christopherson
arch/x86/lib/cache-smp.c +7 -0
index 079c3f3cd32c..1789db5d8825 100644
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
 	on_each_cpu(__wbinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
+
+static void __wbnoinvd(void *dummy)
+{
+	wbnoinvd();
+}
+
+void wbnoinvd_on_all_cpus(void)
Quoted 8 lines

arch/x86/lib/cache-smp.c void wbinvd_on_all_cpus(void) · line 32

Quoted 4 lines
arch/x86/lib/cache-smp.c +4 -0
+{
+	on_each_cpu(__wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL(wbnoinvd_on_all_cpus);

If there's no particular reason for the non-GPL export besides being consistent with the rest - yes, I did the change for wbinvd_on_all_cpus() but that was loooong time ago - I'd simply make this export _GPL.

Uhhhh, how about we use this fancy export to known modules only thing for this?

These are typical things we do *NOT* want people to actually use.

#11 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

Quoted 26 lines · Peter Zijlstra
Quoted 16 lines · Borislav Petkov
Quoted 15 lines · Sean Christopherson
arch/x86/lib/cache-smp.c +7 -0
index 079c3f3cd32c..1789db5d8825 100644
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
 	on_each_cpu(__wbinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
+
+static void __wbnoinvd(void *dummy)
+{
+	wbnoinvd();
+}
+
+void wbnoinvd_on_all_cpus(void)
Quoted 8 lines

arch/x86/lib/cache-smp.c void wbinvd_on_all_cpus(void) · line 32

Quoted 4 lines
arch/x86/lib/cache-smp.c +4 -0
+{
+	on_each_cpu(__wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL(wbnoinvd_on_all_cpus);

If there's no particular reason for the non-GPL export besides being consistent with the rest - yes, I did the change for wbinvd_on_all_cpus() but that was loooong time ago - I'd simply make this export _GPL.

Quoted 4 lines

Uhhhh, how about we use this fancy export to known modules only thing for this?

These are typical things we do *NOT* want people to actually use.

So kvm-amd is the SEV stuff, AGPGART is the ancient crap nobody cares about, CCP is more SEV stuff, DRM actually does CLFLUSH loops, but has a WBINVD fallback. i915 is rude and actually does WBINVD. Could they pretty please also do CLFLUSH loops?

Anyway, the below seems to survive an allmodconfig.

---

arch/x86/lib/cache-smp.c +5 -5
index c5c60d07308c..ac3cc32a4054 100644
@@ -12,19 +12,19 @@ void wbinvd_on_cpu(int cpu)
 {
 	smp_call_function_single(cpu, __wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL(wbinvd_on_cpu);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_cpu, "kvm-amd,agpgart,ccp,drm,i915");
 
 void wbinvd_on_all_cpus(void)
 {
 	on_each_cpu(__wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL(wbinvd_on_all_cpus);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_all_cpus, "kvm-amd,agpgart,ccp,drm,i915,intel-gtt");
 
 void wbinvd_on_cpus_mask(struct cpumask *cpus)
 {
 	on_each_cpu_mask(cpus, __wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbinvd_on_cpus_mask);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_cpus_mask, "kvm,kvm-amd,agpgart,ccp,drm,i915");
 
 static void __wbnoinvd(void *dummy)
 {
@@ -35,10 +35,10 @@ void wbnoinvd_on_all_cpus(void)
 {
 	on_each_cpu(__wbnoinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbnoinvd_on_all_cpus);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbnoinvd_on_all_cpus, "kvm-amd,agpgart,ccp,drm,i915");
 
 void wbnoinvd_on_cpus_mask(struct cpumask *cpus)
 {
 	on_each_cpu_mask(cpus, __wbnoinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbnoinvd_on_cpus_mask);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbnoinvd_on_cpus_mask, "kvm-amd,agpgart,ccp,drm,i915");
author maintainer
#12 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

Quoted 30 lines · Peter Zijlstra
Quoted 29 lines · Peter Zijlstra
Quoted 16 lines · Borislav Petkov
Quoted 15 lines · Sean Christopherson
arch/x86/lib/cache-smp.c +7 -0
index 079c3f3cd32c..1789db5d8825 100644
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
 	on_each_cpu(__wbinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
+
+static void __wbnoinvd(void *dummy)
+{
+	wbnoinvd();
+}
+
+void wbnoinvd_on_all_cpus(void)
Quoted 8 lines

arch/x86/lib/cache-smp.c void wbinvd_on_all_cpus(void) · line 32

Quoted 4 lines
arch/x86/lib/cache-smp.c +4 -0
+{
+	on_each_cpu(__wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL(wbnoinvd_on_all_cpus);

If there's no particular reason for the non-GPL export besides being consistent with the rest - yes, I did the change for wbinvd_on_all_cpus() but that was loooong time ago - I'd simply make this export _GPL.

Uhhhh, how about we use this fancy export to known modules only thing for this?

Quoted 6 lines
Quoted 1 line

These are typical things we do *NOT* want people to actually use.

So kvm-amd is the SEV stuff, AGPGART is the ancient crap nobody cares about, CCP is more SEV stuff, DRM actually does CLFLUSH loops, but has a WBINVD fallback. i915 is rude and actually does WBINVD. Could they pretty please also do CLFLUSH loops?

FWIW, doing CLFLUSH in KVM isn't feasible. In multiple flows, KVM doesn't have a valid virtual mapping, and hardware *requires* a WBINVD for at least one of the SEV paths.

arch/x86/lib/cache-smp.c void wbinvd_on_cpu(int cpu) · line 15

Quoted 13 lines

Anyway, the below seems to survive an allmodconfig.

---

arch/x86/lib/cache-smp.c +1 -1
index c5c60d07308c..ac3cc32a4054 100644
@@ -12,19 +12,19 @@ void wbinvd_on_cpu(int cpu)
 {
 	smp_call_function_single(cpu, __wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL(wbinvd_on_cpu);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_cpu, "kvm-amd,agpgart,ccp,drm,i915");

Patch 5 of this series drops KVM's homebrewed version of WBINVD-on-CPU, so this one at least would need to export the symbol for "kvm" as well.

arch/x86/kvm/x86.c +1 -7
index 1d0e9180148d..d63a2c27e058 100644
@@ -4965,11 +4965,6 @@ long kvm_arch_dev_ioctl(struct file *filp,
 	return r;
 }
 
-static void wbinvd_ipi(void *garbage)
-{
-	wbinvd();
-}
-
 static bool need_emulate_wbinvd(struct kvm_vcpu *vcpu)
 {
 	return kvm_arch_has_noncoherent_dma(vcpu->kvm);
@@ -4991,8 +4986,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 		if (kvm_x86_call(has_wbinvd_exit)())
 			cpumask_set_cpu(cpu, vcpu->arch.wbinvd_dirty_mask);
 		else if (vcpu->cpu != -1 && vcpu->cpu != cpu)
-			smp_call_function_single(vcpu->cpu,
-					wbinvd_ipi, NULL, 1);
+			wbinvd_on_cpu(vcpu->cpu);
 	}
 
 	kvm_x86_call(vcpu_load)(vcpu, cpu);
Quoted 30 lines
arch/x86/lib/cache-smp.c +4 -4
 void wbinvd_on_all_cpus(void)
 {
 	on_each_cpu(__wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL(wbinvd_on_all_cpus);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_all_cpus, "kvm-amd,agpgart,ccp,drm,i915,intel-gtt");
 
 void wbinvd_on_cpus_mask(struct cpumask *cpus)
 {
 	on_each_cpu_mask(cpus, __wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbinvd_on_cpus_mask);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_cpus_mask, "kvm,kvm-amd,agpgart,ccp,drm,i915");
 
 static void __wbnoinvd(void *dummy)
 {
@@ -35,10 +35,10 @@ void wbnoinvd_on_all_cpus(void)
 {
 	on_each_cpu(__wbnoinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbnoinvd_on_all_cpus);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbnoinvd_on_all_cpus, "kvm-amd,agpgart,ccp,drm,i915");
 
 void wbnoinvd_on_cpus_mask(struct cpumask *cpus)
 {
 	on_each_cpu_mask(cpus, __wbnoinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbnoinvd_on_cpus_mask);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbnoinvd_on_cpus_mask, "kvm-amd,agpgart,ccp,drm,i915");
#13 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

Quoted 8 lines · Sean Christopherson
Quoted 4 lines

So kvm-amd is the SEV stuff, AGPGART is the ancient crap nobody cares about, CCP is more SEV stuff, DRM actually does CLFLUSH loops, but has a WBINVD fallback. i915 is rude and actually does WBINVD. Could they pretty please also do CLFLUSH loops?

FWIW, doing CLFLUSH in KVM isn't feasible. In multiple flows, KVM doesn't have a valid virtual mapping, and hardware *requires* a WBINVD for at least one of the SEV paths.

Yeah, I know. We should give the hardware folks more grief about this though. If we ever get into the situation of requiring WBINVD, they've messed up.

Quoted 16 lines

arch/x86/lib/cache-smp.c void wbinvd_on_cpu(int cpu) · line 15

Quoted 13 lines

Anyway, the below seems to survive an allmodconfig.

---

arch/x86/lib/cache-smp.c +1 -1
index c5c60d07308c..ac3cc32a4054 100644
@@ -12,19 +12,19 @@ void wbinvd_on_cpu(int cpu)
 {
 	smp_call_function_single(cpu, __wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL(wbinvd_on_cpu);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_cpu, "kvm-amd,agpgart,ccp,drm,i915");

Patch 5 of this series drops KVM's homebrewed version of WBINVD-on-CPU, so this one at least would need to export the symbol for "kvm" as well.

Ah, sure. At the same time, cpumask_of(cpu) is very cheap.

#14 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

In reply to Peter Zijlstra
Quoted 30 lines · Peter Zijlstra
Quoted 29 lines · Peter Zijlstra
Quoted 16 lines · Borislav Petkov
Quoted 15 lines · Sean Christopherson
arch/x86/lib/cache-smp.c +7 -0
index 079c3f3cd32c..1789db5d8825 100644
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
 	on_each_cpu(__wbinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
+
+static void __wbnoinvd(void *dummy)
+{
+	wbnoinvd();
+}
+
+void wbnoinvd_on_all_cpus(void)
Quoted 8 lines

arch/x86/lib/cache-smp.c void wbinvd_on_all_cpus(void) · line 32

Quoted 4 lines
arch/x86/lib/cache-smp.c +4 -0
+{
+	on_each_cpu(__wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL(wbnoinvd_on_all_cpus);

If there's no particular reason for the non-GPL export besides being consistent with the rest - yes, I did the change for wbinvd_on_all_cpus() but that was loooong time ago - I'd simply make this export _GPL.

Uhhhh, how about we use this fancy export to known modules only thing for this?

Quoted 6 lines
Quoted 1 line

These are typical things we do *NOT* want people to actually use.

So kvm-amd is the SEV stuff, AGPGART is the ancient crap nobody cares about, CCP is more SEV stuff, DRM actually does CLFLUSH loops, but has a WBINVD fallback. i915 is rude and actually does WBINVD. Could they pretty please also do CLFLUSH loops?

There's some ancient pentium M where clflush is a no-op (or at least not getting stuff flushed enough for the gpu to see it), but we still need to ensure cache coherency with the non-coherent gpu, and wbinvd does the job. I figured this out with sheer desperation over a decade ago myself, it's pain.

There shouldn't be any other reason for i915 to wbinvd. -Sima

Quoted 44 lines

Anyway, the below seems to survive an allmodconfig.

---

arch/x86/lib/cache-smp.c +5 -5
index c5c60d07308c..ac3cc32a4054 100644
@@ -12,19 +12,19 @@ void wbinvd_on_cpu(int cpu)
 {
 	smp_call_function_single(cpu, __wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL(wbinvd_on_cpu);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_cpu, "kvm-amd,agpgart,ccp,drm,i915");
 
 void wbinvd_on_all_cpus(void)
 {
 	on_each_cpu(__wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL(wbinvd_on_all_cpus);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_all_cpus, "kvm-amd,agpgart,ccp,drm,i915,intel-gtt");
 
 void wbinvd_on_cpus_mask(struct cpumask *cpus)
 {
 	on_each_cpu_mask(cpus, __wbinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbinvd_on_cpus_mask);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbinvd_on_cpus_mask, "kvm,kvm-amd,agpgart,ccp,drm,i915");
 
 static void __wbnoinvd(void *dummy)
 {
@@ -35,10 +35,10 @@ void wbnoinvd_on_all_cpus(void)
 {
 	on_each_cpu(__wbnoinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbnoinvd_on_all_cpus);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbnoinvd_on_all_cpus, "kvm-amd,agpgart,ccp,drm,i915");
 
 void wbnoinvd_on_cpus_mask(struct cpumask *cpus)
 {
 	on_each_cpu_mask(cpus, __wbnoinvd, NULL, 1);
 }
-EXPORT_SYMBOL_GPL(wbnoinvd_on_cpus_mask);
+EXPORT_SYMBOL_GPL_FOR_MODULES(wbnoinvd_on_cpus_mask, "kvm-amd,agpgart,ccp,drm,i915");
Signature · 3 lines
Simona Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
#15 lore raw

Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions

In reply to Peter Zijlstra
Quoted 4 lines · Peter Zijlstra

So kvm-amd is the SEV stuff, AGPGART is the ancient crap nobody cares about, CCP is more SEV stuff, DRM actually does CLFLUSH loops, but has a WBINVD fallback. i915 is rude and actually does WBINVD. Could they pretty please also do CLFLUSH loops?

So having looked at i915 a little more:

drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c: * Currently we just do a heavy handed wbinvd_on_all_cpus() here since
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c: wbinvd_on_all_cpus();

This one can be runtime; but is only applied on certain parts that might be less than coherent -- hopefully not new parts, but who knows.

Parts not taking this path end up calling drm_clflush_sg(), which DTRT.

And these all look suspend/resume related, so we can live with them:

drivers/gpu/drm/i915/gem/i915_gem_pm.c:#define wbinvd_on_all_cpus() \
drivers/gpu/drm/i915/gem/i915_gem_pm.c: wbinvd_on_all_cpus();
drivers/gpu/drm/i915/gem/i915_gem_pm.c: wbinvd_on_all_cpus();
drivers/gpu/drm/i915/gt/intel_ggtt.c: wbinvd_on_all_cpus();

merge bot Applied
#16 lore raw

[tip: x86/core] x86/lib: Add WBNOINVD helper functions

In reply to Sean Christopherson
The following commit has been merged into the x86/core branch of tip:

The following commit has been merged into the x86/core branch of tip:

Commit-ID:     07f99c3fbe6e322bdb222fbfd59f708ced799cc5
Gitweb:        https://git.kernel.org/tip/07f99c3fbe6e322bdb222fbfd59f708ced799cc5
Author:        Kevin Loughlin <[email protected]>
AuthorDate:    Thu, 22 May 2025 16:37:27 -07:00
Committer:     Borislav Petkov (AMD) <[email protected]>
CommitterDate: Thu, 10 Jul 2025 13:23:10 +02:00

x86/lib: Add WBNOINVD helper functions

In line with WBINVD usage, add WBNOINVD helper functions. Explicitly fall back to WBINVD (via alternative()) if WBNOINVD isn't supported even though the instruction itself is backwards compatible (WBNOINVD is WBINVD with an ignored REP prefix), so that disabling X86_FEATURE_WBNOINVD behaves as one would expect, e.g. in case there's a hardware issue that affects WBNOINVD.

Opportunistically, add comments explaining the architectural behavior of WBINVD and WBNOINVD, and provide hints and pointers to uarch-specific behavior.

Note, alternative() ensures compatibility with early boot code as needed.

[ bp: Massage, fix typos, make export _GPL. ]

---
 arch/x86/include/asm/smp.h           |  6 ++++++-
 arch/x86/include/asm/special_insns.h | 29 ++++++++++++++++++++++++++-
 arch/x86/lib/cache-smp.c             | 11 ++++++++++-
 3 files changed, 45 insertions(+), 1 deletion(-)
arch/x86/include/asm/smp.h +6 -0
index 028f126..e08f1ae 100644
@@ -113,6 +113,7 @@ void native_play_dead(void);
 void play_dead_common(void);
 void wbinvd_on_cpu(int cpu);
 void wbinvd_on_all_cpus(void);
+void wbnoinvd_on_all_cpus(void);
 
 void smp_kick_mwait_play_dead(void);
 void __noreturn mwait_play_dead(unsigned int eax_hint);
@@ -153,6 +154,11 @@ static inline void wbinvd_on_all_cpus(void)
 	wbinvd();
 }
 
+static inline void wbnoinvd_on_all_cpus(void)
+{
+	wbnoinvd();
+}
+
 static inline struct cpumask *cpu_llc_shared_mask(int cpu)
 {
 	return (struct cpumask *)cpumask_of(0);
arch/x86/include/asm/special_insns.h +28 -1
index ecda17e..fde2bd7 100644
@@ -104,9 +104,36 @@ static inline void wrpkru(u32 pkru)
 }
 #endif
 
+/*
+ * Write back all modified lines in all levels of cache associated with this
+ * logical processor to main memory, and then invalidate all caches.  Depending
+ * on the micro-architecture, WBINVD (and WBNOINVD below) may or may not affect
+ * lower level caches associated with another logical processor that shares any
+ * level of this processor's cache hierarchy.
+ */
 static __always_inline void wbinvd(void)
 {
-	asm volatile("wbinvd": : :"memory");
+	asm volatile("wbinvd" : : : "memory");
+}
+
+/* Instruction encoding provided for binutils backwards compatibility. */
+#define ASM_WBNOINVD _ASM_BYTES(0xf3,0x0f,0x09)
+
+/*
+ * Write back all modified lines in all levels of cache associated with this
+ * logical processor to main memory, but do NOT explicitly invalidate caches,
+ * i.e. leave all/most cache lines in the hierarchy in non-modified state.
+ */
+static __always_inline void wbnoinvd(void)
+{
+	/*
+	 * Explicitly encode WBINVD if X86_FEATURE_WBNOINVD is unavailable even
+	 * though WBNOINVD is backwards compatible (it's simply WBINVD with an
+	 * ignored REP prefix), to guarantee that WBNOINVD isn't used if it
+	 * needs to be avoided for any reason.  For all supported usage in the
+	 * kernel, WBINVD is functionally a superset of WBNOINVD.
+	 */
+	alternative("wbinvd", ASM_WBNOINVD, X86_FEATURE_WBNOINVD);
 }
 
 static inline unsigned long __read_cr4(void)
arch/x86/lib/cache-smp.c +11 -0
index 079c3f3..74e0d5b 100644
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
 	on_each_cpu(__wbinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
+
+static void __wbnoinvd(void *dummy)
+{
+	wbnoinvd();
+}
+
+void wbnoinvd_on_all_cpus(void)
+{
+	on_each_cpu(__wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL_GPL(wbnoinvd_on_all_cpus);
author maintainer
#17 lore raw

PATCH v3 4/8 x86, lib: Add wbinvd and wbnoinvd helpers to target multiple CPUs

In reply to Sean Christopherson

From: Zheyun Shen <[email protected]>

Extract KVM's open-coded calls to do writeback caches on multiple CPUs to common library helpers for both WBINVD and WBNOINVD (KVM will use both). Put the onus on the caller to check for a non-empty mask to simplify the SMP=n implementation, e.g. so that it doesn't need to check that the one and only CPU in the system is present in the mask.

---
 arch/x86/include/asm/smp.h | 12 ++++++++++++
 arch/x86/kvm/x86.c         |  3 +--
 arch/x86/lib/cache-smp.c   | 12 ++++++++++++
 3 files changed, 25 insertions(+), 2 deletions(-)
arch/x86/include/asm/smp.h +12 -0
index e08f1ae25401..22bfebe6776d 100644
@@ -113,7 +113,9 @@ void native_play_dead(void);
 void play_dead_common(void);
 void wbinvd_on_cpu(int cpu);
 void wbinvd_on_all_cpus(void);
+void wbinvd_on_cpus_mask(struct cpumask *cpus);
 void wbnoinvd_on_all_cpus(void);
+void wbnoinvd_on_cpus_mask(struct cpumask *cpus);
 
 void smp_kick_mwait_play_dead(void);
 void __noreturn mwait_play_dead(unsigned int eax_hint);
@@ -154,11 +156,21 @@ static inline void wbinvd_on_all_cpus(void)
 	wbinvd();
 }
 
+static inline void wbinvd_on_cpus_mask(struct cpumask *cpus)
+{
+	wbinvd();
+}
+
 static inline void wbnoinvd_on_all_cpus(void)
 {
 	wbnoinvd();
 }
 
+static inline void wbnoinvd_on_cpus_mask(struct cpumask *cpus)
+{
+	wbnoinvd();
+}
+
 static inline struct cpumask *cpu_llc_shared_mask(int cpu)
 {
 	return (struct cpumask *)cpumask_of(0);
arch/x86/kvm/x86.c +1 -2
index be7bb6d20129..1d0e9180148d 100644
@@ -8245,8 +8245,7 @@ static int kvm_emulate_wbinvd_noskip(struct kvm_vcpu *vcpu)
 		int cpu = get_cpu();
 
 		cpumask_set_cpu(cpu, vcpu->arch.wbinvd_dirty_mask);
-		on_each_cpu_mask(vcpu->arch.wbinvd_dirty_mask,
-				wbinvd_ipi, NULL, 1);
+		wbinvd_on_cpus_mask(vcpu->arch.wbinvd_dirty_mask);
 		put_cpu();
 		cpumask_clear(vcpu->arch.wbinvd_dirty_mask);
 	} else
arch/x86/lib/cache-smp.c +12 -0
index 1789db5d8825..b45e8baa69bb 100644
@@ -20,6 +20,12 @@ void wbinvd_on_all_cpus(void)
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
 
+void wbinvd_on_cpus_mask(struct cpumask *cpus)
+{
+	on_each_cpu_mask(cpus, __wbinvd, NULL, 1);
+}
+EXPORT_SYMBOL_GPL(wbinvd_on_cpus_mask);
+
 static void __wbnoinvd(void *dummy)
 {
 	wbnoinvd();
@@ -30,3 +36,9 @@ void wbnoinvd_on_all_cpus(void)
 	on_each_cpu(__wbnoinvd, NULL, 1);
 }
 EXPORT_SYMBOL(wbnoinvd_on_all_cpus);
+
+void wbnoinvd_on_cpus_mask(struct cpumask *cpus)
+{
+	on_each_cpu_mask(cpus, __wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL_GPL(wbnoinvd_on_cpus_mask);
Signature · 1 line
2.49.0.1151.ga128411c76-goog
merge bot Applied
#18 lore raw

[tip: x86/core] x86/lib: Add WBINVD and WBNOINVD helpers to target multiple CPUs

The following commit has been merged into the x86/core branch of tip:

The following commit has been merged into the x86/core branch of tip:

Commit-ID:     4fdc3431e03b9c11803f399f91837fca487029a1
Gitweb:        https://git.kernel.org/tip/4fdc3431e03b9c11803f399f91837fca487029a1
Author:        Zheyun Shen <[email protected]>
AuthorDate:    Thu, 22 May 2025 16:37:28 -07:00
Committer:     Borislav Petkov (AMD) <[email protected]>
CommitterDate: Thu, 10 Jul 2025 13:30:17 +02:00

x86/lib: Add WBINVD and WBNOINVD helpers to target multiple CPUs

Extract KVM's open-coded calls to do writeback caches on multiple CPUs to common library helpers for both WBINVD and WBNOINVD (KVM will use both). Put the onus on the caller to check for a non-empty mask to simplify the SMP=n implementation, e.g. so that it doesn't need to check that the one and only CPU in the system is present in the mask.

[sean: move to lib, add SMP=n helpers, clarify usage]

---
 arch/x86/include/asm/smp.h | 12 ++++++++++++
 arch/x86/kvm/x86.c         |  3 +--
 arch/x86/lib/cache-smp.c   | 12 ++++++++++++
 3 files changed, 25 insertions(+), 2 deletions(-)
arch/x86/include/asm/smp.h +12 -0
index e08f1ae..22bfebe 100644
@@ -113,7 +113,9 @@ void native_play_dead(void);
 void play_dead_common(void);
 void wbinvd_on_cpu(int cpu);
 void wbinvd_on_all_cpus(void);
+void wbinvd_on_cpus_mask(struct cpumask *cpus);
 void wbnoinvd_on_all_cpus(void);
+void wbnoinvd_on_cpus_mask(struct cpumask *cpus);
 
 void smp_kick_mwait_play_dead(void);
 void __noreturn mwait_play_dead(unsigned int eax_hint);
@@ -154,11 +156,21 @@ static inline void wbinvd_on_all_cpus(void)
 	wbinvd();
 }
 
+static inline void wbinvd_on_cpus_mask(struct cpumask *cpus)
+{
+	wbinvd();
+}
+
 static inline void wbnoinvd_on_all_cpus(void)
 {
 	wbnoinvd();
 }
 
+static inline void wbnoinvd_on_cpus_mask(struct cpumask *cpus)
+{
+	wbnoinvd();
+}
+
 static inline struct cpumask *cpu_llc_shared_mask(int cpu)
 {
 	return (struct cpumask *)cpumask_of(0);
arch/x86/kvm/x86.c +1 -2
index a9d992d..5a2160f 100644
@@ -8289,8 +8289,7 @@ static int kvm_emulate_wbinvd_noskip(struct kvm_vcpu *vcpu)
 		int cpu = get_cpu();
 
 		cpumask_set_cpu(cpu, vcpu->arch.wbinvd_dirty_mask);
-		on_each_cpu_mask(vcpu->arch.wbinvd_dirty_mask,
-				wbinvd_ipi, NULL, 1);
+		wbinvd_on_cpus_mask(vcpu->arch.wbinvd_dirty_mask);
 		put_cpu();
 		cpumask_clear(vcpu->arch.wbinvd_dirty_mask);
 	} else
arch/x86/lib/cache-smp.c +12 -0
index 74e0d5b..c5c60d0 100644
@@ -20,6 +20,12 @@ void wbinvd_on_all_cpus(void)
 }
 EXPORT_SYMBOL(wbinvd_on_all_cpus);
 
+void wbinvd_on_cpus_mask(struct cpumask *cpus)
+{
+	on_each_cpu_mask(cpus, __wbinvd, NULL, 1);
+}
+EXPORT_SYMBOL_GPL(wbinvd_on_cpus_mask);
+
 static void __wbnoinvd(void *dummy)
 {
 	wbnoinvd();
@@ -30,3 +36,9 @@ void wbnoinvd_on_all_cpus(void)
 	on_each_cpu(__wbnoinvd, NULL, 1);
 }
 EXPORT_SYMBOL_GPL(wbnoinvd_on_all_cpus);
+
+void wbnoinvd_on_cpus_mask(struct cpumask *cpus)
+{
+	on_each_cpu_mask(cpus, __wbnoinvd, NULL, 1);
+}
+EXPORT_SYMBOL_GPL(wbnoinvd_on_cpus_mask);
author maintainer
#19 lore raw

PATCH v3 5/8 KVM: x86: Use wbinvd_on_cpu() instead of an open-coded equivalent

In reply to Sean Christopherson

Use wbinvd_on_cpu() to target a single CPU instead of open-coding an equivalent, and drop KVM's wbinvd_ipi() now that all users have switched to x86 library versions.

No functional change intended.

---
 arch/x86/kvm/x86.c | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)
arch/x86/kvm/x86.c +1 -7
index 1d0e9180148d..d63a2c27e058 100644
@@ -4965,11 +4965,6 @@ long kvm_arch_dev_ioctl(struct file *filp,
 	return r;
 }
 
-static void wbinvd_ipi(void *garbage)
-{
-	wbinvd();
-}
-
 static bool need_emulate_wbinvd(struct kvm_vcpu *vcpu)
 {
 	return kvm_arch_has_noncoherent_dma(vcpu->kvm);
@@ -4991,8 +4986,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 		if (kvm_x86_call(has_wbinvd_exit)())
 			cpumask_set_cpu(cpu, vcpu->arch.wbinvd_dirty_mask);
 		else if (vcpu->cpu != -1 && vcpu->cpu != cpu)
-			smp_call_function_single(vcpu->cpu,
-					wbinvd_ipi, NULL, 1);
+			wbinvd_on_cpu(vcpu->cpu);
 	}
 
 	kvm_x86_call(vcpu_load)(vcpu, cpu);
Signature · 1 line
2.49.0.1151.ga128411c76-goog
author maintainer
#20 lore raw

PATCH v3 6/8 KVM: SVM: Remove wbinvd in sev_vm_destroy()

In reply to Sean Christopherson

From: Zheyun Shen <[email protected]>

Before sev_vm_destroy() is called, kvm_arch_guest_memory_reclaimed() has been called for SEV and SEV-ES and kvm_arch_gmem_invalidate() has been called for SEV-SNP. These functions have already handled flushing the memory. Therefore, this wbinvd_on_all_cpus() can simply be dropped.

---
 arch/x86/kvm/svm/sev.c | 6 ------
 1 file changed, 6 deletions(-)
arch/x86/kvm/svm/sev.c +0 -6
index a7a7dc507336..be70c8401c9b 100644
@@ -2879,12 +2879,6 @@ void sev_vm_destroy(struct kvm *kvm)
 		return;
 	}
 
-	/*
-	 * Ensure that all guest tagged cache entries are flushed before
-	 * releasing the pages back to the system for use. CLFLUSH will
-	 * not do this, so issue a WBINVD.
-	 */
-	wbinvd_on_all_cpus();
 
 	/*
 	 * if userspace was terminated before unregistering the memory regions
Signature · 1 line
2.49.0.1151.ga128411c76-goog
author maintainer
#21 lore raw

PATCH v3 7/8 KVM: SEV: Prefer WBNOINVD over WBINVD for cache maintenance efficiency

In reply to Sean Christopherson

From: Kevin Loughlin <[email protected]>

AMD CPUs currently execute WBINVD in the host when unregistering SEV guest memory or when deactivating SEV guests. Such cache maintenance is performed to prevent data corruption, wherein the encrypted (C=1) version of a dirty cache line might otherwise only be written back after the memory is written in a different context (ex: C=0), yielding corruption. However, WBINVD is performance-costly, especially because it invalidates processor caches.

Strictly-speaking, unless the SEV ASID is being recycled (meaning the SNP firmware requires the use of WBINVD prior to DF_FLUSH), the cache invalidation triggered by WBINVD is unnecessary; only the writeback is needed to prevent data corruption in remaining scenarios.

To improve performance in these scenarios, use WBNOINVD when available instead of WBINVD. WBNOINVD still writes back all dirty lines (preventing host data corruption by SEV guests) but does *not* invalidate processor caches. Note that the implementation of wbnoinvd() ensures fall back to WBINVD if WBNOINVD is unavailable.

In anticipation of forthcoming optimizations to limit the WBNOINVD only to physical CPUs that have executed SEV guests, place the call to wbnoinvd_on_all_cpus() in a wrapper function sev_writeback_caches().

---
 arch/x86/kvm/svm/sev.c | 45 ++++++++++++++++++++++++------------------
 1 file changed, 26 insertions(+), 19 deletions(-)
arch/x86/kvm/svm/sev.c +26 -19
index be70c8401c9b..2676be2b121d 100644
@@ -116,6 +116,7 @@ static int sev_flush_asids(unsigned int min_asid, unsigned int max_asid)
 	 */
 	down_write(&sev_deactivate_lock);
 
+	/* SNP firmware requires use of WBINVD for ASID recycling. */
 	wbinvd_on_all_cpus();
 
 	if (sev_snp_enabled)
@@ -705,6 +706,18 @@ static void sev_clflush_pages(struct page *pages[], unsigned long npages)
 	}
 }
 
+static void sev_writeback_caches(void)
+{
+	/*
+	 * Ensure that all dirty guest tagged cache entries are written back
+	 * before releasing the pages back to the system for use.  CLFLUSH will
+	 * not do this without SME_COHERENT, and flushing many cache lines
+	 * individually is slower than blasting WBINVD for large VMs, so issue
+	 * WBNOINVD (or WBINVD if the "no invalidate" variant is unsupported).
+	 */
+	wbnoinvd_on_all_cpus();
+}
+
 static unsigned long get_num_contig_pages(unsigned long idx,
 				struct page **inpages, unsigned long npages)
 {
@@ -2753,12 +2766,7 @@ int sev_mem_enc_unregister_region(struct kvm *kvm,
 		goto failed;
 	}
 
-	/*
-	 * Ensure that all guest tagged cache entries are flushed before
-	 * releasing the pages back to the system for use. CLFLUSH will
-	 * not do this, so issue a WBINVD.
-	 */
-	wbinvd_on_all_cpus();
+	sev_writeback_caches();
 
 	__unregister_enc_region_locked(kvm, region);
 
@@ -3110,30 +3118,29 @@ static void sev_flush_encrypted_page(struct kvm_vcpu *vcpu, void *va)
 
 	/*
 	 * VM Page Flush takes a host virtual address and a guest ASID.  Fall
-	 * back to WBINVD if this faults so as not to make any problems worse
-	 * by leaving stale encrypted data in the cache.
+	 * back to full writeback of caches if this faults so as not to make
+	 * any problems worse by leaving stale encrypted data in the cache.
 	 */
 	if (WARN_ON_ONCE(wrmsrl_safe(MSR_AMD64_VM_PAGE_FLUSH, addr | asid)))
-		goto do_wbinvd;
+		goto do_sev_writeback_caches;
 
 	return;
 
-do_wbinvd:
-	wbinvd_on_all_cpus();
+do_sev_writeback_caches:
+	sev_writeback_caches();
 }
 
 void sev_guest_memory_reclaimed(struct kvm *kvm)
 {
 	/*
 	 * With SNP+gmem, private/encrypted memory is unreachable via the
-	 * hva-based mmu notifiers, so these events are only actually
-	 * pertaining to shared pages where there is no need to perform
-	 * the WBINVD to flush associated caches.
+	 * hva-based mmu notifiers, i.e. these events are explicitly scoped to
+	 * shared pages, where there's no need to flush caches.
 	 */
 	if (!sev_guest(kvm) || sev_snp_guest(kvm))
 		return;
 
-	wbinvd_on_all_cpus();
+	sev_writeback_caches();
 }
 
 void sev_free_vcpu(struct kvm_vcpu *vcpu)
@@ -3897,9 +3904,9 @@ void sev_snp_init_protected_guest_state(struct kvm_vcpu *vcpu)
 	 * From this point forward, the VMSA will always be a guest-mapped page
 	 * rather than the initial one allocated by KVM in svm->sev_es.vmsa. In
 	 * theory, svm->sev_es.vmsa could be free'd and cleaned up here, but
-	 * that involves cleanups like wbinvd_on_all_cpus() which would ideally
-	 * be handled during teardown rather than guest boot.  Deferring that
-	 * also allows the existing logic for SEV-ES VMSAs to be re-used with
+	 * that involves cleanups like flushing caches, which would ideally be
+	 * handled during teardown rather than guest boot.  Deferring that also
+	 * allows the existing logic for SEV-ES VMSAs to be re-used with
 	 * minimal SNP-specific changes.
 	 */
 	svm->sev_es.snp_has_guest_vmsa = true;
@@ -4892,7 +4899,7 @@ void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
 
 		/*
 		 * SEV-ES avoids host/guest cache coherency issues through
-		 * WBINVD hooks issued via MMU notifiers during run-time, and
+		 * WBNOINVD hooks issued via MMU notifiers during run-time, and
 		 * KVM's VM destroy path at shutdown. Those MMU notifier events
 		 * don't cover gmem since there is no requirement to map pages
 		 * to a HVA in order to use them for a running guest. While the
Signature · 1 line
2.49.0.1151.ga128411c76-goog
author maintainer
#22 lore raw

PATCH v3 8/8 KVM: SVM: Flush cache only on CPUs running SEV guest

In reply to Sean Christopherson

From: Zheyun Shen <[email protected]>

On AMD CPUs without ensuring cache consistency, each memory page reclamation in an SEV guest triggers a call to do WBNOINVD/WBINVD on all CPUs, thereby affecting the performance of other programs on the host.

Typically, an AMD server may have 128 cores or more, while the SEV guest might only utilize 8 of these cores. Meanwhile, host can use qemu-affinity to bind these 8 vCPUs to specific physical CPUs.

Therefore, keeping a record of the physical core numbers each time a vCPU runs can help avoid flushing the cache for all CPUs every time.

---
 arch/x86/kvm/svm/sev.c | 46 +++++++++++++++++++++++++++++++++++-------
 arch/x86/kvm/svm/svm.h |  1 +
 2 files changed, 40 insertions(+), 7 deletions(-)
arch/x86/kvm/svm/sev.c +39 -7
index 2676be2b121d..c3ddcca9fdce 100644
@@ -446,7 +446,12 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
 	init_args.probe = false;
 	ret = sev_platform_init(&init_args);
 	if (ret)
-		goto e_free;
+		goto e_free_asid;
+
+	if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
+		ret = -ENOMEM;
+		goto e_free_asid;
+	}
 
 	/* This needs to happen after SEV/SNP firmware initialization. */
 	if (vm_type == KVM_X86_SNP_VM) {
@@ -464,6 +469,8 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
 	return 0;
 
 e_free:
+	free_cpumask_var(sev->have_run_cpus);
+e_free_asid:
 	argp->error = init_args.error;
 	sev_asid_free(sev);
 	sev->asid = 0;
@@ -706,16 +713,31 @@ static void sev_clflush_pages(struct page *pages[], unsigned long npages)
 	}
 }
 
-static void sev_writeback_caches(void)
+static void sev_writeback_caches(struct kvm *kvm)
 {
+	/*
+	 * Note, the caller is responsible for ensuring correctness if the mask
+	 * can be modified, e.g. if a CPU could be doing VMRUN.
+	 */
+	if (cpumask_empty(to_kvm_sev_info(kvm)->have_run_cpus))
+		return;
+
 	/*
 	 * Ensure that all dirty guest tagged cache entries are written back
 	 * before releasing the pages back to the system for use.  CLFLUSH will
 	 * not do this without SME_COHERENT, and flushing many cache lines
 	 * individually is slower than blasting WBINVD for large VMs, so issue
-	 * WBNOINVD (or WBINVD if the "no invalidate" variant is unsupported).
+	 * WBNOINVD (or WBINVD if the "no invalidate" variant is unsupported)
+	 * on CPUs that have done VMRUN, i.e. may have dirtied data using the
+	 * VM's ASID.
+	 *
+	 * For simplicity, never remove CPUs from the bitmap.  Ideally, KVM
+	 * would clear the mask when flushing caches, but doing so requires
+	 * serializing multiple calls and having responding CPUs (to the IPI)
+	 * mark themselves as still running if they are running (or about to
+	 * run) a vCPU for the VM.
 	 */
-	wbnoinvd_on_all_cpus();
+	wbnoinvd_on_cpus_mask(to_kvm_sev_info(kvm)->have_run_cpus);
 }
 
 static unsigned long get_num_contig_pages(unsigned long idx,
@@ -2766,7 +2788,7 @@ int sev_mem_enc_unregister_region(struct kvm *kvm,
 		goto failed;
 	}
 
-	sev_writeback_caches();
+	sev_writeback_caches(kvm);
 
 	__unregister_enc_region_locked(kvm, region);
 
@@ -2914,6 +2936,7 @@ void sev_vm_destroy(struct kvm *kvm)
 	}
 
 	sev_asid_free(sev);
+	free_cpumask_var(sev->have_run_cpus);
 }
 
 void __init sev_set_cpu_caps(void)
@@ -3127,7 +3150,7 @@ static void sev_flush_encrypted_page(struct kvm_vcpu *vcpu, void *va)
 	return;
 
 do_sev_writeback_caches:
-	sev_writeback_caches();
+	sev_writeback_caches(vcpu->kvm);
 }
 
 void sev_guest_memory_reclaimed(struct kvm *kvm)
@@ -3140,7 +3163,7 @@ void sev_guest_memory_reclaimed(struct kvm *kvm)
 	if (!sev_guest(kvm) || sev_snp_guest(kvm))
 		return;
 
-	sev_writeback_caches();
+	sev_writeback_caches(kvm);
 }
 
 void sev_free_vcpu(struct kvm_vcpu *vcpu)
@@ -3472,6 +3495,15 @@ int pre_sev_run(struct vcpu_svm *svm, int cpu)
 	if (sev_es_guest(kvm) && !VALID_PAGE(svm->vmcb->control.vmsa_pa))
 		return -EINVAL;
 
+	/*
+	 * To optimize cache flushes when memory is reclaimed from an SEV VM,
+	 * track physical CPUs that enter the guest for SEV VMs and thus can
+	 * have encrypted, dirty data in the cache, and flush caches only for
+	 * CPUs that have entered the guest.
+	 */
+	if (!cpumask_test_cpu(cpu, to_kvm_sev_info(kvm)->have_run_cpus))
+		cpumask_set_cpu(cpu, to_kvm_sev_info(kvm)->have_run_cpus);
+
 	/* Assign the asid allocated with this SEV guest */
 	svm->asid = asid;
 
arch/x86/kvm/svm/svm.h +1 -0
index f16b068c4228..45d564c674ef 100644
@@ -112,6 +112,7 @@ struct kvm_sev_info {
 	void *guest_req_buf;    /* Bounce buffer for SNP Guest Request input */
 	void *guest_resp_buf;   /* Bounce buffer for SNP Guest Request output */
 	struct mutex guest_req_mutex; /* Must acquire before using bounce buffers */
+	cpumask_var_t have_run_cpus; /* CPUs that have done VMRUN for this VM. */
 };
 
 struct kvm_svm {
Signature · 1 line
2.49.0.1151.ga128411c76-goog
author maintainer
#23 lore raw
In reply to Sean Christopherson
Quoted 8 lines · Sean Christopherson

This is the combination of Kevin's WBNOINVD series[1] with Zheyun's targeted flushing series[2]. The combined goal is to use WBNOINVD instead of WBINVD when doing cached maintenance to prevent data corruption due to C-bit aliasing, and to reduce the number of cache invalidations by only performing flushes on CPUs that have entered the relevant VM since the last cache flush.

All of the non-KVM patches are frontloaded and based on v6.15-rc7, so that they can go through the tip tree (in a stable branch, please :-) ).

Tip tree folks, any feedback/thoughts on this series (patches 1-4 in particular)? It'd be nice to get this into 6.17, and I'd really like land it by 6.18 at the latest.

Quoted 28 lines

Kevin Loughlin (2):
x86, lib: Add WBNOINVD helper functions
KVM: SEV: Prefer WBNOINVD over WBINVD for cache maintenance efficiency

Sean Christopherson (3):
drm/gpu: Remove dead checks on wbinvd_on_all_cpus()'s return value x86, lib: Drop the unused return value from wbinvd_on_all_cpus()
KVM: x86: Use wbinvd_on_cpu() instead of an open-coded equivalent

Zheyun Shen (3):
x86, lib: Add wbinvd and wbnoinvd helpers to target multiple CPUs
KVM: SVM: Remove wbinvd in sev_vm_destroy()
KVM: SVM: Flush cache only on CPUs running SEV guest

 arch/x86/include/asm/smp.h           | 23 +++++++-
 arch/x86/include/asm/special_insns.h | 32 ++++++++++-
 arch/x86/kvm/svm/sev.c               | 85 +++++++++++++++++++---------
 arch/x86/kvm/svm/svm.h               |  1 +
 arch/x86/kvm/x86.c                   | 11 +---
 arch/x86/lib/cache-smp.c             | 26 ++++++++-
 drivers/gpu/drm/drm_cache.c          |  9 +--
 7 files changed, 140 insertions(+), 47 deletions(-)

base-commit: a5806cd506af5a7c19bcd596e4708b5c464bfd21

Signature · 1 line
2.49.0.1151.ga128411c76-goog
maintainer
#24 lore raw
Quoted 7 lines · Sean Christopherson
Quoted 6 lines · Sean Christopherson

This is the combination of Kevin's WBNOINVD series[1] with Zheyun's targeted flushing series[2]. The combined goal is to use WBNOINVD instead of WBINVD when doing cached maintenance to prevent data corruption due to C-bit aliasing, and to reduce the number of cache invalidations by only performing flushes on CPUs that have entered the relevant VM since the last cache flush.

Quoted 6 lines
Quoted 2 lines

All of the non-KVM patches are frontloaded and based on v6.15-rc7, so that they can go through the tip tree (in a stable branch, please :-) ).

Tip tree folks, any feedback/thoughts on this series (patches 1-4 in particular)? It'd be nice to get this into 6.17, and I'd really like land it by 6.18 at the latest.

I'll take a look tomorrow.

If I queue 1-4, would you like an immutable branch to merge for the other 4?

Thx.

Signature · 4 lines
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
author maintainer
#25 lore raw
Quoted 11 lines · Borislav Petkov
Quoted 10 lines · Sean Christopherson
Quoted 8 lines · Sean Christopherson

This is the combination of Kevin's WBNOINVD series[1] with Zheyun's targeted flushing series[2]. The combined goal is to use WBNOINVD instead of WBINVD when doing cached maintenance to prevent data corruption due to C-bit aliasing, and to reduce the number of cache invalidations by only performing flushes on CPUs that have entered the relevant VM since the last cache flush.

All of the non-KVM patches are frontloaded and based on v6.15-rc7, so that they can go through the tip tree (in a stable branch, please :-) ).

Quoted 7 lines
Quoted 3 lines

Tip tree folks, any feedback/thoughts on this series (patches 1-4 in particular)? It'd be nice to get this into 6.17, and I'd really like land it by 6.18 at the latest.

I'll take a look tomorrow.

If I queue 1-4, would you like an immutable branch to merge for the other 4?

Yes please, thanks!

author maintainer
#26 lore raw
In reply to Sean Christopherson
Quoted 10 lines · Sean Christopherson

This is the combination of Kevin's WBNOINVD series[1] with Zheyun's targeted flushing series[2]. The combined goal is to use WBNOINVD instead of WBINVD when doing cached maintenance to prevent data corruption due to C-bit aliasing, and to reduce the number of cache invalidations by only performing flushes on CPUs that have entered the relevant VM since the last cache flush.

All of the non-KVM patches are frontloaded and based on v6.15-rc7, so that they can go through the tip tree (in a stable branch, please :-) ).

[...]

Applied 5-8 to kvm-x86 sev (which is built on tip/x86_core_for_kvm).

[5/8] KVM: x86: Use wbinvd_on_cpu() instead of an open-coded equivalent

      https://github.com/kvm-x86/linux/commit/55aed8c2dbc4
[6/8] KVM: SVM: Remove wbinvd in sev_vm_destroy()
      https://github.com/kvm-x86/linux/commit/7e00013bd339
[7/8] KVM: SEV: Prefer WBNOINVD over WBINVD for cache maintenance efficiency
      https://github.com/kvm-x86/linux/commit/a77896eea33d
[8/8] KVM: SVM: Flush cache only on CPUs running SEV guest
      https://github.com/kvm-x86/linux/commit/d6581b6f2e26

--
https://github.com/kvm-x86/linux/tree/next