mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2] ARM: asid: Do not replace active_asids if already 0
@ 2026-08-09 18:06 Karl Mehltretter
  2026-08-27  3:56 ` Karl Mehltretter
  2026-08-27 10:26 ` Catalin Marinas
  0 siblings, 2 replies; 3+ messages in thread
From: Karl Mehltretter @ 2026-08-09 18:06 UTC (permalink / raw)
  To: Russell King
  Cc: Karl Mehltretter, Catalin Marinas, Will Deacon, linux-arm-kernel,
	linux-kernel

From: Catalin Marinas <catalin.marinas@arm.com>

Under some uncommon timing conditions, a generation check and
xchg(active_asids, A1) in check_and_switch_context() on P1 can race with
an ASID roll-over on P2. If P2 has not seen the update to
active_asids[P1], it can re-allocate A1 to a new task T2 on P2. P1 ends
up waiting on the spinlock since the xchg() returned 0 while P2 can go
through a second ASID roll-over with (T2,A1,G2) active on P2. This
roll-over copies active_asids[P1] == A1,G1 into reserved_asids[P1] and
active_asids[P2] == A1,G2 into reserved_asids[P2]. A subsequent
scheduling of T1 on P1 and T2 on P2 would match reserved_asids and get
their generation bumped to G3:

P1					P2
--                                      --
TTBR0.BADDR = T0
TTBR0.ASID = A0
asid_generation = G1
check_and_switch_context(T1,A1,G1)
  generation match
					check_and_switch_context(T2,A0,G0)
					  new_context()
					    ASID roll-over
					    asid_generation = G2
					    flush_context()
					      active_asids[P1] = 0
					      asid_map[A1] = 0
					      reserved_asids[P1] = A0,G0
  xchg(active_asids, A1)
    active_asids[P1] = A1,G1
    xchg returns 0
  spin_lock_irqsave()
					    allocated ASID (T2,A1,G2)
					    asid_map[A1] = 1
					  active_asids[P2] = A1,G2
					...
					check_and_switch_context(T3,A0,G0)
					  new_context()
					    ASID roll-over
					    asid_generation = G3
					    flush_context()
					      active_asids[P1] = 0
					      asid_map[A1] = 1
					      reserved_asids[P1] = A1,G1
					      reserved_asids[P2] = A1,G2
					    allocated ASID (T3,A2,G3)
					    asid_map[A2] = 1
					  active_asids[P2] = A2,G3
  new_context()
    check_update_reserved_asid(A1,G1)
      matches reserved_asid[P1]
      reserved_asid[P1] = A1,G3
  updated T1 ASID to (T1,A1,G3)
					check_and_switch_context(T2,A1,G2)
					  new_context()
					    check_update_reserved_asid(A1,G2)
					      matches reserved_asids[P2]
					      reserved_asids[P2] = A1,G3
					  updated T2 ASID to (T2,A1,G3)

At this point, we have two tasks, T1 and T2 both using ASID A1 with the
latest generation G3. Any of them is allowed to be scheduled on the
other CPU leading to two different tasks with the same ASID on the same
CPU.

This patch changes the xchg to cmpxchg so that the active_asids is only
updated if non-zero to avoid a race with an ASID roll-over on a
different CPU.

Cc: Russell King <linux@armlinux.org.uk>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Tested-by: Karl Mehltretter <kmehltretter@gmail.com>
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
This is similar to the arm64 patch [1], with the difference that
non-relaxed (cmp)xchg is used as in the existing ARM code.

This is a resubmission of Catalin Marinas' original 2018 patch. The
original submission was not merged, and the ARM32 equivalent of the arm64
ASID rollover fix remains missing.

Changes in v2:
- Rebased onto v7.2-rc6-429-ga7c7074b58d2.
- Corrected the function name in the commit message's race diagram:
  check_update_reserved_asid(), called by new_context().
- Added my Tested-by and Signed-off-by trailers.

Tested with vexpress_defconfig on QEMU vexpress-a9 using four Cortex-A9
CPUs and four batches of 320 processes to exercise repeated ASID
roll-overs.

v1: https://lore.kernel.org/r/20180104180405.37596-1-catalin.marinas@arm.com/
[1] https://lore.kernel.org/r/20180104111721.33834-1-catalin.marinas@arm.com/

 arch/arm/mm/context.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c
