Skip to content

Commit 2ea907c

Browse files
hansendcIngo Molnar
authored andcommitted
x86/mm: Allow flushing for future ASID switches
If changing the page tables in such a way that an invalidation of all contexts (aka. PCIDs / ASIDs) is required, they can be actively invalidated by: 1. INVPCID for each PCID (works for single pages too). 2. Load CR3 with each PCID without the NOFLUSH bit set 3. Load CR3 with the NOFLUSH bit set for each and do INVLPG for each address. But, none of these are really feasible since there are ~6 ASIDs (12 with PAGE_TABLE_ISOLATION) at the time that invalidation is required. Instead of actively invalidating them, invalidate the *current* context and also mark the cpu_tlbstate _quickly_ to indicate future invalidation to be required. At the next context-switch, look for this indicator ('invalidate_other' being set) invalidate all of the cpu_tlbstate.ctxs[] entries. This ensures that any future context switches will do a full flush of the TLB, picking up the previous changes. [ tglx: Folded more fixups from Peter ] Signed-off-by: Dave Hansen <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Boris Ostrovsky <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Brian Gerst <[email protected]> Cc: David Laight <[email protected]> Cc: Denys Vlasenko <[email protected]> Cc: Eduardo Valentin <[email protected]> Cc: Greg KH <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Juergen Gross <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Will Deacon <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 85900ea commit 2ea907c

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

arch/x86/include/asm/tlbflush.h

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ struct tlb_state {
134134
*/
135135
bool is_lazy;
136136

137+
/*
138+
* If set we changed the page tables in such a way that we
139+
* needed an invalidation of all contexts (aka. PCIDs / ASIDs).
140+
* This tells us to go invalidate all the non-loaded ctxs[]
141+
* on the next context switch.
142+
*
143+
* The current ctx was kept up-to-date as it ran and does not
144+
* need to be invalidated.
145+
*/
146+
bool invalidate_other;
147+
137148
/*
138149
* Access to this CR4 shadow and to H/W CR4 is protected by
139150
* disabling interrupts when modifying either one.
@@ -211,6 +222,14 @@ static inline unsigned long cr4_read_shadow(void)
211222
return this_cpu_read(cpu_tlbstate.cr4);
212223
}
213224

225+
/*
226+
* Mark all other ASIDs as invalid, preserves the current.
227+
*/
228+
static inline void invalidate_other_asid(void)
229+
{
230+
this_cpu_write(cpu_tlbstate.invalidate_other, true);
231+
}
232+
214233
/*
215234
* Save some of cr4 feature set we're using (e.g. Pentium 4MB
216235
* enable and PPro Global page enable), so that any CPU's that boot
@@ -298,14 +317,6 @@ static inline void __flush_tlb_all(void)
298317
*/
299318
__flush_tlb();
300319
}
301-
302-
/*
303-
* Note: if we somehow had PCID but not PGE, then this wouldn't work --
304-
* we'd end up flushing kernel translations for the current ASID but
305-
* we might fail to flush kernel translations for other cached ASIDs.
306-
*
307-
* To avoid this issue, we force PCID off if PGE is off.
308-
*/
309320
}
310321

311322
/*
@@ -315,6 +326,16 @@ static inline void __flush_tlb_one(unsigned long addr)
315326
{
316327
count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ONE);
317328
__flush_tlb_single(addr);
329+
330+
if (!static_cpu_has(X86_FEATURE_PTI))
331+
return;
332+
333+
/*
334+
* __flush_tlb_single() will have cleared the TLB entry for this ASID,
335+
* but since kernel space is replicated across all, we must also
336+
* invalidate all others.
337+
*/
338+
invalidate_other_asid();
318339
}
319340

320341
#define TLB_FLUSH_ALL -1UL

arch/x86/mm/tlb.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,38 @@
2828
* Implement flush IPI by CALL_FUNCTION_VECTOR, Alex Shi
2929
*/
3030

31+
/*
32+
* We get here when we do something requiring a TLB invalidation
33+
* but could not go invalidate all of the contexts. We do the
34+
* necessary invalidation by clearing out the 'ctx_id' which
35+
* forces a TLB flush when the context is loaded.
36+
*/
37+
void clear_asid_other(void)
38+
{
39+
u16 asid;
40+
41+
/*
42+
* This is only expected to be set if we have disabled
43+
* kernel _PAGE_GLOBAL pages.
44+
*/
45+
if (!static_cpu_has(X86_FEATURE_PTI)) {
46+
WARN_ON_ONCE(1);
47+
return;
48+
}
49+
50+
for (asid = 0; asid < TLB_NR_DYN_ASIDS; asid++) {
51+
/* Do not need to flush the current asid */
52+
if (asid == this_cpu_read(cpu_tlbstate.loaded_mm_asid))
53+
continue;
54+
/*
55+
* Make sure the next time we go to switch to
56+
* this asid, we do a flush:
57+
*/
58+
this_cpu_write(cpu_tlbstate.ctxs[asid].ctx_id, 0);
59+
}
60+
this_cpu_write(cpu_tlbstate.invalidate_other, false);
61+
}
62+
3163
atomic64_t last_mm_ctx_id = ATOMIC64_INIT(1);
3264

3365

@@ -42,6 +74,9 @@ static void choose_new_asid(struct mm_struct *next, u64 next_tlb_gen,
4274
return;
4375
}
4476

77+
if (this_cpu_read(cpu_tlbstate.invalidate_other))
78+
clear_asid_other();
79+
4580
for (asid = 0; asid < TLB_NR_DYN_ASIDS; asid++) {
4681
if (this_cpu_read(cpu_tlbstate.ctxs[asid].ctx_id) !=
4782
next->context.ctx_id)

0 commit comments

Comments
 (0)