Skip to contentSkip to conversation

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

PATCH In review Imre Kaloz 2 participants 2 messages Sep 27

Subsystems (2)
lore
Share & tools
lore

Imre Kaloz

author
#0 lore raw

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

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")

---

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(+)
arch/sparc/kernel/sun4d_smp.c +6 -0
index 9a62a5cf3337..11ce613e2682 100644
@@ -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);
 				}
 			}
arch/sparc/kernel/sun4m_smp.c +6 -0
index 056df034e79e..6ee65c05d089 100644
@@ -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

Signature · 1 line
2.47.3
#1 lore raw

Hi Imre,

Quoted 15 lines · Imre Kaloz

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.

Quoted 9 lines

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")

---

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

Quoted 51 lines

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(+)
arch/sparc/kernel/sun4d_smp.c +6 -0
index 9a62a5cf3337..11ce613e2682 100644
@@ -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);
                                }
                        }
arch/sparc/kernel/sun4m_smp.c +6 -0
index 056df034e79e..6ee65c05d089 100644
@@ -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