Skip to content

Commit f86c4fb

Browse files
wildea01Marc Zyngier
authored andcommitted
irqchip/gic: Ensure ordering between read of INTACK and shared data
When an IPI is generated by a CPU, the pattern looks roughly like: <write shared data> smp_wmb(); <write to GIC to signal SGI> On the receiving CPU we rely on the fact that, once we've taken the interrupt, then the freshly written shared data must be visible to us. Put another way, the CPU isn't going to speculate taking an interrupt. Unfortunately, this assumption turns out to be broken. Consider that CPUx wants to send an IPI to CPUy, which will cause CPUy to read some shared_data. Before CPUx has done anything, a random peripheral raises an IRQ to the GIC and the IRQ line on CPUy is raised. CPUy then takes the IRQ and starts executing the entry code, heading towards gic_handle_irq. Furthermore, let's assume that a bunch of the previous interrupts handled by CPUy were SGIs, so the branch predictor kicks in and speculates that irqnr will be <16 and we're likely to head into handle_IPI. The prefetcher then grabs a speculative copy of shared_data which contains a stale value. Meanwhile, CPUx gets round to updating shared_data and asking the GIC to send an SGI to CPUy. Internally, the GIC decides that the SGI is more important than the peripheral interrupt (which hasn't yet been ACKed) but doesn't need to do anything to CPUy, because the IRQ line is already raised. CPUy then reads the ACK register on the GIC, sees the SGI value which confirms the branch prediction and we end up with a stale shared_data value. This patch fixes the problem by adding an smp_rmb() to the IPI entry code in gic_handle_irq. As it turns out, the combination of a control dependency and an ISB instruction from the EOI in the GICv3 driver is enough to provide the ordering we need, so we add a comment there justifying the absence of an explicit smp_rmb(). Cc: [email protected] Signed-off-by: Will Deacon <[email protected]> Signed-off-by: Marc Zyngier <[email protected]>
1 parent b8f3ebe commit f86c4fb

File tree

2 files changed

+15
-0
lines changed

2 files changed

+15
-0
lines changed

drivers/irqchip/irq-gic-v3.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
367367
if (static_key_true(&supports_deactivate))
368368
gic_write_dir(irqnr);
369369
#ifdef CONFIG_SMP
370+
/*
371+
* Unlike GICv2, we don't need an smp_rmb() here.
372+
* The control dependency from gic_read_iar to
373+
* the ISB in gic_write_eoir is enough to ensure
374+
* that any shared data read by handle_IPI will
375+
* be read after the ACK.
376+
*/
370377
handle_IPI(irqnr, regs);
371378
#else
372379
WARN_ONCE(true, "Unexpected SGI received!\n");

drivers/irqchip/irq-gic.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,14 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
344344
if (static_key_true(&supports_deactivate))
345345
writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
346346
#ifdef CONFIG_SMP
347+
/*
348+
* Ensure any shared data written by the CPU sending
349+
* the IPI is read after we've read the ACK register
350+
* on the GIC.
351+
*
352+
* Pairs with the write barrier in gic_raise_softirq
353+
*/
354+
smp_rmb();
347355
handle_IPI(irqnr, regs);
348356
#endif
349357
continue;

0 commit comments

Comments
 (0)