index 4204ffa2d104..7c4e1e4ba77b 100644
--- a/arch/arm/mm/context.c
+++ b/arch/arm/mm/context.c
@@ -238,7 +238,7 @@ void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk)
 {
 	unsigned long flags;
 	unsigned int cpu = smp_processor_id();
-	u64 asid;
+	u64 asid, old_active_asid;
 
 	check_vmalloc_seq(mm);
 
@@ -250,8 +250,17 @@ void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk)
 	cpu_set_reserved_ttbr0();
 
 	asid = atomic64_read(&mm->context.id);
-	if (!((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS)
-	    && atomic64_xchg(&per_cpu(active_asids, cpu), asid))
+
+	/*
+	 * If our active_asids is zero, we are racing with an ASID roll-over
+	 * on a different CPU, so skip the update (using cmpxchg if non-zero)
+	 * and take the slow path.
+	 */
+	old_active_asid = atomic64_read(&per_cpu(active_asids, cpu));
+	if (old_active_asid &&
+	    !((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS) &&
+	    atomic64_cmpxchg(&per_cpu(active_asids, cpu),
+			     old_active_asid, asid))
 		goto switch_mm_fastpath;
 
 	raw_spin_lock_irqsave(&cpu_asid_lock, flags);
-- 
2.39.5 (Apple Git-154)

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v2] ARM: asid: Do not replace active_asids if already 0
  2026-08-09 18:06 [PATCH v2] ARM: asid: Do not replace active_asids if already 0 Karl Mehltretter
@ 2026-08-27  3:56 ` Karl Mehltretter
  2026-08-27 10:26 ` Catalin Marinas
  1 sibling, 0 replies; 3+ messages in thread
From: Karl Mehltretter @ 2026-08-27  3:56 UTC (permalink / raw)
  To: Russell King; +Cc: Catalin Marinas, Will Deacon, linux-arm-kernel, linux-kernel

On Sun, Aug 09, 2026 at 08:06:00PM +0100, Karl Mehltretter wrote:
> This patch changes the xchg to cmpxchg so that the active_asids is only
> updated if non-zero to avoid a race with an ASID roll-over on a
> different CPU.
> 
> Cc: Russell King <linux@armlinux.org.uk>
> Cc: Will Deacon <will@kernel.org>
> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> Tested-by: Karl Mehltretter <kmehltretter@gmail.com>
> Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
> ---
> This is similar to the arm64 patch [1], with the difference that
> non-relaxed (cmp)xchg is used as in the existing ARM code.
> 
> This is a resubmission of Catalin Marinas' original 2018 patch. The
> original submission was not merged, and the ARM32 equivalent of the arm64
> ASID rollover fix remains missing.
> 

Just a friendly ping in case this got missed.

Thanks,
Karl

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v2] ARM: asid: Do not replace active_asids if already 0
  2026-08-09 18:06 [PATCH v2] ARM: asid: Do not replace active_asids if already 0 Karl Mehltretter
  2026-08-27  3:56 ` Karl Mehltretter
@ 2026-08-27 10:26 ` Catalin Marinas
  1 sibling, 0 replies; 3+ messages in thread
From: Catalin Marinas @ 2026-08-27 10:26 UTC (permalink / raw)
  To: Karl Mehltretter
  Cc: Russell King, Will Deacon, linux-arm-kernel, linux-kernel

On Sun, Aug 09, 2026 at 08:06:00PM +0200, Karl Mehltretter wrote:
> This is similar to the arm64 patch [1], with the difference that
> non-relaxed (cmp)xchg is used as in the existing ARM code.

The patch is still valid. It's safe to use the relaxed accessors here if
you want or maybe do it as a separate patch for other atomic ops in this
code. I think at the time we added the ASID allocator, there were no
relaxed accessors in the kernel.

You can send it to Russell's patch system.

-- 
Catalin

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-08-27 10:26 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-09 18:06 [PATCH v2] ARM: asid: Do not replace active_asids if already 0 Karl Mehltretter
2026-08-27  3:56 ` Karl Mehltretter
2026-08-27 10:26 ` Catalin Marinas

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®