From: Rik van Riel <riel@surriel.com>
To: x86@kernel.org
Cc: linux-kernel@vger.kernel.org, bp@alien8.de, peterz@infradead.org,
dave.hansen@linux.intel.com, zhengqi.arch@bytedance.com,
nadav.amit@gmail.com, thomas.lendacky@amd.com,
kernel-team@meta.com, linux-mm@kvack.org,
akpm@linux-foundation.org, jackmanb@google.com, jannh@google.com,
mhklinux@outlook.com, andrew.cooper3@citrix.com,
Manali.Shukla@amd.com, Rik van Riel <riel@surriel.com>
Subject: [PATCH v12 12/16] x86/mm: enable broadcast TLB invalidation for multi-threaded processes
Date: Thu, 20 Feb 2025 19:53:11 -0500 [thread overview]
Message-ID: <20250221005345.2156760-13-riel@surriel.com> (raw)
In-Reply-To: <20250221005345.2156760-1-riel@surriel.com>
Use broadcast TLB invalidation, using the INVPLGB instruction.
There is not enough room in the 12-bit ASID address space to hand
out broadcast ASIDs to every process. Only hand out broadcast ASIDs
to processes when they are observed to be simultaneously running
on 4 or more CPUs.
This also allows single threaded process to continue using the
cheaper, local TLB invalidation instructions like INVLPGB.
Signed-off-by: Rik van Riel <riel@surriel.com>
Reviewed-by: Nadav Amit <nadav.amit@gmail.com>
Tested-by: Manali Shukla <Manali.Shukla@amd.com>
Tested-by: Brendan Jackman <jackmanb@google.com>
Tested-by: Michael Kelley <mhklinux@outlook.com>
---
arch/x86/mm/tlb.c | 107 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 106 insertions(+), 1 deletion(-)
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index d8a04e398615..01a5edb51ebe 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -420,6 +420,108 @@ static bool needs_global_asid_reload(struct mm_struct *next, u16 prev_asid)
return false;
}
+/*
+ * x86 has 4k ASIDs (2k when compiled with KPTI), but the largest
+ * x86 systems have over 8k CPUs. Because of this potential ASID
+ * shortage, global ASIDs are handed out to processes that have
+ * frequent TLB flushes and are active on 4 or more CPUs simultaneously.
+ */
+static void consider_global_asid(struct mm_struct *mm)
+{
+ if (!static_cpu_has(X86_FEATURE_INVLPGB))
+ return;
+
+ /* Check every once in a while. */
+ if ((current->pid & 0x1f) != (jiffies & 0x1f))
+ return;
+
+ if (!READ_ONCE(global_asid_available))
+ return;
+
+ /*
+ * Assign a global ASID if the process is active on
+ * 4 or more CPUs simultaneously.
+ */
+ if (mm_active_cpus_exceeds(mm, 3))
+ use_global_asid(mm);
+}
+
+static void finish_asid_transition(struct flush_tlb_info *info)
+{
+ struct mm_struct *mm = info->mm;
+ int bc_asid = mm_global_asid(mm);
+ int cpu;
+
+ if (!READ_ONCE(mm->context.asid_transition))
+ return;
+
+ for_each_cpu(cpu, mm_cpumask(mm)) {
+ /*
+ * The remote CPU is context switching. Wait for that to
+ * finish, to catch the unlikely case of it switching to
+ * the target mm with an out of date ASID.
+ */
+ while (READ_ONCE(per_cpu(cpu_tlbstate.loaded_mm, cpu)) == LOADED_MM_SWITCHING)
+ cpu_relax();
+
+ if (READ_ONCE(per_cpu(cpu_tlbstate.loaded_mm, cpu)) != mm)
+ continue;
+
+ /*
+ * If at least one CPU is not using the global ASID yet,
+ * send a TLB flush IPI. The IPI should cause stragglers
+ * to transition soon.
+ *
+ * This can race with the CPU switching to another task;
+ * that results in a (harmless) extra IPI.
+ */
+ if (READ_ONCE(per_cpu(cpu_tlbstate.loaded_mm_asid, cpu)) != bc_asid) {
+ flush_tlb_multi(mm_cpumask(info->mm), info);
+ return;
+ }
+ }
+
+ /* All the CPUs running this process are using the global ASID. */
+ WRITE_ONCE(mm->context.asid_transition, false);
+}
+
+static void broadcast_tlb_flush(struct flush_tlb_info *info)
+{
+ bool pmd = info->stride_shift == PMD_SHIFT;
+ unsigned long asid = info->mm->context.global_asid;
+ unsigned long addr = info->start;
+
+ /*
+ * TLB flushes with INVLPGB are kicked off asynchronously.
+ * The inc_mm_tlb_gen() guarantees page table updates are done
+ * before these TLB flushes happen.
+ */
+ if (info->end == TLB_FLUSH_ALL) {
+ invlpgb_flush_single_pcid_nosync(kern_pcid(asid));
+ /* Do any CPUs supporting INVLPGB need PTI? */
+ if (static_cpu_has(X86_FEATURE_PTI))
+ invlpgb_flush_single_pcid_nosync(user_pcid(asid));
+ } else do {
+ unsigned long nr = 1;
+
+ if (info->stride_shift <= PMD_SHIFT) {
+ nr = (info->end - addr) >> info->stride_shift;
+ nr = clamp_val(nr, 1, invlpgb_count_max);
+ }
+
+ invlpgb_flush_user_nr_nosync(kern_pcid(asid), addr, nr, pmd);
+ if (static_cpu_has(X86_FEATURE_PTI))
+ invlpgb_flush_user_nr_nosync(user_pcid(asid), addr, nr, pmd);
+
+ addr += nr << info->stride_shift;
+ } while (addr < info->end);
+
+ finish_asid_transition(info);
+
+ /* Wait for the INVLPGBs kicked off above to finish. */
+ __tlbsync();
+}
+
/*
* Given an ASID, flush the corresponding user ASID. We can delay this
* until the next time we switch to it.
@@ -1250,9 +1352,12 @@ void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
* a local TLB flush is needed. Optimize this use-case by calling
* flush_tlb_func_local() directly in this case.
*/
- if (cpumask_any_but(mm_cpumask(mm), cpu) < nr_cpu_ids) {
+ if (mm_global_asid(mm)) {
+ broadcast_tlb_flush(info);
+ } else if (cpumask_any_but(mm_cpumask(mm), cpu) < nr_cpu_ids) {
info->trim_cpumask = should_trim_cpumask(mm);
flush_tlb_multi(mm_cpumask(mm), info);
+ consider_global_asid(mm);
} else if (mm == this_cpu_read(cpu_tlbstate.loaded_mm)) {
lockdep_assert_irqs_enabled();
local_irq_disable();
--
2.47.1
next prev parent reply other threads:[~2025-02-21 0:55 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-21 0:52 [PATCH v12 00/16] AMD broadcast TLB invalidation Rik van Riel
2025-02-21 0:53 ` [PATCH v12 01/16] x86/mm: make MMU_GATHER_RCU_TABLE_FREE unconditional Rik van Riel
2025-02-21 0:53 ` [PATCH v12 02/16] x86/mm: remove pv_ops.mmu.tlb_remove_table call Rik van Riel
2025-02-21 0:53 ` [PATCH v12 03/16] x86/mm: consolidate full flush threshold decision Rik van Riel
2025-02-21 0:53 ` [PATCH v12 04/16] x86/mm: get INVLPGB count max from CPUID Rik van Riel
2025-02-21 1:58 ` Borislav Petkov
2025-02-21 2:25 ` Rik van Riel
2025-02-21 18:03 ` [PATCH v12.1 " Rik van Riel
2025-02-21 18:41 ` Tom Lendacky
2025-02-22 3:35 ` [PATCH v12.2 " Rik van Riel
2025-02-21 0:53 ` [PATCH v12 05/16] x86/mm: add INVLPGB support code Rik van Riel
2025-02-21 0:53 ` [PATCH v12 06/16] x86/mm: use INVLPGB for kernel TLB flushes Rik van Riel
2025-02-21 0:53 ` [PATCH v12 07/16] x86/mm: use INVLPGB in flush_tlb_all Rik van Riel
2025-02-21 0:53 ` [PATCH v12 08/16] x86/mm: use broadcast TLB flushing for page reclaim TLB flushing Rik van Riel
2025-02-21 0:53 ` [PATCH v12 09/16] x86/mm: global ASID allocation helper functions Rik van Riel
2025-02-21 0:53 ` [PATCH v12 10/16] x86/mm: global ASID context switch & TLB flush handling Rik van Riel
2025-02-21 0:53 ` [PATCH v12 11/16] x86/mm: global ASID process exit helpers Rik van Riel
2025-02-21 0:53 ` Rik van Riel [this message]
2025-02-21 0:53 ` [PATCH v12 13/16] x86/mm: do targeted broadcast flushing from tlbbatch code Rik van Riel
2025-02-21 0:53 ` [PATCH v12 14/16] x86/mm: enable AMD translation cache extensions Rik van Riel
2025-02-21 0:53 ` [PATCH v12 15/16] x86/mm: only invalidate final translations with INVLPGB Rik van Riel
2025-02-21 0:53 ` [PATCH v12 16/16] x86/mm: add noinvlpgb commandline option Rik van Riel
2025-02-22 11:29 ` [PATCH v12 00/16] AMD broadcast TLB invalidation Oleksandr Natalenko
2025-02-22 11:36 ` Oleksandr Natalenko
2025-02-22 16:05 ` Rik van Riel
2025-02-22 16:19 ` Borislav Petkov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250221005345.2156760-13-riel@surriel.com \
--to=riel@surriel.com \
--cc=Manali.Shukla@amd.com \
--cc=akpm@linux-foundation.org \
--cc=andrew.cooper3@citrix.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=jackmanb@google.com \
--cc=jannh@google.com \
--cc=kernel-team@meta.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mhklinux@outlook.com \
--cc=nadav.amit@gmail.com \
--cc=peterz@infradead.org \
--cc=thomas.lendacky@amd.com \
--cc=x86@kernel.org \
--cc=zhengqi.arch@bytedance.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox