The x86 INVLPG Instruction: Invalidating a Single TLB Entry Without Flushing the Whole TLB

2026-09-11

When the kernel unmaps a page, changes its permissions, or moves it to a different physical frame, the TLB entries caching the old translation become stale. Reloading CR3 flushes the entire TLB — thousands of entries, all of which have to be re-walked from scratch on the next access. INVLPG is the surgical tool: it invalidates the TLB entry for exactly one virtual page, on the local core, and leaves everything else intact.

The encoding is 0F 01 /7, and the operand is unusual: it's a memory operand, but the CPU doesn't actually read that memory. It uses the address of the operand as the virtual page to invalidate. So INVLPG [rdi] invalidates the TLB entry for the page containing whatever RDI points to. Ring 0 only — a #GP if you try it from user space.

Three things INVLPG does not do:

The global-page exception. Pages marked with the G bit (kernel text, vDSO, etc.) survive a MOV-to-CR3 flush precisely so common kernel mappings stay hot across context switches. But INVLPG does flush a global page's entry — that's the whole point of having a surgical instruction. If the kernel remaps its own text, INVLPG is the only way to make that stick without disabling CR4.PGE.

Real-world example. In Linux, flush_tlb_page() calls __flush_tlb_one_user(), which on x86-64 emits a single INVLPG. If the mapping is shared across CPUs, flush_tlb_mm_range() broadcasts an IPI carrying the address, and each remote core runs INVLPG in the IPI handler. This is why munmap() on a large mapping with many threads scales badly: the shootdown cost is N cores × page count × IPI latency.

Rule of thumb. INVLPG costs ~100–200 cycles locally. A full TLB flush via CR3 costs ~300–500 cycles plus the refill storm afterward (each subsequent miss is a ~1000-cycle page walk). Breakeven is roughly 4 pages — below that, use INVLPG per page; above that, the kernel switches to a full flush. Linux hardcodes this crossover in tlb_single_page_flush_ceiling (default 33 on x86-64, tuned for the shootdown IPI amortization).

Key Takeaway: INVLPG is the CPU's scalpel for TLB invalidation — one page, one core, no PCID awareness — and the kernel batches or upgrades to a full flush once the per-page cost exceeds the refill cost of nuking everything.

All newsletters