| 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 |
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 --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;
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