Commit 7778c4b27cbe ("irqchip: mips-gic: Use pcpu_masks to avoid reading
GIC_SH_MASK*") adjusted the way we handle masking interrupts to set &
clear the interrupt's bit in each pcpu_mask. This allows us to avoid
needing to read the GIC mask registers and perform a bitwise and of
their values with the pending & pcpu_masks.

Unfortunately this didn't quite work for IPIs, which were mapped to a
particular CPU/VP during initialisation but never set the affinity or
effective_affinity fields of their struct irq_desc. This led to them
losing their affinity when gic_unmask_irq() was called for them, and
they'd all become affine to cpu0.

Fix this by:

 1) Setting the effective affinity of interrupts in
    gic_shared_irq_domain_map(), which is where we actually map an
    interrupt to a CPU/VP. This ensures that the effective affinity mask
    is always valid, not just after explicitly setting affinity.

 2) Using an interrupt's effective affinity when unmasking it, which
    prevents gic_unmask_irq() from unintentionally changing which
    pcpu_mask includes an interrupt.

Signed-off-by: Paul Burton <[email protected]>
Fixes: 7778c4b27cbe ("irqchip: mips-gic: Use pcpu_masks to avoid reading 
GIC_SH_MASK*")
Cc: Jason Cooper <[email protected]>
Cc: Marc Zyngier <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: [email protected]

---

 drivers/irqchip/irq-mips-gic.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index 0022b31ad2c5..c90976d7e53c 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -175,14 +175,13 @@ static void gic_mask_irq(struct irq_data *d)
 
 static void gic_unmask_irq(struct irq_data *d)
 {
-       struct cpumask *affinity = irq_data_get_affinity_mask(d);
        unsigned int intr = GIC_HWIRQ_TO_SHARED(d->hwirq);
        unsigned int cpu;
 
        write_gic_smask(intr);
 
        gic_clear_pcpu_masks(intr);
-       cpu = cpumask_first_and(affinity, cpu_online_mask);
+       cpu = cpumask_first(irq_data_get_effective_affinity_mask(d));
        set_bit(intr, per_cpu_ptr(pcpu_masks, cpu));
 }
 
@@ -420,13 +419,17 @@ static int gic_shared_irq_domain_map(struct irq_domain 
*d, unsigned int virq,
                                     irq_hw_number_t hw, unsigned int cpu)
 {
        int intr = GIC_HWIRQ_TO_SHARED(hw);
+       struct irq_data *data;
        unsigned long flags;
 
+       data = irq_get_irq_data(virq);
+
        spin_lock_irqsave(&gic_lock, flags);
        write_gic_map_pin(intr, GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin);
        write_gic_map_vp(intr, BIT(mips_cm_vp_id(cpu)));
        gic_clear_pcpu_masks(intr);
        set_bit(intr, per_cpu_ptr(pcpu_masks, cpu));
+       irq_data_update_effective_affinity(data, cpumask_of(cpu));
        spin_unlock_irqrestore(&gic_lock, flags);
 
        return 0;
-- 
2.14.1

Reply via email to