* [PATCH 1/2] sparc32: serialize SuperSPARC demap operations
2026-09-04 5:53 [PATCH 0/2] sparc32: fix SuperSPARC SMP synchronization Magnus Lindholm
@ 2026-09-04 5:53 ` Magnus Lindholm
2026-09-04 5:53 ` [PATCH 2/2] sparc32: synchronize SuperSPARC instruction updates Magnus Lindholm
1 sibling, 0 replies; 3+ messages in thread
From: Magnus Lindholm @ 2026-09-04 5:53 UTC (permalink / raw)
To: davem, andreas; +Cc: sam, sparclinux, linux-kernel, Magnus Lindholm
SuperSPARC requires software to guarantee that only one Demap operation
is in progress across the system. On MBus, each processor which may
retain a stale translation must perform its own local Demap.
A lock around each local operation is not safe on sun4m. Cross-call
callbacks run at interrupt level 15, so they can interrupt a CPU even
while spin_lock_irqsave() protects its local Demap. If the callback then
takes the same lock, it deadlocks against the interrupted owner.
Serialize each complete shootdown at the initiating CPU instead. Invoke
remote CPUs one at a time with lock-free callbacks, perform the
initiator's local Demap last, and release the lock only after the entire
operation is complete.
sun4m_cross_call() waits for the target CPU to complete its callback.
Singleton cross-calls therefore serialize the actual Demap operations,
not merely their dispatch.
sun4d already serializes Viking TLB flushes around its broadcast Demap
operations, so this wrapper is needed only for sun4m.
This is independent of MBus cache coherence. MBus Level 2 and the MXCC
coherence protocol describe physical cache-block transactions; they do
not make an MBus peer discard a virtual TLB entry.
This follows SuperSPARC Family User's Manual sections 8.5.3 and 9.8.2;
Sun-4M System Architecture section 7.1.4; MBus Interface Specification,
Level 2 Overview; and MXCC Addendum section B.2, Multiprocessor Cache
Coherence Support.
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/mm/srmmu.c | 88 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
index 1b24c5e8d73d..3b87e6f53fca 100644
--- a/arch/sparc/mm/srmmu.c
+++ b/arch/sparc/mm/srmmu.c
@@ -1642,6 +1642,33 @@ static void smp_flush_tlb_all(void)
local_ops->tlb_all();
}
+/* Serialize complete sun4m Viking shootdowns; remote callbacks must not lock. */
+static DEFINE_SPINLOCK(viking_tlb_lock);
+
+static void smp_viking_flush_tlb_xcall(void *func, unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ if (cpu == smp_processor_id())
+ continue;
+ sparc32_ipi_ops->cross_call(func, *cpumask_of(cpu),
+ arg1, arg2, arg3, 0);
+ }
+}
+
+static void smp_viking_flush_tlb_all(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&viking_tlb_lock, flags);
+ smp_viking_flush_tlb_xcall(local_ops->tlb_all, 0, 0, 0);
+ local_ops->tlb_all();
+ spin_unlock_irqrestore(&viking_tlb_lock, flags);
+}
+
static bool any_other_mm_cpus(struct mm_struct *mm)
{
return cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids;
@@ -1669,6 +1696,25 @@ static void smp_flush_tlb_mm(struct mm_struct *mm)
}
}
+static void smp_viking_flush_tlb_mm(struct mm_struct *mm)
+{
+ unsigned long flags;
+
+ if (mm->context != NO_CONTEXT) {
+ spin_lock_irqsave(&viking_tlb_lock, flags);
+ if (any_other_mm_cpus(mm)) {
+ smp_viking_flush_tlb_xcall(local_ops->tlb_mm,
+ (unsigned long)mm, 0, 0);
+ if (atomic_read(&mm->mm_users) == 1 &&
+ current->active_mm == mm)
+ cpumask_copy(mm_cpumask(mm),
+ cpumask_of(smp_processor_id()));
+ }
+ local_ops->tlb_mm(mm);
+ spin_unlock_irqrestore(&viking_tlb_lock, flags);
+ }
+}
+
static void smp_flush_cache_range(struct vm_area_struct *vma,
unsigned long start,
unsigned long end)
@@ -1697,6 +1743,24 @@ static void smp_flush_tlb_range(struct vm_area_struct *vma,
}
}
+static void smp_viking_flush_tlb_range(struct vm_area_struct *vma,
+ unsigned long start,
+ unsigned long end)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ unsigned long flags;
+
+ if (mm->context != NO_CONTEXT) {
+ spin_lock_irqsave(&viking_tlb_lock, flags);
+ if (any_other_mm_cpus(mm))
+ smp_viking_flush_tlb_xcall(local_ops->tlb_range,
+ (unsigned long)vma,
+ start, end);
+ local_ops->tlb_range(vma, start, end);
+ spin_unlock_irqrestore(&viking_tlb_lock, flags);
+ }
+}
+
static void smp_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
{
struct mm_struct *mm = vma->vm_mm;
@@ -1719,6 +1783,23 @@ static void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
}
}
+static void smp_viking_flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long page)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ unsigned long flags;
+
+ if (mm->context != NO_CONTEXT) {
+ spin_lock_irqsave(&viking_tlb_lock, flags);
+ if (any_other_mm_cpus(mm))
+ smp_viking_flush_tlb_xcall(local_ops->tlb_page,
+ (unsigned long)vma,
+ page, 0);
+ local_ops->tlb_page(vma, page);
+ spin_unlock_irqrestore(&viking_tlb_lock, flags);
+ }
+}
+
static void smp_flush_page_to_ram(unsigned long page)
{
/* Current theory is that those who call this are the one's
@@ -1773,6 +1854,13 @@ void __init load_mmu(void)
}
if (poke_srmmu == poke_viking) {
+ if (sparc_cpu_model == sun4m) {
+ smp_cachetlb_ops.tlb_all = smp_viking_flush_tlb_all;
+ smp_cachetlb_ops.tlb_mm = smp_viking_flush_tlb_mm;
+ smp_cachetlb_ops.tlb_range = smp_viking_flush_tlb_range;
+ smp_cachetlb_ops.tlb_page = smp_viking_flush_tlb_page;
+ }
+
/* Avoid unnecessary cross calls. */
smp_cachetlb_ops.cache_all = local_ops->cache_all;
smp_cachetlb_ops.cache_mm = local_ops->cache_mm;
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH 2/2] sparc32: synchronize SuperSPARC instruction updates
2026-09-04 5:53 [PATCH 0/2] sparc32: fix SuperSPARC SMP synchronization Magnus Lindholm
2026-09-04 5:53 ` [PATCH 1/2] sparc32: serialize SuperSPARC demap operations Magnus Lindholm
@ 2026-09-04 5:53 ` Magnus Lindholm
1 sibling, 0 replies; 3+ messages in thread
From: Magnus Lindholm @ 2026-09-04 5:53 UTC (permalink / raw)
To: davem, andreas; +Cc: sam, sparclinux, linux-kernel, Magnus Lindholm
SuperSPARC keeps its instruction cache coherent by snooping bus
transactions, but a FLUSH is still required after modifying instructions.
It drains the local store buffer, completes pending coherency traffic and
clears the local pipeline and instruction buffer. FLUSH affects only the
processor which executes it.
The SuperSPARC II addendum also states that the store buffer cannot be
snooped. The writer's FLUSH is therefore required to make buffered stores
reach the coherent hierarchy before remote processors clear their own
pipelines.
Implement the previously empty Viking signal-instruction operation and
stop bypassing the SMP wrapper. Run a local FLUSH on the writing CPU
before asking remote processors to clear their pipelines.
Also implement the generic flush_icache_range() hook for Viking. The
module loader and kernel text modification paths use this hook after
publishing executable code. One FLUSH on each processor is sufficient:
Viking invalidates instruction-cache entries through hardware snooping,
while FLUSH provides the required store and pipeline synchronization.
The manual is explicit that FLUSH is not scoped to the address given to
it: "No cached information is explicitly flushed by the instruction ...
FLUSH operations simply cause an exact synchronization of all pending
activity" (section 7.4). A single FLUSH per processor therefore covers
however many words were written before it, which is also why the
sig_insns hook above needs only one FLUSH for its two-instruction
trampoline.
This follows SuperSPARC Family User's Manual sections 7.4, Flush
(IFLUSH), and 10.2.5, Instruction Cache Consistency, and SuperSPARC II
Addendum section A.8.2, Store Buffer & Snoops.
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/include/asm/cacheflush_32.h | 2 +-
arch/sparc/mm/srmmu.c | 32 ++++++++++++++++++++++++--
arch/sparc/mm/viking.S | 4 ++++
3 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/arch/sparc/include/asm/cacheflush_32.h b/arch/sparc/include/asm/cacheflush_32.h
index 9fee0ccfccb8..4249663efacc 100644
--- a/arch/sparc/include/asm/cacheflush_32.h
+++ b/arch/sparc/include/asm/cacheflush_32.h
@@ -15,7 +15,7 @@
sparc32_cachetlb_ops->cache_range(vma, start, end)
#define flush_cache_page(vma,addr,pfn) \
sparc32_cachetlb_ops->cache_page(vma, addr)
-#define flush_icache_range(start, end) do { } while (0)
+void flush_icache_range(unsigned long start, unsigned long end);
#define copy_to_user_page(vma, page, vaddr, dst, src, len) \
do { \
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
index 3b87e6f53fca..2cf26285e50f 100644
--- a/arch/sparc/mm/srmmu.c
+++ b/arch/sparc/mm/srmmu.c
@@ -1816,9 +1816,12 @@ static void smp_flush_page_to_ram(unsigned long page)
static void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
{
+ preempt_disable();
+ /* Publish the stores before remote CPUs discard prefetched insns. */
+ local_ops->sig_insns(mm, insn_addr);
if (any_other_mm_cpus(mm))
xc2(local_ops->sig_insns, (unsigned long)mm, insn_addr);
- local_ops->sig_insns(mm, insn_addr);
+ preempt_enable();
}
static struct sparc32_cachetlb_ops smp_cachetlb_ops __ro_after_init = {
@@ -1836,6 +1839,32 @@ static struct sparc32_cachetlb_ops smp_cachetlb_ops __ro_after_init = {
};
#endif
+static void local_viking_flush_icache_range(unsigned long start,
+ unsigned long end)
+{
+ if (start >= end)
+ return;
+
+ /* Viking snoops the I-cache; FLUSH drains stores and the pipeline. */
+ __asm__ __volatile__("flush %0" : : "r" (start) : "memory");
+}
+
+void flush_icache_range(unsigned long start, unsigned long end)
+{
+ if (start >= end || poke_srmmu != poke_viking)
+ return;
+
+ /* Keep the locally flushed CPU as the CPU omitted by the cross-call. */
+ preempt_disable();
+ /* Make the modified instructions visible before flushing remotes. */
+ local_viking_flush_icache_range(start, end);
+#ifdef CONFIG_SMP
+ xc2(local_viking_flush_icache_range, start, end);
+#endif
+ preempt_enable();
+}
+EXPORT_SYMBOL(flush_icache_range);
+
/* Load up routines and constants for sun4m and sun4d mmu */
void __init load_mmu(void)
{
@@ -1868,7 +1897,6 @@ void __init load_mmu(void)
smp_cachetlb_ops.cache_page = local_ops->cache_page;
smp_cachetlb_ops.page_to_ram = local_ops->page_to_ram;
- smp_cachetlb_ops.sig_insns = local_ops->sig_insns;
smp_cachetlb_ops.page_for_dma = local_ops->page_for_dma;
}
diff --git a/arch/sparc/mm/viking.S b/arch/sparc/mm/viking.S
index 8b4e251bbba2..f8cdd399d26b 100644
--- a/arch/sparc/mm/viking.S
+++ b/arch/sparc/mm/viking.S
@@ -201,7 +201,11 @@ viking_flush_tlb_page:
viking_flush_page_to_ram:
viking_flush_page_for_dma:
+ retl
+ nop
+
viking_flush_sig_insns:
+ flush %o1
retl
nop
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread