Re: [PATCH] x86/mm: Skip global ASID broadcast TLB flush when PCID is disabled

From: Dave Hansen

Date: Wed May 13 2026 - 14:38:12 EST


On 5/13/26 11:11, Tom Lendacky wrote:
> Booting with "nopcid" clears X86_FEATURE_PCID in x86_nopcid_setup(),
> but X86_FEATURE_INVLPGB is left intact. On AMD CPUs that support
> INVLPGB, broadcast TLB flushing remains active even though CR4.PCIDE
> is off.
>
> There are two checks that decide whether the global ASID code runs,
> mm_global_asid() and consider_global_asid(), that key off of the
> X86_FEATURE_INVLPGB feature. Once an mm becomes active on more than
> three CPUs, consider_global_asid() assigns it a global ASID, after which
> flush_tlb_mm_range() takes the broadcast_tlb_flush() path. The
> broadcast_tlb_flush() function calls invlpgb_flush_single_pcid_nosync() or
> invlpgb_flush_user_nr_nosync() with kern_pcid(asid), which expands to
> (asid + 1). Both helpers set INVLPGB_FLAG_PCID and place the non-zero
> PCID into EDX[31:16] of the INVLPGB instruction. Issuing INVLPGB with
> INVLPGB_FLAG_PCID (RAX[1]) and a non-zero PCID (RDX[27:16]) while
> CR4.PCIDE is not set results in a #GP:

Just to be clear, the APM says there's a #GP if: "CR4.PCID =0 and
EDX[PCID] is not zero." I don't think INVLPGB_FLAG_PCID (RAX[1]) is
supposed to play a role.

Although the architecture doesn't care, it's also at least a _little_
funky to be setting INVLPGB_FLAG_PCID here:

u8 flags = INVLPGB_FLAG_PCID | INVLPGB_FLAG_VA;

when PCIDs are disabled.

How about we add a:

static const struct cpuid_dep cpuid_deps[] = {
...
{ X86_FEATURE_INVLPGB, X86_FEATURE_PCID },

and be done with it?

If you don't have PCIDs, you don't get INVLPGB at all. Yes, you could
theoretically use INVLPGB for kernel mappings without PCIDs. But all
actual CPUs that have INVLPGB presumably also have PCID support.

Then we never have to worry about cases where INVLPGB==1, but PCID==0 at
*all*.