irqchip/mips-gic: Get rid of the reliance on irq_cpu_online()
authorMarc Zyngier <maz@kernel.org>
Thu, 21 Oct 2021 17:04:13 +0000 (18:04 +0100)
committerMarc Zyngier <maz@kernel.org>
Tue, 26 Oct 2021 10:19:38 +0000 (11:19 +0100)
The MIPS GIC driver uses irq_cpu_online() to go and program the
per-CPU interrupts. However, this method iterates over all IRQs
in the system, despite only 3 per-CPU interrupts being of interest.

Let's be terribly bold and do the iteration ourselves. To ensure
mutual exclusion, hold the gic_lock spinlock that is otherwise
taken while dealing with these interrupts.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Reviewed-by: Serge Semin <fancer.lancer@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Tested-by: Serge Semin <fancer.lancer@gmail.com>
Link: https://lore.kernel.org/r/20211021170414.3341522-3-maz@kernel.org
drivers/irqchip/irq-mips-gic.c

index 54c7092..d02b05a 100644 (file)
@@ -381,24 +381,35 @@ static void gic_unmask_local_irq_all_vpes(struct irq_data *d)
        spin_unlock_irqrestore(&gic_lock, flags);
 }
 
-static void gic_all_vpes_irq_cpu_online(struct irq_data *d)
+static void gic_all_vpes_irq_cpu_online(void)
 {
-       struct gic_all_vpes_chip_data *cd;
-       unsigned int intr;
+       static const unsigned int local_intrs[] = {
+               GIC_LOCAL_INT_TIMER,
+               GIC_LOCAL_INT_PERFCTR,
+               GIC_LOCAL_INT_FDC,
+       };
+       unsigned long flags;
+       int i;
 
-       intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
-       cd = irq_data_get_irq_chip_data(d);
+       spin_lock_irqsave(&gic_lock, flags);
 
-       write_gic_vl_map(mips_gic_vx_map_reg(intr), cd->map);
-       if (cd->mask)
-               write_gic_vl_smask(BIT(intr));
+       for (i = 0; i < ARRAY_SIZE(local_intrs); i++) {
+               unsigned int intr = local_intrs[i];
+               struct gic_all_vpes_chip_data *cd;
+
+               cd = &gic_all_vpes_chip_data[intr];
+               write_gic_vl_map(mips_gic_vx_map_reg(intr), cd->map);
+               if (cd->mask)
+                       write_gic_vl_smask(BIT(intr));
+       }
+
+       spin_unlock_irqrestore(&gic_lock, flags);
 }
 
 static struct irq_chip gic_all_vpes_local_irq_controller = {
        .name                   = "MIPS GIC Local",
        .irq_mask               = gic_mask_local_irq_all_vpes,
        .irq_unmask             = gic_unmask_local_irq_all_vpes,
-       .irq_cpu_online         = gic_all_vpes_irq_cpu_online,
 };
 
 static void __gic_irq_dispatch(void)
@@ -477,6 +488,10 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
        intr = GIC_HWIRQ_TO_LOCAL(hwirq);
        map = GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin;
 
+       /*
+        * If adding support for more per-cpu interrupts, keep the the
+        * array in gic_all_vpes_irq_cpu_online() in sync.
+        */
        switch (intr) {
        case GIC_LOCAL_INT_TIMER:
                /* CONFIG_MIPS_CMP workaround (see __gic_init) */
@@ -663,8 +678,8 @@ static int gic_cpu_startup(unsigned int cpu)
        /* Clear all local IRQ masks (ie. disable all local interrupts) */
        write_gic_vl_rmask(~0);
 
-       /* Invoke irq_cpu_online callbacks to enable desired interrupts */
-       irq_cpu_online();
+       /* Enable desired interrupts */
+       gic_all_vpes_irq_cpu_online();
 
        return 0;
 }