linux/arch/x86/mm
Dave Hansen d87392094f x86/mm: Eliminate window where TLB flushes may be inadvertently skipped
commit fea4e317f9 upstream.

tl;dr: There is a window in the mm switching code where the new CR3 is
set and the CPU should be getting TLB flushes for the new mm.  But
should_flush_tlb() has a bug and suppresses the flush.  Fix it by
widening the window where should_flush_tlb() sends an IPI.

Long Version:

=== History ===

There were a few things leading up to this.

First, updating mm_cpumask() was observed to be too expensive, so it was
made lazier.  But being lazy caused too many unnecessary IPIs to CPUs
due to the now-lazy mm_cpumask().  So code was added to cull
mm_cpumask() periodically[2].  But that culling was a bit too aggressive
and skipped sending TLB flushes to CPUs that need them.  So here we are
again.

=== Problem ===

The too-aggressive code in should_flush_tlb() strikes in this window:

	// Turn on IPIs for this CPU/mm combination, but only
	// if should_flush_tlb() agrees:
	cpumask_set_cpu(cpu, mm_cpumask(next));

	next_tlb_gen = atomic64_read(&next->context.tlb_gen);
	choose_new_asid(next, next_tlb_gen, &new_asid, &need_flush);
	load_new_mm_cr3(need_flush);
	// ^ After 'need_flush' is set to false, IPIs *MUST*
	// be sent to this CPU and not be ignored.

        this_cpu_write(cpu_tlbstate.loaded_mm, next);
	// ^ Not until this point does should_flush_tlb()
	// become true!

should_flush_tlb() will suppress TLB flushes between load_new_mm_cr3()
and writing to 'loaded_mm', which is a window where they should not be
suppressed.  Whoops.

=== Solution ===

Thankfully, the fuzzy "just about to write CR3" window is already marked
with loaded_mm==LOADED_MM_SWITCHING.  Simply checking for that state in
should_flush_tlb() is sufficient to ensure that the CPU is targeted with
an IPI.

This will cause more TLB flush IPIs.  But the window is relatively small
and I do not expect this to cause any kind of measurable performance
impact.

Update the comment where LOADED_MM_SWITCHING is written since it grew
yet another user.

