* FAILED: patch "[PATCH] alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms" failed to apply to 5.15-stable tree
@ 2026-11-07 11:08 Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 1/4] arch/alpha/kernel/smp.c: remove unnecessary (void*) conversions Sasha Levin
0 siblings, 1 reply; 5+ messages in thread
From: Sasha Levin @ 2026-11-07 11:08 UTC (permalink / raw)
To: linmag7, mattst88; +Cc: stable
The patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x fe221b7b2d3ecd042dd391f74ff6a6fdeb7c2038
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '20261107060811-failed-5.15-fe221b7b2d3e@kernel.org' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Thanks,
Sasha
------------------ original commit in Linus's tree ------------------
From fe221b7b2d3ecd042dd391f74ff6a6fdeb7c2038 Mon Sep 17 00:00:00 2001
From: Magnus Lindholm <linmag7@gmail.com>
Date: Sat, 10 Oct 2026 16:33:11 +0200
Subject: [PATCH] alpha: do not skip TLB shootdown IPIs for kthread-borrowed
mms
flush_tlb_mm(), flush_tlb_page() and flush_icache_user_page() skip the
shootdown IPI when mm_users <= 1, on the assumption that no other CPU can
be running the mm. A task that borrows an mm through kthread_use_mm()
takes mmgrab() rather than mmget(), so it never appears in mm_users, and
kthread_use_mm() may be handed an mm that is already the caller's
active_mm and loaded on that CPU. Such a CPU was not only left without
an IPI, its mm->context[cpu] was cleared from under it.
mm->context[cpu] is already the record of which CPUs hold an ASN for the
mm. Test it rather than clearing it: take the shortcut only when no
other CPU has one, and otherwise fall through to the IPI, which
invalidates those CPUs properly. A CPU that merely ran the mm in the
past also holds a context and now costs an IPI, which errs in the safe
direction.
Change ipi_flush_tlb_mm(), ipi_flush_icache_page() and the migration
callback ipi_flush_mm_and_page() to test current->mm, like
ipi_flush_tlb_page(). A CPU that only retains the mm lazily then clears
its own slot through flush_tlb_other(), rather than publishing a new ASN
on every IPI or migration rendezvous. The migration callback still does
its per-VA tbi() afterwards. Returning to the mm allocates a fresh ASN
through ev5_switch_mm(), or through the direct-load path when
kthread_use_mm() switches current's mm.
Once these historical slots are gone, the shortcut is available again
if mm_users remains at most one and no other CPU has taken or kept a
context. An explicit kthread_use_mm() borrower has current->mm set and
still receives the invalidate it needs.
The caller-side tests in flush_tlb_mm() and flush_icache_user_page()
remain current->active_mm. A kernel thread that flushes its lazily held
mm can therefore publish its own context slot. Those branches also
perform the shortcut check, which excludes the calling CPU's slot, so
leave them unchanged. This is distinct from a remote lazy CPU keeping
its slot populated through repeated callbacks.
Together with the preceding patch, dropping these clearing loops leaves
every runtime write to mm->context[] targeting the writing CPU's own slot,
so the array becomes a lockless publication of which CPUs may be using the
mm, with a single writer per slot. Mark the two stores that are now
observed from other CPUs; flush_tlb_other() already uses WRITE_ONCE().
The uniprocessor flush_icache_user_page() is left alone, as nothing reads
another CPU's slot there, and init_new_context() runs before the mm is
shared.
Order the slot reads after the page-table changes with smp_mb(). On the
publishing CPU, the store in ev5_switch_mm() precedes the mb() in Alpha's
arch_spin_unlock(), when finish_lock_switch() drops the rq lock. This
relies on Alpha's unlock implementation, not a generic scheduler promise
of a full barrier on unlock; document that reliance at the store. Also
issue smp_mb() after publishing in __load_new_mm_context(), before loading
and using the mm.
This covers every caller, including check_mmu_context(), which can
publish a replacement after finish_lock_switch() has already run.
This requires the preceding removal of remote slot clearing from
migrate_flush_tlb_page(). It also requires the stale-TLB series to set
need_new_asn on both the reused and newly allocated ASN paths. Otherwise
an IPI between finish_lock_switch() and check_mmu_context() can zero a
new slot while asn_lock is set, and the task can return to user space
with a live ASN that the shortcut cannot see.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: <stable@vger.kernel.org> # 53ab52cfe5ce: alpha: load the MMU context when switch_mm() switches the current task
Cc: <stable@vger.kernel.org> # 01df8f401888: alpha: run check_mmu_context() from finish_arch_post_lock_switch()
Cc: <stable@vger.kernel.org> # ef43cfc28c42: alpha: only use a targeted tbi() when the target mm is really current
Cc: <stable@vger.kernel.org> # 738cf8f7a78f: alpha: invalidate the local context in flush_tlb_page()
Cc: <stable@vger.kernel.org> # 62c92086f30e: alpha: fix the local TLB invalidate in flush_tlb_page()
Cc: <stable@vger.kernel.org> # 3faee097b939: alpha: fix the local TLB invalidate in the UP flush_tlb_page()
Cc: <stable@vger.kernel.org> # b2c3b654bb64: alpha: invalidate the local context in flush_tlb_mm()
Cc: <stable@vger.kernel.org> # f19ae6f51a38: alpha: invalidate the local context in flush_icache_user_page()
Cc: <stable@vger.kernel.org> # 471e7f9c890b: alpha: do not clear remote MMU contexts in migrate_flush_tlb_page()
Cc: stable@vger.kernel.org
Reviewed-by: Matt Turner <mattst88@gmail.com>
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Link: https://lore.kernel.org/r/20261010144621.974850-3-linmag7@gmail.com
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/alpha/include/asm/mmu_context.h | 7 +++-
arch/alpha/kernel/smp.c | 52 ++++++++++++++--------------
arch/alpha/mm/fault.c | 4 ++-
arch/alpha/mm/tlbflush.c | 2 +-
4 files changed, 36 insertions(+), 29 deletions(-)
diff --git a/arch/alpha/include/asm/mmu_context.h b/arch/alpha/include/asm/mmu_context.h
index f41d7c73fdd64..09f356934b90a 100644
--- a/arch/alpha/include/asm/mmu_context.h
+++ b/arch/alpha/include/asm/mmu_context.h
@@ -159,7 +159,12 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
mmc = next_mm->context[cpu];
if ((mmc ^ asn) & ~HARDWARE_ASN_MASK) {
mmc = __get_new_mm_context(next_mm, cpu);
- next_mm->context[cpu] = mmc;
+ /*
+ * Ordered before any user access through the mm by the
+ * mb() in arch_spin_unlock(), when finish_lock_switch()
+ * drops the rq lock.
+ */
+ WRITE_ONCE(next_mm->context[cpu], mmc);
}
#ifdef CONFIG_SMP
/* A deferred shootdown can also invalidate a newly allocated ASN. */
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index e21bc3920bece..11768aa292a48 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -625,12 +625,30 @@ static void
ipi_flush_tlb_mm(void *x)
{
struct mm_struct *mm = x;
- if (mm == current->active_mm && !asn_locked())
+ if (mm == current->mm && !asn_locked())
flush_tlb_current(mm);
else
flush_tlb_other(mm);
}
+/* True if a CPU other than this one holds an ASN for MM. */
+static bool
+mm_context_elsewhere(struct mm_struct *mm)
+{
+ int cpu, this_cpu = smp_processor_id();
+
+ /* Pairs with the barrier the publishing CPU issues before using MM. */
+ smp_mb();
+
+ for_each_online_cpu(cpu) {
+ if (cpu == this_cpu)
+ continue;
+ if (READ_ONCE(mm->context[cpu]))
+ return true;
+ }
+ return false;
+}
+
void
flush_tlb_mm(struct mm_struct *mm)
{
@@ -638,14 +656,8 @@ flush_tlb_mm(struct mm_struct *mm)
if (mm == current->active_mm) {
flush_tlb_current(mm);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
@@ -690,14 +702,8 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
/* As in ipi_flush_tlb_page(): a targeted tbi() needs MM current. */
if (mm == current->mm) {
flush_tlb_current_page(mm, vma, addr);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
@@ -728,7 +734,7 @@ static void
ipi_flush_icache_page(void *x)
{
struct mm_struct *mm = (struct mm_struct *) x;
- if (mm == current->active_mm && !asn_locked())
+ if (mm == current->mm && !asn_locked())
__load_new_mm_context(mm);
else
flush_tlb_other(mm);
@@ -747,14 +753,8 @@ flush_icache_user_page(struct vm_area_struct *vma, struct page *page,
if (mm == current->active_mm) {
__load_new_mm_context(mm);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c
index 24408b53197c1..63a24afa7a155 100644
--- a/arch/alpha/mm/fault.c
+++ b/arch/alpha/mm/fault.c
@@ -70,7 +70,9 @@ __load_new_mm_context(struct mm_struct *next_mm)
struct pcb_struct *pcb;
mmc = __get_new_mm_context(next_mm, smp_processor_id());
- next_mm->context[smp_processor_id()] = mmc;
+ WRITE_ONCE(next_mm->context[smp_processor_id()], mmc);
+ /* Publish the context before this CPU can access the mm. */
+ smp_mb();
pcb = ¤t_thread_info()->pcb;
pcb->asn = mmc & HARDWARE_ASN_MASK;
diff --git a/arch/alpha/mm/tlbflush.c b/arch/alpha/mm/tlbflush.c
index 239c72b8a7412..a2aef90e6234b 100644
--- a/arch/alpha/mm/tlbflush.c
+++ b/arch/alpha/mm/tlbflush.c
@@ -66,7 +66,7 @@ static void ipi_flush_mm_and_page(void *x)
struct tlb_mm_and_addr *d = x;
/* Part 1: mm context side (Alpha uses ASN/context as a key mechanism). */
- if (d->mm == current->active_mm && !asn_locked())
+ if (d->mm == current->mm && !asn_locked())
__load_new_mm_context(d->mm);
else
flush_tlb_other(d->mm);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 5.15.y 1/4] arch/alpha/kernel/smp.c: remove unnecessary (void*) conversions
2026-11-07 11:08 FAILED: patch "[PATCH] alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms" failed to apply to 5.15-stable tree Sasha Levin
@ 2026-11-11 17:57 ` Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 2/4] arch: turn off -Werror for architectures with known warnings Sasha Levin
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Sasha Levin @ 2026-11-11 17:57 UTC (permalink / raw)
To: stable
Cc: Li zeming, Ivan Kokshaysky, Matt Turner, Richard Henderson,
Andrew Morton, Sasha Levin
From: Li zeming <zeming@nfschina.com>
[ Upstream commit 829715a2d14c7110987bfa5e6173b516c41f8627 ]
The void * type pointer does not need to be cast and assigned to another
pointer.
Link: https://lkml.kernel.org/r/20221213074522.3738-1-zeming@nfschina.com
Signed-off-by: Li zeming <zeming@nfschina.com>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Matt Turner <mattst88@gmail.com>
Cc: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Stable-dep-of: fe221b7b2d3e ("alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/alpha/kernel/smp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index 8a712076436f0..8fa4345306a53 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -634,7 +634,7 @@ flush_tlb_all(void)
static void
ipi_flush_tlb_mm(void *x)
{
- struct mm_struct *mm = (struct mm_struct *) x;
+ struct mm_struct *mm = x;
if (mm == current->active_mm && !asn_locked())
flush_tlb_current(mm);
else
@@ -679,7 +679,7 @@ struct flush_tlb_page_struct {
static void
ipi_flush_tlb_page(void *x)
{
- struct flush_tlb_page_struct *data = (struct flush_tlb_page_struct *)x;
+ struct flush_tlb_page_struct *data = x;
struct mm_struct * mm = data->mm;
/* A targeted tbi() needs a thread of MM to be current. */
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 5.15.y 2/4] arch: turn off -Werror for architectures with known warnings
2026-11-11 17:57 ` [PATCH 5.15.y 1/4] arch/alpha/kernel/smp.c: remove unnecessary (void*) conversions Sasha Levin
@ 2026-11-11 17:57 ` Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 3/4] alpha: fix user-space corruption during memory compaction Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 4/4] alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-11-11 17:57 UTC (permalink / raw)
To: stable
Cc: Arnd Bergmann, Stephen Rothwell, David S. Miller,
Thomas Bogendoerfer, Andrew Morton, Sasha Levin
From: Arnd Bergmann <arnd@arndb.de>
[ Upstream commit 0f0d2871e78db648a2578abbeb9103f484f9b754 ]
A couple of architectures enable -Werror for their own files regardless of
CONFIG_WERROR but also have known warnings that fail the build with
-Wmissing-prototypes enabled by default:
arch/alpha/lib/memcpy.c:153:8: error: no previous prototype for 'memcpy' [-Werror=missing-prototypes]
arch/alpha/kernel/irq.c:96:1: error: no previous prototype for 'handle_irq' [-Werror=missing-prototypes]
arch/mips/kernel/signal.c:673:17: error: no previous prototype for ‘sys_rt_sigreturn’ [-Werror=missing-prototypes]
arch/mips/kernel/signal.c:636:17: error: no previous prototype for ‘sys_sigreturn’ [-Werror=missing-prototypes]
arch/mips/kernel/syscall.c:51:16: error: no previous prototype for ‘sysm_pipe’ [-Werror=missing-prototypes]
arch/mips/mm/fault.c:323:17: error: no previous prototype for ‘do_page_fault’ [-Werror=missing-prototypes]
arch/sparc/vdso/vma.c:246:12: warning: no previous prototype for ‘init_vdso_image’ [-Wmissing-prototypes]v
arch/sparc/vdso/vdso32/../vclock_gettime.c:343:1: warning: no previous prototype for ‘__vdso_gettimeofday_stick’ [-Wmissing-prototypes]
arch/sparc/vdso/vclock_gettime.c:343:1: warning: no previous prototype for ‘__vdso_gettimeofday_stick’ [-Wmissing-prototypes]
arch/sparc/prom/p1275.c:52:6: warning: no previous prototype for ‘prom_cif_init’ [-Wmissing-prototypes]
arch/sparc/prom/misc_64.c:165:5: warning: no previous prototype for ‘prom_get_mmu_ihandle’ [-Wmissing-prototypes]
This appears to be an artifact from the times when this architecture code
was better maintained that most device drivers and before CONFIG_WERROR
was added. Now it just gets in the way, so remove all of these.
Powerpc and x86 both still have their own Kconfig options to enable
-Werror for some of their files. These architectures are better
maintained than most and the options are easy to disable, so leave those
untouched.
Link: https://lkml.kernel.org/r/4be73872-c1f5-4c31-8201-712c19290a22@app.fastmail.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Reported-by: Stephen Rothwell <sfr@rothwell.id.au>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Stable-dep-of: fe221b7b2d3e ("alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/alpha/lib/Makefile | 1 -
arch/alpha/mm/Makefile | 2 --
arch/mips/Kbuild | 6 ------
arch/sparc/kernel/Makefile | 1 -
arch/sparc/lib/Makefile | 1 -
arch/sparc/mm/Makefile | 1 -
arch/sparc/prom/Makefile | 1 -
7 files changed, 13 deletions(-)
diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile
index 1cc74f7b50efb..6a779b9018fd1 100644
--- a/arch/alpha/lib/Makefile
+++ b/arch/alpha/lib/Makefile
@@ -4,7 +4,6 @@
#
asflags-y := $(KBUILD_CFLAGS)
-ccflags-y := -Werror
# Many of these routines have implementations tuned for ev6.
# Choose them iff we're targeting ev6 specifically.
diff --git a/arch/alpha/mm/Makefile b/arch/alpha/mm/Makefile
index bd770302eb824..101dbd06b4ceb 100644
--- a/arch/alpha/mm/Makefile
+++ b/arch/alpha/mm/Makefile
@@ -3,6 +3,4 @@
# Makefile for the linux alpha-specific parts of the memory manager.
#
-ccflags-y := -Werror
-
obj-y := init.o fault.o
diff --git a/arch/mips/Kbuild b/arch/mips/Kbuild
index d5d6ef9bb9867..ec088204f51cb 100644
--- a/arch/mips/Kbuild
+++ b/arch/mips/Kbuild
@@ -1,10 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-# Fail on warnings - also for files referenced in subdirs
-# -Werror can be disabled for specific files using:
-# CFLAGS_<file.o> := -Wno-error
-ifeq ($(W),)
-subdir-ccflags-y := -Werror
-endif
# platform specific definitions
include arch/mips/Kbuild.platforms
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index d3a0e072ebe8c..6858304be1dbe 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -5,7 +5,6 @@
#
asflags-y := -ansi
-ccflags-y := -Werror
extra-y := head_$(BITS).o
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
index 063556fe2cb1d..59669ebddd4e1 100644
--- a/arch/sparc/lib/Makefile
+++ b/arch/sparc/lib/Makefile
@@ -3,7 +3,6 @@
#
asflags-y := -ansi -DST_DIV0=0x02
-ccflags-y := -Werror
lib-$(CONFIG_SPARC32) += ashrdi3.o
lib-$(CONFIG_SPARC32) += memcpy.o memset.o
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
index 871354aa3c002..809d993f6d888 100644
--- a/arch/sparc/mm/Makefile
+++ b/arch/sparc/mm/Makefile
@@ -3,7 +3,6 @@
#
asflags-y := -ansi
-ccflags-y := -Werror
obj-$(CONFIG_SPARC64) += ultra.o tlb.o tsb.o
obj-y += fault_$(BITS).o
diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile
index 397b79af77f7b..a1adc75d80551 100644
--- a/arch/sparc/prom/Makefile
+++ b/arch/sparc/prom/Makefile
@@ -3,7 +3,6 @@
# Linux.
#
asflags := -ansi
-ccflags := -Werror
lib-y := bootstr_$(BITS).o
lib-y += init_$(BITS).o
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 5.15.y 3/4] alpha: fix user-space corruption during memory compaction
2026-11-11 17:57 ` [PATCH 5.15.y 1/4] arch/alpha/kernel/smp.c: remove unnecessary (void*) conversions Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 2/4] arch: turn off -Werror for architectures with known warnings Sasha Levin
@ 2026-11-11 17:57 ` Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 4/4] alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-11-11 17:57 UTC (permalink / raw)
To: stable
Cc: Magnus Lindholm, Ivan Kokshaysky, Matoro Mahri, Michael Cree,
Sasha Levin
From: Magnus Lindholm <linmag7@gmail.com>
[ Upstream commit dd5712f3379cfe760267cdd28ff957d9ab4e51c7 ]
Alpha systems can suffer sporadic user-space crashes and heap
corruption when memory compaction is enabled.
Symptoms include SIGSEGV, glibc allocator failures (e.g. "unaligned
tcache chunk"), and compiler internal errors. The failures disappear
when compaction is disabled or when using global TLB invalidation.
The root cause is insufficient TLB shootdown during page migration.
Alpha relies on ASN-based MM context rollover for instruction cache
coherency, but this alone is not sufficient to prevent stale data or
instruction translations from surviving migration.
Fix this by introducing a migration-specific helper that combines:
- MM context invalidation (ASN rollover),
- immediate per-CPU TLB invalidation (TBI),
- synchronous cross-CPU shootdown when required.
The helper is used only by migration/compaction paths to avoid changing
global TLB semantics.
Additionally, update flush_tlb_other(), pte_clear(), to use
READ_ONCE()/WRITE_ONCE() for correct SMP memory ordering.
This fixes observed crashes on both UP and SMP Alpha systems.
Reviewed-by: Ivan Kokshaysky <ink@unseen.parts>
Tested-by: Matoro Mahri <matoro_mailinglist_kernel@matoro.tk>
Tested-by: Michael Cree <mcree@orcon.net.nz>
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Link: https://lore.kernel.org/r/20260102173603.18247-2-linmag7@gmail.com
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Stable-dep-of: fe221b7b2d3e ("alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/alpha/include/asm/pgtable.h | 33 ++++++++-
arch/alpha/include/asm/tlbflush.h | 4 +-
arch/alpha/mm/Makefile | 2 +-
arch/alpha/mm/tlbflush.c | 112 ++++++++++++++++++++++++++++++
4 files changed, 148 insertions(+), 3 deletions(-)
create mode 100644 arch/alpha/mm/tlbflush.c
diff --git a/arch/alpha/include/asm/pgtable.h b/arch/alpha/include/asm/pgtable.h
index 8e3cf3c9f913d..e95fa0ee50c00 100644
--- a/arch/alpha/include/asm/pgtable.h
+++ b/arch/alpha/include/asm/pgtable.h
@@ -17,6 +17,7 @@
#include <asm/processor.h> /* For TASK_SIZE */
#include <asm/machvec.h>
#include <asm/setup.h>
+#include <linux/page_table_check.h>
struct mm_struct;
struct vm_area_struct;
@@ -227,6 +228,9 @@ extern inline void pud_set(pud_t * pudp, pmd_t * pmdp)
{ pud_val(*pudp) = _PAGE_TABLE | ((((unsigned long) pmdp) - PAGE_OFFSET) << (32-PAGE_SHIFT)); }
+extern void migrate_flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long addr);
+
extern inline unsigned long
pmd_page_vaddr(pmd_t pmd)
{
@@ -245,7 +249,7 @@ extern inline int pte_none(pte_t pte) { return !pte_val(pte); }
extern inline int pte_present(pte_t pte) { return pte_val(pte) & _PAGE_VALID; }
extern inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
- pte_val(*ptep) = 0;
+ WRITE_ONCE(pte_val(*ptep), 0);
}
extern inline int pmd_none(pmd_t pmd) { return !pmd_val(pmd); }
@@ -307,6 +311,33 @@ extern inline pte_t * pte_offset_kernel(pmd_t * dir, unsigned long address)
extern pgd_t swapper_pg_dir[1024];
+#ifdef CONFIG_COMPACTION
+#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
+
+static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
+ unsigned long address,
+ pte_t *ptep)
+{
+ pte_t pte = READ_ONCE(*ptep);
+
+ pte_clear(mm, address, ptep);
+ return pte;
+}
+
+#define __HAVE_ARCH_PTEP_CLEAR_FLUSH
+
+static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ pte_t pte = ptep_get_and_clear(mm, addr, ptep);
+
+ page_table_check_pte_clear(mm, pte);
+ migrate_flush_tlb_page(vma, addr);
+ return pte;
+}
+
+#endif
/*
* The Alpha doesn't have any external MMU info: the kernel page
* tables contain all the necessary information.
diff --git a/arch/alpha/include/asm/tlbflush.h b/arch/alpha/include/asm/tlbflush.h
index 64ea0f130cd57..d79d225da6c76 100644
--- a/arch/alpha/include/asm/tlbflush.h
+++ b/arch/alpha/include/asm/tlbflush.h
@@ -91,7 +91,9 @@ flush_tlb_other(struct mm_struct *mm)
unsigned long *mmc = &mm->context[smp_processor_id()];
/* Check it's not zero first to avoid cacheline ping pong
when possible. */
- if (*mmc) *mmc = 0;
+
+ if (READ_ONCE(*mmc))
+ WRITE_ONCE(*mmc, 0);
}
#ifndef CONFIG_SMP
diff --git a/arch/alpha/mm/Makefile b/arch/alpha/mm/Makefile
index 101dbd06b4ceb..2d05664058f64 100644
--- a/arch/alpha/mm/Makefile
+++ b/arch/alpha/mm/Makefile
@@ -3,4 +3,4 @@
# Makefile for the linux alpha-specific parts of the memory manager.
#
-obj-y := init.o fault.o
+obj-y := init.o fault.o tlbflush.o
diff --git a/arch/alpha/mm/tlbflush.c b/arch/alpha/mm/tlbflush.c
new file mode 100644
index 0000000000000..ccbc317b9a348
--- /dev/null
+++ b/arch/alpha/mm/tlbflush.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Alpha TLB shootdown helpers
+ *
+ * Copyright (C) 2025 Magnus Lindholm <linmag7@gmail.com>
+ *
+ * Alpha-specific TLB flush helpers that cannot be expressed purely
+ * as inline functions.
+ *
+ * These helpers provide combined MM context handling (ASN rollover)
+ * and immediate TLB invalidation for page migration and memory
+ * compaction paths, where lazy shootdowns are insufficient.
+ */
+
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/sched.h>
+#include <asm/tlbflush.h>
+#include <asm/pal.h>
+#include <asm/mmu_context.h>
+
+#define asn_locked() (cpu_data[smp_processor_id()].asn_lock)
+
+/*
+ * Migration/compaction helper: combine mm context (ASN) handling with an
+ * immediate per-page TLB invalidate and (for exec) an instruction barrier.
+ *
+ * This mirrors the SMP combined IPI handler semantics, but runs locally on UP.
+ */
+#ifndef CONFIG_SMP
+void migrate_flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ int tbi_type = (vma->vm_flags & VM_EXEC) ? 3 : 2;
+
+ /*
+ * First do the mm-context side:
+ * If we're currently running this mm, reload a fresh context ASN.
+ * Otherwise, mark context invalid.
+ *
+ * On UP, this is mostly about matching the SMP semantics and ensuring
+ * exec/i-cache tagging assumptions hold when compaction migrates pages.
+ */
+ if (mm == current->active_mm)
+ flush_tlb_current(mm);
+ else
+ flush_tlb_other(mm);
+
+ /*
+ * Then do the immediate translation kill for this VA.
+ * For exec mappings, order instruction fetch after invalidation.
+ */
+ tbi(tbi_type, addr);
+}
+
+#else
+struct tlb_mm_and_addr {
+ struct mm_struct *mm;
+ unsigned long addr;
+ int tbi_type; /* 2 = DTB, 3 = ITB+DTB */
+};
+
+static void ipi_flush_mm_and_page(void *x)
+{
+ struct tlb_mm_and_addr *d = x;
+
+ /* Part 1: mm context side (Alpha uses ASN/context as a key mechanism). */
+ if (d->mm == current->active_mm && !asn_locked())
+ __load_new_mm_context(d->mm);
+ else
+ flush_tlb_other(d->mm);
+
+ /* Part 2: immediate per-VA invalidation on this CPU. */
+ tbi(d->tbi_type, d->addr);
+}
+
+void migrate_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ struct tlb_mm_and_addr d = {
+ .mm = mm,
+ .addr = addr,
+ .tbi_type = (vma->vm_flags & VM_EXEC) ? 3 : 2,
+ };
+
+ /*
+ * One synchronous rendezvous: every CPU runs ipi_flush_mm_and_page().
+ * This is the "combined" version of flush_tlb_mm + per-page invalidate.
+ */
+ preempt_disable();
+ on_each_cpu(ipi_flush_mm_and_page, &d, 1);
+
+ /*
+ * mimic flush_tlb_mm()'s mm_users<=1 optimization.
+ */
+ if (atomic_read(&mm->mm_users) <= 1) {
+
+ int cpu, this_cpu;
+ this_cpu = smp_processor_id();
+
+ for (cpu = 0; cpu < NR_CPUS; cpu++) {
+ if (!cpu_online(cpu) || cpu == this_cpu)
+ continue;
+ if (READ_ONCE(mm->context[cpu]))
+ WRITE_ONCE(mm->context[cpu], 0);
+ }
+ }
+ preempt_enable();
+}
+
+#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 5.15.y 4/4] alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms
2026-11-11 17:57 ` [PATCH 5.15.y 1/4] arch/alpha/kernel/smp.c: remove unnecessary (void*) conversions Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 2/4] arch: turn off -Werror for architectures with known warnings Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 3/4] alpha: fix user-space corruption during memory compaction Sasha Levin
@ 2026-11-11 17:57 ` Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-11-11 17:57 UTC (permalink / raw)
To: stable; +Cc: Magnus Lindholm, Matt Turner, Sasha Levin
From: Magnus Lindholm <linmag7@gmail.com>
[ Upstream commit fe221b7b2d3ecd042dd391f74ff6a6fdeb7c2038 ]
flush_tlb_mm(), flush_tlb_page() and flush_icache_user_page() skip the
shootdown IPI when mm_users <= 1, on the assumption that no other CPU can
be running the mm. A task that borrows an mm through kthread_use_mm()
takes mmgrab() rather than mmget(), so it never appears in mm_users, and
kthread_use_mm() may be handed an mm that is already the caller's
active_mm and loaded on that CPU. Such a CPU was not only left without
an IPI, its mm->context[cpu] was cleared from under it.
mm->context[cpu] is already the record of which CPUs hold an ASN for the
mm. Test it rather than clearing it: take the shortcut only when no
other CPU has one, and otherwise fall through to the IPI, which
invalidates those CPUs properly. A CPU that merely ran the mm in the
past also holds a context and now costs an IPI, which errs in the safe
direction.
Change ipi_flush_tlb_mm(), ipi_flush_icache_page() and the migration
callback ipi_flush_mm_and_page() to test current->mm, like
ipi_flush_tlb_page(). A CPU that only retains the mm lazily then clears
its own slot through flush_tlb_other(), rather than publishing a new ASN
on every IPI or migration rendezvous. The migration callback still does
its per-VA tbi() afterwards. Returning to the mm allocates a fresh ASN
through ev5_switch_mm(), or through the direct-load path when
kthread_use_mm() switches current's mm.
Once these historical slots are gone, the shortcut is available again
if mm_users remains at most one and no other CPU has taken or kept a
context. An explicit kthread_use_mm() borrower has current->mm set and
still receives the invalidate it needs.
The caller-side tests in flush_tlb_mm() and flush_icache_user_page()
remain current->active_mm. A kernel thread that flushes its lazily held
mm can therefore publish its own context slot. Those branches also
perform the shortcut check, which excludes the calling CPU's slot, so
leave them unchanged. This is distinct from a remote lazy CPU keeping
its slot populated through repeated callbacks.
Together with the preceding patch, dropping these clearing loops leaves
every runtime write to mm->context[] targeting the writing CPU's own slot,
so the array becomes a lockless publication of which CPUs may be using the
mm, with a single writer per slot. Mark the two stores that are now
observed from other CPUs; flush_tlb_other() already uses WRITE_ONCE().
The uniprocessor flush_icache_user_page() is left alone, as nothing reads
another CPU's slot there, and init_new_context() runs before the mm is
shared.
Order the slot reads after the page-table changes with smp_mb(). On the
publishing CPU, the store in ev5_switch_mm() precedes the mb() in Alpha's
arch_spin_unlock(), when finish_lock_switch() drops the rq lock. This
relies on Alpha's unlock implementation, not a generic scheduler promise
of a full barrier on unlock; document that reliance at the store. Also
issue smp_mb() after publishing in __load_new_mm_context(), before loading
and using the mm.
This covers every caller, including check_mmu_context(), which can
publish a replacement after finish_lock_switch() has already run.
This requires the preceding removal of remote slot clearing from
migrate_flush_tlb_page(). It also requires the stale-TLB series to set
need_new_asn on both the reused and newly allocated ASN paths. Otherwise
an IPI between finish_lock_switch() and check_mmu_context() can zero a
new slot while asn_lock is set, and the task can return to user space
with a live ASN that the shortcut cannot see.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: <stable@vger.kernel.org> # 53ab52cfe5ce: alpha: load the MMU context when switch_mm() switches the current task
Cc: <stable@vger.kernel.org> # 01df8f401888: alpha: run check_mmu_context() from finish_arch_post_lock_switch()
Cc: <stable@vger.kernel.org> # ef43cfc28c42: alpha: only use a targeted tbi() when the target mm is really current
Cc: <stable@vger.kernel.org> # 738cf8f7a78f: alpha: invalidate the local context in flush_tlb_page()
Cc: <stable@vger.kernel.org> # 62c92086f30e: alpha: fix the local TLB invalidate in flush_tlb_page()
Cc: <stable@vger.kernel.org> # 3faee097b939: alpha: fix the local TLB invalidate in the UP flush_tlb_page()
Cc: <stable@vger.kernel.org> # b2c3b654bb64: alpha: invalidate the local context in flush_tlb_mm()
Cc: <stable@vger.kernel.org> # f19ae6f51a38: alpha: invalidate the local context in flush_icache_user_page()
Cc: <stable@vger.kernel.org> # 471e7f9c890b: alpha: do not clear remote MMU contexts in migrate_flush_tlb_page()
Cc: stable@vger.kernel.org
Reviewed-by: Matt Turner <mattst88@gmail.com>
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Link: https://lore.kernel.org/r/20261010144621.974850-3-linmag7@gmail.com
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
arch/alpha/include/asm/mmu_context.h | 7 +++-
arch/alpha/kernel/smp.c | 52 ++++++++++++++--------------
arch/alpha/mm/fault.c | 4 ++-
arch/alpha/mm/tlbflush.c | 2 +-
4 files changed, 36 insertions(+), 29 deletions(-)
diff --git a/arch/alpha/include/asm/mmu_context.h b/arch/alpha/include/asm/mmu_context.h
index 7aeee0846dc9d..42c9ab7624224 100644
--- a/arch/alpha/include/asm/mmu_context.h
+++ b/arch/alpha/include/asm/mmu_context.h
@@ -149,7 +149,12 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
mmc = next_mm->context[cpu];
if ((mmc ^ asn) & ~HARDWARE_ASN_MASK) {
mmc = __get_new_mm_context(next_mm, cpu);
- next_mm->context[cpu] = mmc;
+ /*
+ * Ordered before any user access through the mm by the
+ * mb() in arch_spin_unlock(), when finish_lock_switch()
+ * drops the rq lock.
+ */
+ WRITE_ONCE(next_mm->context[cpu], mmc);
}
#ifdef CONFIG_SMP
/* A deferred shootdown can also invalidate a newly allocated ASN. */
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index 8fa4345306a53..f5f34eaf92bb4 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -635,12 +635,30 @@ static void
ipi_flush_tlb_mm(void *x)
{
struct mm_struct *mm = x;
- if (mm == current->active_mm && !asn_locked())
+ if (mm == current->mm && !asn_locked())
flush_tlb_current(mm);
else
flush_tlb_other(mm);
}
+/* True if a CPU other than this one holds an ASN for MM. */
+static bool
+mm_context_elsewhere(struct mm_struct *mm)
+{
+ int cpu, this_cpu = smp_processor_id();
+
+ /* Pairs with the barrier the publishing CPU issues before using MM. */
+ smp_mb();
+
+ for_each_online_cpu(cpu) {
+ if (cpu == this_cpu)
+ continue;
+ if (READ_ONCE(mm->context[cpu]))
+ return true;
+ }
+ return false;
+}
+
void
flush_tlb_mm(struct mm_struct *mm)
{
@@ -648,14 +666,8 @@ flush_tlb_mm(struct mm_struct *mm)
if (mm == current->active_mm) {
flush_tlb_current(mm);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
@@ -700,14 +712,8 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
/* As in ipi_flush_tlb_page(): a targeted tbi() needs MM current. */
if (mm == current->mm) {
flush_tlb_current_page(mm, vma, addr);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
@@ -738,7 +744,7 @@ static void
ipi_flush_icache_page(void *x)
{
struct mm_struct *mm = (struct mm_struct *) x;
- if (mm == current->active_mm && !asn_locked())
+ if (mm == current->mm && !asn_locked())
__load_new_mm_context(mm);
else
flush_tlb_other(mm);
@@ -757,14 +763,8 @@ flush_icache_user_page(struct vm_area_struct *vma, struct page *page,
if (mm == current->active_mm) {
__load_new_mm_context(mm);
- if (atomic_read(&mm->mm_users) <= 1) {
- int cpu, this_cpu = smp_processor_id();
- for (cpu = 0; cpu < NR_CPUS; cpu++) {
- if (!cpu_online(cpu) || cpu == this_cpu)
- continue;
- if (mm->context[cpu])
- mm->context[cpu] = 0;
- }
+ if (atomic_read(&mm->mm_users) <= 1 &&
+ !mm_context_elsewhere(mm)) {
preempt_enable();
return;
}
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c
index 56ea2856e4888..14c56512bddac 100644
--- a/arch/alpha/mm/fault.c
+++ b/arch/alpha/mm/fault.c
@@ -45,7 +45,9 @@ __load_new_mm_context(struct mm_struct *next_mm)
struct pcb_struct *pcb;
mmc = __get_new_mm_context(next_mm, smp_processor_id());
- next_mm->context[smp_processor_id()] = mmc;
+ WRITE_ONCE(next_mm->context[smp_processor_id()], mmc);
+ /* Publish the context before this CPU can access the mm. */
+ smp_mb();
pcb = ¤t_thread_info()->pcb;
pcb->asn = mmc & HARDWARE_ASN_MASK;
diff --git a/arch/alpha/mm/tlbflush.c b/arch/alpha/mm/tlbflush.c
index ccbc317b9a348..7fa36d0eb4622 100644
--- a/arch/alpha/mm/tlbflush.c
+++ b/arch/alpha/mm/tlbflush.c
@@ -66,7 +66,7 @@ static void ipi_flush_mm_and_page(void *x)
struct tlb_mm_and_addr *d = x;
/* Part 1: mm context side (Alpha uses ASN/context as a key mechanism). */
- if (d->mm == current->active_mm && !asn_locked())
+ if (d->mm == current->mm && !asn_locked())
__load_new_mm_context(d->mm);
else
flush_tlb_other(d->mm);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-11 17:57 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-07 11:08 FAILED: patch "[PATCH] alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms" failed to apply to 5.15-stable tree Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 1/4] arch/alpha/kernel/smp.c: remove unnecessary (void*) conversions Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 2/4] arch: turn off -Werror for architectures with known warnings Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 3/4] alpha: fix user-space corruption during memory compaction Sasha Levin
2026-11-11 17:57 ` [PATCH 5.15.y 4/4] alpha: do not skip TLB shootdown IPIs for kthread-borrowed mms Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox