Skip to content

Commit 07f99c3

Browse files
kevinloughlinbp3tk0v
authored andcommitted
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. ] Signed-off-by: Kevin Loughlin <[email protected]> Co-developed-by: Sean Christopherson <[email protected]> Signed-off-by: Sean Christopherson <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Tom Lendacky <[email protected]> Reviewed-by: Kai Huang <[email protected]> Acked-by: Ingo Molnar <[email protected]> Link: https://lore.kernel.org/[email protected]
1 parent e638081 commit 07f99c3

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

arch/x86/include/asm/smp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ void native_play_dead(void);
113113
void play_dead_common(void);
114114
void wbinvd_on_cpu(int cpu);
115115
void wbinvd_on_all_cpus(void);
116+
void wbnoinvd_on_all_cpus(void);
116117

117118
void smp_kick_mwait_play_dead(void);
118119
void __noreturn mwait_play_dead(unsigned int eax_hint);
@@ -153,6 +154,11 @@ static inline void wbinvd_on_all_cpus(void)
153154
wbinvd();
154155
}
155156

157+
static inline void wbnoinvd_on_all_cpus(void)
158+
{
159+
wbnoinvd();
160+
}
161+
156162
static inline struct cpumask *cpu_llc_shared_mask(int cpu)
157163
{
158164
return (struct cpumask *)cpumask_of(0);

arch/x86/include/asm/special_insns.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,36 @@ static inline void wrpkru(u32 pkru)
104104
}
105105
#endif
106106

107+
/*
108+
* Write back all modified lines in all levels of cache associated with this
109+
* logical processor to main memory, and then invalidate all caches. Depending
110+
* on the micro-architecture, WBINVD (and WBNOINVD below) may or may not affect
111+
* lower level caches associated with another logical processor that shares any
112+
* level of this processor's cache hierarchy.
113+
*/
107114
static __always_inline void wbinvd(void)
108115
{
109-
asm volatile("wbinvd": : :"memory");
116+
asm volatile("wbinvd" : : : "memory");
117+
}
118+
119+
/* Instruction encoding provided for binutils backwards compatibility. */
120+
#define ASM_WBNOINVD _ASM_BYTES(0xf3,0x0f,0x09)
121+
122+
/*
123+
* Write back all modified lines in all levels of cache associated with this
124+
* logical processor to main memory, but do NOT explicitly invalidate caches,
125+
* i.e. leave all/most cache lines in the hierarchy in non-modified state.
126+
*/
127+
static __always_inline void wbnoinvd(void)
128+
{
129+
/*
130+
* Explicitly encode WBINVD if X86_FEATURE_WBNOINVD is unavailable even
131+
* though WBNOINVD is backwards compatible (it's simply WBINVD with an
132+
* ignored REP prefix), to guarantee that WBNOINVD isn't used if it
133+
* needs to be avoided for any reason. For all supported usage in the
134+
* kernel, WBINVD is functionally a superset of WBNOINVD.
135+
*/
136+
alternative("wbinvd", ASM_WBNOINVD, X86_FEATURE_WBNOINVD);
110137
}
111138

112139
static inline unsigned long __read_cr4(void)

arch/x86/lib/cache-smp.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,14 @@ void wbinvd_on_all_cpus(void)
1919
on_each_cpu(__wbinvd, NULL, 1);
2020
}
2121
EXPORT_SYMBOL(wbinvd_on_all_cpus);
22+
23+
static void __wbnoinvd(void *dummy)
24+
{
25+
wbnoinvd();
26+
}
27+
28+
void wbnoinvd_on_all_cpus(void)
29+
{
30+
on_each_cpu(__wbnoinvd, NULL, 1);
31+
}
32+
EXPORT_SYMBOL_GPL(wbnoinvd_on_all_cpus);

0 commit comments

Comments
 (0)