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, mingo@kernel.org,
Rik van Riel <riel@surriel.com>
Subject: [PATCH v13 10/14] x86/mm: enable broadcast TLB invalidation for multi-threaded processes
Date: Sun, 23 Feb 2025 14:49:00 -0500 [thread overview]
Message-ID: <20250223194943.3518952-11-riel@surriel.com> (raw)
In-Reply-To: <20250223194943.3518952-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.
Combined with the removal of unnecessary lru_add_drain calls
(see https://lkml.org/lkml/2024/12/19/1388) this results in a
nice performance boost for the will-it-scale tlb_flush2_threads
test on an AMD Milan system with 36 cores:
- vanilla kernel: 527k loops/second
- lru_add_drain removal: 731k loops/second
- only INVLPGB: 527k loops/second
- lru_add_drain + INVLPGB: 1157k loops/second
Profiling with only the INVLPGB changes showed while
TLB invalidation went down from 40% of the total CPU
time to only around 4% of CPU time, the contention
simply moved to the LRU lock.
Fixing both at the same time about doubles the
number of iterations per second from this case.
Comparing will-it-scale tlb_flush2_threads with
several different numbers of threads on a 72 CPU
AMD Milan shows similar results. The number
represents the total number of loops per second
across all the threads:
threads tip invlpgb
1 315k 304k
2 423k 424k
4 644k 1032k
8 652k 1267k
16 737k 1368k
32 759k 1199k
64 636k 1094k
72 609k 993k
1 and 2 thread performance is similar with and
without invlpgb, because invlpgb is only used
on processes using 4 or more CPUs simultaneously.
The number is the median across 5 runs.
Some numbers closer to real world performance
can be found at Phoronix, thanks to Michael:
https://www.phoronix.com/news/AMD-INVLPGB-Linux-Benefits
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-23 19:50 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-23 19:48 [PATCH v13 00/14] AMD broadcast TLB invalidation Rik van Riel
2025-02-23 19:48 ` [PATCH v13 01/14] x86/mm: consolidate full flush threshold decision Rik van Riel
2025-02-24 11:48 ` Borislav Petkov
2025-02-23 19:48 ` [PATCH v13 02/14] x86/mm: get INVLPGB count max from CPUID Rik van Riel
2025-02-24 12:00 ` Borislav Petkov
2025-02-23 19:48 ` [PATCH v13 03/14] x86/mm: add INVLPGB support code Rik van Riel
2025-02-24 12:14 ` Borislav Petkov
2025-02-23 19:48 ` [PATCH v13 04/14] x86/mm: use INVLPGB for kernel TLB flushes Rik van Riel
2025-02-24 12:31 ` Borislav Petkov
2025-02-24 12:44 ` Nadav Amit
2025-02-23 19:48 ` [PATCH v13 05/14] x86/mm: use INVLPGB in flush_tlb_all Rik van Riel
2025-02-24 12:46 ` Borislav Petkov
2025-02-23 19:48 ` [PATCH v13 06/14] x86/mm: use broadcast TLB flushing for page reclaim TLB flushing Rik van Riel
2025-02-24 13:27 ` Borislav Petkov
2025-02-25 19:17 ` Rik van Riel
2025-02-25 20:38 ` Borislav Petkov
2025-02-25 21:03 ` Borislav Petkov
2025-02-25 22:03 ` Rik van Riel
2025-02-26 17:00 ` Tom Lendacky
2025-02-26 17:02 ` Rik van Riel
2025-02-26 17:36 ` Tom Lendacky
2025-02-26 17:46 ` Rik van Riel
2025-02-26 18:12 ` Tom Lendacky
2025-02-26 22:01 ` Rik van Riel
2025-02-28 1:13 ` Rik van Riel
2025-02-28 15:02 ` Tom Lendacky
2025-02-28 15:57 ` Sean Christopherson
2025-02-28 20:42 ` Tom Lendacky
2025-02-23 19:48 ` [PATCH v13 07/14] x86/mm: global ASID allocation helper functions Rik van Riel
2025-02-24 14:16 ` Borislav Petkov
2025-02-25 20:22 ` Rik van Riel
2025-02-23 19:48 ` [PATCH v13 08/14] x86/mm: global ASID context switch & TLB flush handling Rik van Riel
2025-02-23 23:08 ` kernel test robot
2025-02-24 1:26 ` Rik van Riel
2025-02-24 2:01 ` Rik van Riel
2025-02-24 18:41 ` kernel test robot
2025-02-23 19:48 ` [PATCH v13 09/14] x86/mm: global ASID process exit helpers Rik van Riel
2025-02-23 19:49 ` Rik van Riel [this message]
2025-02-23 19:49 ` [PATCH v13 11/14] x86/mm: do targeted broadcast flushing from tlbbatch code Rik van Riel
2025-02-23 19:49 ` [PATCH v13 12/14] x86/mm: enable AMD translation cache extensions Rik van Riel
2025-02-23 19:49 ` [PATCH v13 13/14] x86/mm: only invalidate final translations with INVLPGB Rik van Riel
2025-02-23 19:49 ` [PATCH v13 14/14] x86/mm: add noinvlpgb commandline option Rik van Riel
2025-02-23 21:29 ` Borislav Petkov
2025-02-24 0:34 ` Rik van Riel
2025-02-24 6:29 ` 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=20250223194943.3518952-11-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=mingo@kernel.org \
--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