diff mbox series

sparc32: order sun4m and sun4d cross-call flag resets before sending the IPI

Message ID 20260927005726.8274-1-kaloz@kernel.org
State New
Headers show
Series sparc32: order sun4m and sun4d cross-call flag resets before sending the IPI | expand

Commit Message

Imre Kaloz Sept. 27, 2026, 12:57 a.m. UTC
sun4m_cross_call() and sun4d_cross_call() clear processors_in[]/
processors_out[] for each target and then raise the cross-call
interrupt. sun4m sends it through sbus_writel(), a volatile C store,
which orders it only against other volatile accesses; sun4d sends it
through cc_set_igen(), an asm volatile with no "memory" clobber.
ccall_info is not volatile, so in both paths the compiler may sink the
two resets below the send; gcc 14 does it for sun4m. A target that
takes the interrupt and runs smp4m_cross_call_irq() or
smp4d_cross_call_irq() before the resets land has its flags cleared
behind it, and the sender spins forever on processors_in[] with
cross_call_lock held.

Add a wmb() before sending the IPI in both cross-call paths.

sun4m has raised the IPI inline since the commit below; before it,
set_cpu_int() was an out-of-line call. sun4d has had the same gap since
2.1.97.

Fixes: 4ba22b16bbf3 ("sparc32: move smp ipi to method ops")
Cc: stable@vger.kernel.org
Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---

Notes:
    - gcc 14 moves the resets past the send in sun4m_cross_call() and
      keeps them ahead of it in sun4d_cross_call(); the barrier is missing
      in both.
    - leon_cross_call() needs no change: leon_store_reg() already carries
      a "memory" clobber.

 arch/sparc/kernel/sun4d_smp.c | 6 ++++++
 arch/sparc/kernel/sun4m_smp.c | 6 ++++++
 2 files changed, 12 insertions(+)


base-commit: 62f4c998b297cf233997a2b4cd6fc2d2df0319c9

Comments

Magnus Lindholm Sept. 27, 2026, 10:44 p.m. UTC | #1
Hi Imre,

On Sun, Sep 27, 2026 at 2:58 AM Imre Kaloz <kaloz@kernel.org> wrote:
>
> sun4m_cross_call() and sun4d_cross_call() clear processors_in[]/
> processors_out[] for each target and then raise the cross-call
> interrupt. sun4m sends it through sbus_writel(), a volatile C store,
> which orders it only against other volatile accesses; sun4d sends it
> through cc_set_igen(), an asm volatile with no "memory" clobber.
> ccall_info is not volatile, so in both paths the compiler may sink the
> two resets below the send; gcc 14 does it for sun4m. A target that
> takes the interrupt and runs smp4m_cross_call_irq() or
> smp4d_cross_call_irq() before the resets land has its flags cleared
> behind it, and the sender spins forever on processors_in[] with
> cross_call_lock held.
>
> Add a wmb() before sending the IPI in both cross-call paths.
>
> sun4m has raised the IPI inline since the commit below; before it,
> set_cpu_int() was an out-of-line call. sun4d has had the same gap since
> 2.1.97.
>
> Fixes: 4ba22b16bbf3 ("sparc32: move smp ipi to method ops")
> Cc: stable@vger.kernel.org
> Signed-off-by: Imre Kaloz <kaloz@kernel.org>
> ---
>

You have gcc 14 sinking them for sun4m; the other end of the range, for the
record: gcc 9.5.0 (crosstool-NG, sparc-linux-gnu) does not. Adding your wmb()
at exactly this spot leaves sun4m_smp.o byte-identical, and wmb() is barrier()
on sparc32, so that compiler was already keeping the resets ahead of the send.

Might be worth a line in the changelog for the stable side: older toolchains
build correct code today and the patch is a no-op for them. Still obviously
right to add the barrier.

Magnus

> Notes:
>     - gcc 14 moves the resets past the send in sun4m_cross_call() and
>       keeps them ahead of it in sun4d_cross_call(); the barrier is missing
>       in both.
>     - leon_cross_call() needs no change: leon_store_reg() already carries
>       a "memory" clobber.
>
>  arch/sparc/kernel/sun4d_smp.c | 6 ++++++
>  arch/sparc/kernel/sun4m_smp.c | 6 ++++++
>  2 files changed, 12 insertions(+)
>
> diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
> index 9a62a5cf3337..11ce613e2682 100644
> --- a/arch/sparc/kernel/sun4d_smp.c
> +++ b/arch/sparc/kernel/sun4d_smp.c
> @@ -321,6 +321,12 @@ static void sun4d_cross_call(void *func, cpumask_t mask, unsigned long arg1,
>                                 if (cpumask_test_cpu(i, &mask)) {
>                                         ccall_info.processors_in[i] = 0;
>                                         ccall_info.processors_out[i] = 0;
> +                                       /*
> +                                        * The target may run the handler as soon
> +                                        * as the IPI lands, so the resets must be
> +                                        * visible before it is sent.
> +                                        */
> +                                       wmb();
>                                         sun4d_send_ipi(i, IRQ_CROSS_CALL);
>                                 }
>                         }
> diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
> index 056df034e79e..6ee65c05d089 100644
> --- a/arch/sparc/kernel/sun4m_smp.c
> +++ b/arch/sparc/kernel/sun4m_smp.c
> @@ -197,6 +197,12 @@ static void sun4m_cross_call(void *func, cpumask_t mask, unsigned long arg1,
>                                 if (cpumask_test_cpu(i, &mask)) {
>                                         ccall_info.processors_in[i] = 0;
>                                         ccall_info.processors_out[i] = 0;
> +                                       /*
> +                                        * The target may run the handler as soon
> +                                        * as the IPI lands, so the resets must be
> +                                        * visible before it is sent.
> +                                        */
> +                                       wmb();
>                                         sun4m_send_ipi(i, IRQ_CROSS_CALL);
>                                 } else {
>                                         ccall_info.processors_in[i] = 1;
>
> base-commit: 62f4c998b297cf233997a2b4cd6fc2d2df0319c9
> --
> 2.47.3
>
>
diff mbox series

Patch

diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
index 9a62a5cf3337..11ce613e2682 100644
--- a/arch/sparc/kernel/sun4d_smp.c
+++ b/arch/sparc/kernel/sun4d_smp.c
@@ -321,6 +321,12 @@  static void sun4d_cross_call(void *func, cpumask_t mask, unsigned long arg1,
 				if (cpumask_test_cpu(i, &mask)) {
 					ccall_info.processors_in[i] = 0;
 					ccall_info.processors_out[i] = 0;
+					/*
+					 * The target may run the handler as soon
+					 * as the IPI lands, so the resets must be
+					 * visible before it is sent.
+					 */
+					wmb();
 					sun4d_send_ipi(i, IRQ_CROSS_CALL);
 				}
 			}
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
index 056df034e79e..6ee65c05d089 100644
--- a/arch/sparc/kernel/sun4m_smp.c
+++ b/arch/sparc/kernel/sun4m_smp.c
@@ -197,6 +197,12 @@  static void sun4m_cross_call(void *func, cpumask_t mask, unsigned long arg1,
 				if (cpumask_test_cpu(i, &mask)) {
 					ccall_info.processors_in[i] = 0;
 					ccall_info.processors_out[i] = 0;
+					/*
+					 * The target may run the handler as soon
+					 * as the IPI lands, so the resets must be
+					 * visible before it is sent.
+					 */
+					wmb();
 					sun4m_send_ipi(i, IRQ_CROSS_CALL);
 				} else {
 					ccall_info.processors_in[i] = 1;