x86, KVM: Optimize SEV cache flushing
Subsystems (5)
Sean Christopherson
maintainerv3 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
Sean Christopherson
maintainerPATCH 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
@@ -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
[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
@@ -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
Re: [PATCH v3 1/8] drm/gpu: Remove dead checks on wbinvd_on_all_cpus()'s return value
In reply to Sean ChristophersonQuoted 6 lines · Sean Christopherson
Remove the checks and associated
pr_err()onwbinvd_on_all_cpus()failure, as the helper has unconditionally returned 0/success since commitcaa759323c73("smp: Removesmp_call_function()andon_each_cpu()return values").Signed-off-by Sean Christopherson <[email protected]>
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@@ -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
Sean Christopherson
maintainerPATCH v3 2/8 x86, lib: Drop the unused return value from wbinvd_on_all_cpus()
In reply to Sean ChristophersonDrop 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
@@ -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
@@ -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
[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
@@ -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
@@ -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);
Sean Christopherson
maintainerPATCH v3 3/8 x86, lib: Add WBNOINVD helper functions
In reply to Sean ChristophersonFrom: 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
@@ -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
@@ -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) { - asmvolatile ("wbinvd" : : :"memory" ); + asmvolatile ("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
@@ -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
Borislav Petkov
maintainerRe: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions
Quoted 9 lines · Sean Christopherson
arch/x86/lib/cache-smp.c +1
-0@@ -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-netiquetteSean Christopherson
maintainerRe: [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@@ -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.
Borislav Petkov
maintainerRe: [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-netiquetteRe: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions
In reply to Borislav PetkovQuoted 16 lines · Borislav Petkov
Quoted 15 lines · Sean Christopherson
arch/x86/lib/cache-smp.c +7
-0@@ -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.
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@@ -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
@@ -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" );
Sean Christopherson
maintainerRe: [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@@ -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@@ -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
@@ -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
-4void 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" );
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@@ -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.
Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions
In reply to Peter ZijlstraQuoted 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@@ -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@@ -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
Re: [PATCH v3 3/8] x86, lib: Add WBNOINVD helper functions
In reply to Peter ZijlstraQuoted 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 sincedrivers/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();
[tip: x86/core] x86/lib: Add WBNOINVD helper functions
In reply to Sean ChristophersonThe 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
@@ -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
@@ -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) { - asmvolatile ("wbinvd" : : :"memory" ); + asmvolatile ("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
@@ -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);
Sean Christopherson
maintainerPATCH v3 4/8 x86, lib: Add wbinvd and wbnoinvd helpers to target multiple CPUs
In reply to Sean ChristophersonFrom: 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
@@ -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
@@ -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
@@ -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
[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
@@ -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
@@ -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
@@ -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);
Sean Christopherson
maintainerPATCH v3 5/8 KVM: x86: Use wbinvd_on_cpu() instead of an open-coded equivalent
In reply to Sean ChristophersonUse 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
@@ -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
Sean Christopherson
maintainerPATCH v3 6/8 KVM: SVM: Remove wbinvd in sev_vm_destroy()
In reply to Sean ChristophersonFrom: 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
@@ -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
Sean Christopherson
maintainerPATCH v3 7/8 KVM: SEV: Prefer WBNOINVD over WBINVD for cache maintenance efficiency
In reply to Sean ChristophersonFrom: 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
@@ -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
Sean Christopherson
maintainerPATCH v3 8/8 KVM: SVM: Flush cache only on CPUs running SEV guest
In reply to Sean ChristophersonFrom: 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
@@ -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
@@ -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
Sean Christopherson
maintainerQuoted 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 efficiencySean Christopherson (3):
drm/gpu: Remove dead checks on wbinvd_on_all_cpus()'s return value x86, lib: Drop the unused return value fromwbinvd_on_all_cpus()
KVM: x86: Usewbinvd_on_cpu()instead of an open-coded equivalentZheyun Shen (3):
x86, lib: Add wbinvd and wbnoinvd helpers to target multiple CPUs
KVM: SVM: Remove wbinvd insev_vm_destroy()
KVM: SVM: Flush cache only on CPUs running SEV guestarch/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:
a5806cd506af5a7c19bcd596e4708b5c464bfd21Signature · 1 line
2.49.0.1151.ga128411c76-goog
Borislav Petkov
maintainerQuoted 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-netiquetteSean Christopherson
maintainerQuoted 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!
Sean Christopherson
maintainerQuoted 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