From: Borislav Petkov <bp@kernel.org>
To: riel@surriel.com
Cc: Manali.Shukla@amd.com, akpm@linux-foundation.org,
andrew.cooper3@citrix.com, jackmanb@google.com, jannh@google.com,
kernel-team@meta.com, linux-kernel@vger.kernel.org,
linux-mm@kvack.org, mhklinux@outlook.com, nadav.amit@gmail.com,
thomas.lendacky@amd.com, x86@kernel.org,
zhengqi.arch@bytedance.com, Borislav Petkov <bp@alien8.de>
Subject: [PATCH v15 10/11] x86/mm: Do targeted broadcast flushing from tlbbatch code
Date: Tue, 4 Mar 2025 14:58:15 +0100 [thread overview]
Message-ID: <20250304135816.12356-11-bp@kernel.org> (raw)
In-Reply-To: <20250304135816.12356-1-bp@kernel.org>
From: Rik van Riel <riel@surriel.com>
Instead of doing a system-wide TLB flush from arch_tlbbatch_flush(), queue up
asynchronous, targeted flushes from arch_tlbbatch_add_pending().
This also allows to avoid adding the CPUs of processes using broadcast
flushing to the batch->cpumask, and will hopefully further reduce TLB flushing
from the reclaim and compaction paths.
[ bp:
- Massage
- :%s/\<static_cpu_has\>/cpu_feature_enabled/cgi
- merge in improvements from dhansen ]
Signed-off-by: Rik van Riel <riel@surriel.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20250226030129.530345-12-riel@surriel.com
---
arch/x86/include/asm/tlb.h | 10 ++--
arch/x86/include/asm/tlbflush.h | 27 ++++++----
arch/x86/mm/tlb.c | 88 +++++++++++++++++++++++++++++++--
3 files changed, 108 insertions(+), 17 deletions(-)
diff --git a/arch/x86/include/asm/tlb.h b/arch/x86/include/asm/tlb.h
index 8ffcae7beb55..e8561a846754 100644
--- a/arch/x86/include/asm/tlb.h
+++ b/arch/x86/include/asm/tlb.h
@@ -108,9 +108,9 @@ static inline void __tlbsync(void) { }
/* The implied mode when all bits are clear: */
#define INVLPGB_MODE_ALL_NONGLOBALS 0UL
-static inline void invlpgb_flush_user_nr_nosync(unsigned long pcid,
- unsigned long addr,
- u16 nr, bool stride)
+static inline void __invlpgb_flush_user_nr_nosync(unsigned long pcid,
+ unsigned long addr,
+ u16 nr, bool stride)
{
enum addr_stride str = stride ? PMD_STRIDE : PTE_STRIDE;
u8 flags = INVLPGB_FLAG_PCID | INVLPGB_FLAG_VA;
@@ -119,7 +119,7 @@ static inline void invlpgb_flush_user_nr_nosync(unsigned long pcid,
}
/* Flush all mappings for a given PCID, not including globals. */
-static inline void invlpgb_flush_single_pcid_nosync(unsigned long pcid)
+static inline void __invlpgb_flush_single_pcid_nosync(unsigned long pcid)
{
__invlpgb(0, pcid, 0, 1, PTE_STRIDE, INVLPGB_FLAG_PCID);
}
@@ -139,7 +139,7 @@ static inline void invlpgb_flush_all(void)
}
/* Flush addr, including globals, for all PCIDs. */
-static inline void invlpgb_flush_addr_nosync(unsigned long addr, u16 nr)
+static inline void __invlpgb_flush_addr_nosync(unsigned long addr, u16 nr)
{
__invlpgb(0, 0, addr, nr, PTE_STRIDE, INVLPGB_FLAG_INCLUDE_GLOBAL);
}
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 7cad283d502d..214d912ac148 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -105,6 +105,9 @@ struct tlb_state {
* need to be invalidated.
*/
bool invalidate_other;
+#ifdef CONFIG_BROADCAST_TLB_FLUSH
+ bool need_tlbsync;
+#endif
#ifdef CONFIG_ADDRESS_MASKING
/*
@@ -292,12 +295,24 @@ static inline bool mm_in_asid_transition(struct mm_struct *mm)
return mm && READ_ONCE(mm->context.asid_transition);
}
+
+static inline bool cpu_need_tlbsync(void)
+{
+ return this_cpu_read(cpu_tlbstate.need_tlbsync);
+}
+
+static inline void cpu_set_tlbsync(bool state)
+{
+ this_cpu_write(cpu_tlbstate.need_tlbsync, state);
+}
#else
static inline u16 mm_global_asid(struct mm_struct *mm) { return 0; }
static inline void mm_init_global_asid(struct mm_struct *mm) { }
static inline void mm_assign_global_asid(struct mm_struct *mm, u16 asid) { }
static inline void mm_clear_asid_transition(struct mm_struct *mm) { }
static inline bool mm_in_asid_transition(struct mm_struct *mm) { return false; }
+static inline bool cpu_need_tlbsync(void) { return false; }
+static inline void cpu_set_tlbsync(bool state) { }
#endif /* CONFIG_BROADCAST_TLB_FLUSH */
#ifdef CONFIG_PARAVIRT
@@ -347,21 +362,15 @@ static inline u64 inc_mm_tlb_gen(struct mm_struct *mm)
return atomic64_inc_return(&mm->context.tlb_gen);
}
-static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *batch,
- struct mm_struct *mm,
- unsigned long uaddr)
-{
- inc_mm_tlb_gen(mm);
- cpumask_or(&batch->cpumask, &batch->cpumask, mm_cpumask(mm));
- mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
-}
-
static inline void arch_flush_tlb_batched_pending(struct mm_struct *mm)
{
flush_tlb_mm(mm);
}
extern void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch);
+extern void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *batch,
+ struct mm_struct *mm,
+ unsigned long uaddr);
static inline bool pte_flags_need_flush(unsigned long oldflags,
unsigned long newflags,
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 0efd99053c09..61065975c139 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -492,6 +492,37 @@ static void finish_asid_transition(struct flush_tlb_info *info)
mm_clear_asid_transition(mm);
}
+static inline void tlbsync(void)
+{
+ if (cpu_need_tlbsync()) {
+ __tlbsync();
+ cpu_set_tlbsync(false);
+ }
+}
+
+static inline void invlpgb_flush_user_nr_nosync(unsigned long pcid,
+ unsigned long addr,
+ u16 nr, bool pmd_stride)
+{
+ __invlpgb_flush_user_nr_nosync(pcid, addr, nr, pmd_stride);
+ if (!cpu_need_tlbsync())
+ cpu_set_tlbsync(true);
+}
+
+static inline void invlpgb_flush_single_pcid_nosync(unsigned long pcid)
+{
+ __invlpgb_flush_single_pcid_nosync(pcid);
+ if (!cpu_need_tlbsync())
+ cpu_set_tlbsync(true);
+}
+
+static inline void invlpgb_flush_addr_nosync(unsigned long addr, u16 nr)
+{
+ __invlpgb_flush_addr_nosync(addr, nr);
+ if (!cpu_need_tlbsync())
+ cpu_set_tlbsync(true);
+}
+
static void broadcast_tlb_flush(struct flush_tlb_info *info)
{
bool pmd = info->stride_shift == PMD_SHIFT;
@@ -790,6 +821,8 @@ void switch_mm_irqs_off(struct mm_struct *unused, struct mm_struct *next,
if (IS_ENABLED(CONFIG_PROVE_LOCKING))
WARN_ON_ONCE(!irqs_disabled());
+ tlbsync();
+
/*
* Verify that CR3 is what we think it is. This will catch
* hypothetical buggy code that directly switches to swapper_pg_dir
@@ -966,6 +999,8 @@ void switch_mm_irqs_off(struct mm_struct *unused, struct mm_struct *next,
*/
void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
{
+ tlbsync();
+
if (this_cpu_read(cpu_tlbstate.loaded_mm) == &init_mm)
return;
@@ -1633,9 +1668,7 @@ void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
* a local TLB flush is needed. Optimize this use-case by calling
* flush_tlb_func_local() directly in this case.
*/
- if (cpu_feature_enabled(X86_FEATURE_INVLPGB)) {
- invlpgb_flush_all_nonglobals();
- } else if (cpumask_any_but(&batch->cpumask, cpu) < nr_cpu_ids) {
+ if (cpumask_any_but(&batch->cpumask, cpu) < nr_cpu_ids) {
flush_tlb_multi(&batch->cpumask, info);
} else if (cpumask_test_cpu(cpu, &batch->cpumask)) {
lockdep_assert_irqs_enabled();
@@ -1644,12 +1677,61 @@ void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
local_irq_enable();
}
+ /*
+ * Wait for outstanding INVLPGB flushes. batch->cpumask will
+ * be empty when the batch was handled completely by INVLPGB.
+ * Note that mm_in_asid_transition() mm's may use INVLPGB and
+ * the flush_tlb_multi() IPIs at the same time.
+ */
+ tlbsync();
+
cpumask_clear(&batch->cpumask);
put_flush_tlb_info();
put_cpu();
}
+void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *batch,
+ struct mm_struct *mm, unsigned long uaddr)
+{
+ u16 global_asid = mm_global_asid(mm);
+
+ if (global_asid) {
+ /*
+ * Global ASIDs can be flushed with INVLPGB. Flush
+ * now instead of batching them for later. A later
+ * tlbsync() is required to ensure these completed.
+ */
+ invlpgb_flush_user_nr_nosync(kern_pcid(global_asid), uaddr, 1, false);
+ /* Do any CPUs supporting INVLPGB need PTI? */
+ if (cpu_feature_enabled(X86_FEATURE_PTI))
+ invlpgb_flush_user_nr_nosync(user_pcid(global_asid), uaddr, 1, false);
+
+ /*
+ * Some CPUs might still be using a local ASID for this
+ * process, and require IPIs, while others are using the
+ * global ASID.
+ *
+ * In this corner case, both broadcast TLB invalidation
+ * and IPIs need to be sent. The IPIs will help
+ * stragglers transition to the broadcast ASID.
+ */
+ if (mm_in_asid_transition(mm))
+ global_asid = 0;
+ }
+
+ if (!global_asid) {
+ /*
+ * Mark the mm and the CPU so that
+ * the TLB gets flushed later.
+ */
+ inc_mm_tlb_gen(mm);
+ cpumask_or(&batch->cpumask, &batch->cpumask, mm_cpumask(mm));
+ }
+
+ mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
+}
+
/*
* Blindly accessing user memory from NMI context can be dangerous
* if we're in the middle of switching the current user task or
--
2.43.0
next prev parent reply other threads:[~2025-03-04 13:59 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-04 13:58 [PATCH v15 00/11] AMD broadcast TLB invalidation Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 01/11] x86/mm: Consolidate full flush threshold decision Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 02/11] x86/mm: Add INVLPGB feature and Kconfig entry Borislav Petkov
2025-03-05 12:01 ` Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 03/11] x86/mm: Add INVLPGB support code Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 04/11] x86/mm: Use INVLPGB for kernel TLB flushes Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 05/11] x86/mm: Use broadcast TLB flushing in page reclaim Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 06/11] x86/mm: Add global ASID allocation helper functions Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 07/11] x86/mm: Handle global ASID context switch and TLB flush Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 08/11] x86/mm: Add global ASID process exit helpers Borislav Petkov
2025-03-04 13:58 ` [PATCH v15 09/11] x86/mm: Enable broadcast TLB invalidation for multi-threaded processes Borislav Petkov
2025-03-04 13:58 ` Borislav Petkov [this message]
2025-03-04 13:58 ` [PATCH v15 11/11] x86/mm: Enable AMD translation cache extensions 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=20250304135816.12356-11-bp@kernel.org \
--to=bp@kernel.org \
--cc=Manali.Shukla@amd.com \
--cc=akpm@linux-foundation.org \
--cc=andrew.cooper3@citrix.com \
--cc=bp@alien8.de \
--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=riel@surriel.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.