[PATCH] genirq: Fix generic irq chip locking in non-interrupt code

From: Marc Zyngier
Date: Thu Feb 21 2019 - 05:05:24 EST


A number of the irqchip methods can be called both in and out
of interrupt context (mask, unmask). If these methods are
using any form of locking, it is crucial to make this locking
interrupt safe, or risk a deadlock in an interrupt occurs during
the critical section.

The generic irqchip implementation doesn't obey the above rule.
Let's address it by changing all the callbacks that can be used
in non-interrupt context to using irqsafe locking.

Fixes: 7d8280624797 ("genirq: Implement a generic interrupt chip")
Signed-off-by: Marc Zyngier <marc.zyngier@xxxxxxx>
---
kernel/irq/generic-chip.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index e2999a070a99..a21c42c6db51 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -38,11 +38,12 @@ void irq_gc_mask_disable_reg(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
+ unsigned long flags;

- irq_gc_lock(gc);
+ irq_gc_lock_irqsave(gc, flags);
irq_reg_writel(gc, mask, ct->regs.disable);
*ct->mask_cache &= ~mask;
- irq_gc_unlock(gc);
+ irq_gc_unlock_irqrestore(gc, flags);
}

/**
@@ -57,11 +58,12 @@ void irq_gc_mask_set_bit(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
+ unsigned long flags;

- irq_gc_lock(gc);
+ irq_gc_lock_irqsave(gc, flags);
*ct->mask_cache |= mask;
irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
- irq_gc_unlock(gc);
+ irq_gc_unlock_irqrestore(gc, flags);
}
EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit);

@@ -77,11 +79,12 @@ void irq_gc_mask_clr_bit(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
+ unsigned long flags;

- irq_gc_lock(gc);
+ irq_gc_lock_irqsave(gc, flags);
*ct->mask_cache &= ~mask;
irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
- irq_gc_unlock(gc);
+ irq_gc_unlock_irqrestore(gc, flags);
}
EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit);

@@ -97,11 +100,12 @@ void irq_gc_unmask_enable_reg(struct irq_data *d)
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct irq_chip_type *ct = irq_data_get_chip_type(d);
u32 mask = d->mask;
+ unsigned long flags;

- irq_gc_lock(gc);
+ irq_gc_lock_irqsave(gc, flags);
irq_reg_writel(gc, mask, ct->regs.enable);
*ct->mask_cache |= mask;
- irq_gc_unlock(gc);
+ irq_gc_unlock_irqrestore(gc, flags);
}

/**
@@ -188,16 +192,17 @@ int irq_gc_set_wake(struct irq_data *d, unsigned int on)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
u32 mask = d->mask;
+ unsigned long flags;

if (!(mask & gc->wake_enabled))
return -EINVAL;

- irq_gc_lock(gc);
+ irq_gc_lock_irqsave(gc, flags);
if (on)
gc->wake_active |= mask;
else
gc->wake_active &= ~mask;
- irq_gc_unlock(gc);
+ irq_gc_unlock_irqrestore(gc, flags);
return 0;
}

--
2.20.1


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