Peter Z also raised a concern that should_flush_tlb() might not observe
'loaded_mm' and 'is_lazy' in the same order that switch_mm_irqs_off()
writes them.  Add a barrier to ensure that they are observed in the
order they are written.

Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Rik van Riel <riel@surriel.com>
Link: https://lore.kernel.org/oe-lkp/202411282207.6bd28eae-lkp@intel.com/ [1]
Fixes: 6db2526c1d ("x86/mm/tlb: Only trim the mm_cpumask once a second") [2]
Reported-by: Stephen Dolan <sdolan@janestreet.com>
Cc: stable@vger.kernel.org
Acked-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
History 2025-05-18 08:24:51 +02:00
..
pat x86/mm: Clear _PAGE_DIRTY for kernel mappings when we clear _PAGE_RW 2025-04-20 10:15:11 +02:00
amdtopology.c mm: introduce numa_memblks 2024-09-03 21:15:30 -07:00
cpu_entry_area.c x86/cpu_entry_area: Annotate percpu_setup_exception_stacks() as __init 2024-08-25 14:29:38 +02:00
debug_pagetables.c x86/bugs: Rename CONFIG_PAGE_TABLE_ISOLATION => CONFIG_MITIGATION_PAGE_TABLE_ISOLATION 2024-01-10 10:52:28 +01:00
dump_pagetables.c - Sumanth Korikkar has taught s390 to allocate hotplug-time page frames 2024-03-14 17:43:30 -07:00
extable.c x86/extable: Remove unused fixup type EX_TYPE_COPY 2024-04-04 17:01:40 +02:00
fault.c x86/mm: Convert unreachable() to BUG() 2025-02-17 10:05:47 +01:00
highmem_32.c x86/mm: Include asm/numa.h for set_highmem_pages_init() 2023-05-18 11:56:18 -07:00
hugetlbpage.c treewide: use initializer for struct vm_unmapped_area_info 2024-04-25 20:56:27 -07:00
ident_map.c x86/mm: Add _PAGE_NOPTISHADOW bit to avoid updating userspace page tables 2024-12-14 20:03:33 +01:00
init.c x86/mm: Don't disable PCID when INVLPG has been fixed by microcode 2025-03-13 13:02:20 +01:00
init_32.c mm/treewide: replace pmd_large() with pmd_leaf() 2024-03-06 13:04:19 -08:00
init_64.c x86/kaslr: Expose and use the end of the physical memory address space 2024-08-20 13:44:57 +02:00
iomap_32.c io-mapping: Cleanup atomic iomap 2020-11-06 23:14:58 +01:00
ioremap.c x86/mm: Fix a kdump kernel failure on SME system when CONFIG_IMA_KEXEC=y 2024-11-13 14:11:33 +01:00
kasan_init_64.c mm/treewide: replace pud_large() with pud_leaf() 2024-03-06 13:04:19 -08:00
kaslr.c x86/kaslr: Expose and use the end of the physical memory address space 2024-08-20 13:44:57 +02:00
kmmio.c x86/mm/kmmio: Remove redundant preempt_disable() 2022-12-12 10:54:48 -05:00
kmsan_shadow.c x86: kmsan: handle CPU entry area 2022-10-03 14:03:26 -07:00
maccess.c x86/mm: Disallow vsyscall page read for copy_from_kernel_nofault() 2024-02-15 19:21:39 -08:00
Makefile mm: introduce numa_emulation 2024-09-03 21:15:31 -07:00
mem_encrypt.c x86/sev: Add callback to apply RMP table fixups for kexec 2024-04-29 11:21:09 +02:00
mem_encrypt_amd.c - Add support for running the kernel in a SEV-SNP guest, over a Secure 2024-07-16 11:12:25 -07:00
mem_encrypt_boot.S x86/mm: Remove P*D_PAGE_MASK and P*D_PAGE_SIZE macros 2022-12-15 10:37:27 -08:00
mem_encrypt_identity.c x86/sev: Add missing RIP_REL_REF() invocations during sme_enable() 2025-04-10 14:39:11 +02:00
mm_internal.h x86/mm: thread pgprot_t through init_memory_mapping() 2020-04-10 15:36:21 -07:00
mmap.c mm: switch mm->get_unmapped_area() to a flag 2024-04-25 20:56:25 -07:00
mmio-mod.c x86: Replace cpumask_weight() with cpumask_empty() where appropriate 2022-04-10 22:35:38 +02:00
numa.c mm: make range-to-target_node lookup facility a part of numa_memblks 2024-09-03 21:15:32 -07:00
numa_32.c fix missing vmalloc.h includes 2024-04-25 20:55:49 -07:00
numa_64.c mm: remove include/linux/bootmem.h 2018-10-31 08:54:16 -07:00
numa_internal.h mm: introduce numa_emulation 2024-09-03 21:15:31 -07:00
pf_in.c treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 156 2019-05-30 11:26:35 -07:00
pf_in.h treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 156 2019-05-30 11:26:35 -07:00
pgprot.c x86/mm: move protection_map[] inside the platform 2022-07-17 17:14:38 -07:00
pgtable.c mm/x86: add missing pud helpers 2024-09-01 20:26:09 -07:00
pgtable_32.c mm: remove unneeded includes of <asm/pgalloc.h> 2020-08-07 11:33:26 -07:00
physaddr.c mm, x86/mm: Untangle address space layout definitions from basic pgtable type definitions 2019-12-10 10:12:55 +01:00
physaddr.h License cleanup: add SPDX GPL-2.0 license identifier to files with no license 2017-11-02 11:10:55 +01:00
pkeys.c x86/pkeys: Clarify PKRU_AD_KEY macro 2022-06-07 16:06:33 -07:00
pti.c x86/mm: Add _PAGE_NOPTISHADOW bit to avoid updating userspace page tables 2024-12-14 20:03:33 +01:00
srat.c x86/mm: Don't print out SRAT table information 2024-08-08 17:23:40 +02:00
testmmiotrace.c x86/mm: add testmmiotrace MODULE_DESCRIPTION() 2024-09-01 20:43:31 -07:00
tlb.c x86/mm: Eliminate window where TLB flushes may be inadvertently skipped 2025-05-18 08:24:51 +02:00