Thread (80 messages) 80 messages, 14 authors, 2020-10-27

Re: [PATCH v3 08/16] irqchip/gic: Configure SGIs as standard interrupts

From: Marc Zyngier <maz@kernel.org>
Date: 2020-09-18 10:22:05
Also in: lkml

Hi James,

On Fri, 18 Sep 2020 10:58:45 +0100,
James Morse [off-list ref] wrote:
Hi Marc,

(CC: +Jon)

On 01/09/2020 15:43, Marc Zyngier wrote:
quoted
Change the way we deal with GIC SGIs by turning them into proper
IRQs, and calling into the arch code to register the interrupt range
instead of a callback.
Your comment "This only works because we don't nest SGIs..." on this
thread tripped some bad memories from adding the irq-stack. Softirq
causes us to nest irqs, but only once.


(I've messed with the below diff to remove the added stuff:)
quoted
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 4ffd62af888f..4be2b62f816f 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -335,31 +335,22 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
 		irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
 		irqnr = irqstat & GICC_IAR_INT_ID_MASK;
 
-		if (likely(irqnr > 15 && irqnr < 1020)) {
-			if (static_branch_likely(&supports_deactivate_key))
-				writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
-			isb();
-			handle_domain_irq(gic->domain, irqnr, regs);
-			continue;
-		}
-		if (irqnr < 16) {
 			writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
-			if (static_branch_likely(&supports_deactivate_key))
-				writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
-#ifdef CONFIG_SMP
-			/*
-			 * Ensure any shared data written by the CPU sending
-			 * the IPI is read after we've read the ACK register
-			 * on the GIC.
-			 *
-			 * Pairs with the write barrier in gic_raise_softirq
-			 */
 			smp_rmb();
-			handle_IPI(irqnr, regs);
If I read this right, previously we would EOI the interrupt before
calling handle_IPI().  Where as now with the version of this series
in your tree, we stuff the to-be-EOId value in a percpu variable,
which is only safe if these don't nest.

Hidden in irq_exit(), kernel/softirq.c::__irq_exit_rcu() has this:
|	preempt_count_sub(HARDIRQ_OFFSET);
|	if (!in_interrupt() && local_softirq_pending())
|		invoke_softirq();

The arch code doesn't raise the preempt counter by HARDIRQ, so once
__irq_exit_rcu() has dropped it, in_interrupt() returns false, and
we invoke_softirq().

invoke_softirq() -> __do_softirq() -> local_irq_enable()!

Fortunately, __do_softirq() raises the softirq count first using
__local_bh_disable_ip(), which in-interrupt() checks too, so this
can only happen once per IRQ.

Now the irq_exit() has moved from handle_IPI(), which ran after EOI,
into handle_domain_irq(), which runs before. I think its possible
SGIs nest, and the new percpu variable becomes corrupted.
I can't see how. The interrupt is active until we EOI/deactivate it,
and thus cannot be observed again by the CPU interface until this
happens.

Furthermore, irq_exit() in __handle_domain_irq() is *after* the EOI
anyway (generic_handle_irq_() directly calls the flow, which
immediately EOIs the interrupt). The only material change is that
irq_enter() happens before EOI. Is that what you are referring to?

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help