genirq/affinity: Make affinity setting if activated opt-in
authorThomas Gleixner <tglx@linutronix.de>
Fri, 24 Jul 2020 20:44:41 +0000 (22:44 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Aug 2020 07:48:23 +0000 (09:48 +0200)
commit f0c7baca180046824e07fc5f1326e83a8fd150c7 upstream.

John reported that on a RK3288 system the perf per CPU interrupts are all
affine to CPU0 and provided the analysis:

 "It looks like what happens is that because the interrupts are not per-CPU
  in the hardware, armpmu_request_irq() calls irq_force_affinity() while
  the interrupt is deactivated and then request_irq() with IRQF_PERCPU |
  IRQF_NOBALANCING.

  Now when irq_startup() runs with IRQ_STARTUP_NORMAL, it calls
  irq_setup_affinity() which returns early because IRQF_PERCPU and
  IRQF_NOBALANCING are set, leaving the interrupt on its original CPU."

This was broken by the recent commit which blocked interrupt affinity
setting in hardware before activation of the interrupt. While this works in
general, it does not work for this particular case. As contrary to the
initial analysis not all interrupt chip drivers implement an activate
callback, the safe cure is to make the deferred interrupt affinity setting
at activation time opt-in.

Implement the necessary core logic and make the two irqchip implementations
for which this is required opt-in. In hindsight this would have been the
right thing to do, but ...

Fixes: baedb87d1b53 ("genirq/affinity: Handle affinity setting on inactive interrupts correctly")
Reported-by: John Keeping <john@metanate.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Marc Zyngier <maz@kernel.org>
Acked-by: Marc Zyngier <maz@kernel.org>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/87blk4tzgm.fsf@nanos.tec.linutronix.de
[fllinden@amazon.com - backported to 4.14]
Signed-off-by: Frank van der Linden <fllinden@amazon.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/kernel/apic/vector.c
drivers/irqchip/irq-gic-v3-its.c
include/linux/irq.h
kernel/irq/manage.c

index 36cd34524ac197116dd412b0881d8c26016684e7..637cf4dfccc9ab31af983d7cfb812abe9f7540a8 100644 (file)
@@ -368,6 +368,10 @@ static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq,
                irq_data->chip = &lapic_controller;
                irq_data->chip_data = data;
                irq_data->hwirq = virq + i;
+
+               /* Don't invoke affinity setter on deactivated interrupts */
+               irqd_set_affinity_on_activate(irq_data);
+
                err = assign_irq_vector_policy(virq + i, node, data, info,
                                               irq_data);
                if (err) {
index 84b23d902d5b86875cd5be3d9fc858a5d4329665..1d2267c6d31aa02ce697ef817b76f9ecbe0901be 100644 (file)
@@ -2199,6 +2199,7 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 {
        msi_alloc_info_t *info = args;
        struct its_device *its_dev = info->scratchpad[0].ptr;
+       struct irq_data *irqd;
        irq_hw_number_t hwirq;
        int err;
        int i;
@@ -2214,7 +2215,9 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 
                irq_domain_set_hwirq_and_chip(domain, virq + i,
                                              hwirq + i, &its_irq_chip, its_dev);
-               irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq + i)));
+               irqd = irq_get_irq_data(virq + i);
+               irqd_set_single_target(irqd);
+               irqd_set_affinity_on_activate(irqd);
                pr_debug("ID:%d pID:%d vID:%d\n",
                         (int)(hwirq + i - its_dev->event_map.lpi_base),
                         (int)(hwirq + i), virq + i);
index 0d53626405bf85b263f4528d339e28ce547a2502..c08758a63d2684afd06b40c1477b3b16fbe0f261 100644 (file)
@@ -212,6 +212,8 @@ struct irq_data {
  *                               mask. Applies only to affinity managed irqs.
  * IRQD_SINGLE_TARGET          - IRQ allows only a single affinity target
  * IRQD_DEFAULT_TRIGGER_SET    - Expected trigger already been set
+ * IRQD_AFFINITY_ON_ACTIVATE   - Affinity is set on activation. Don't call
+ *                               irq_chip::irq_set_affinity() when deactivated.
  */
 enum {
        IRQD_TRIGGER_MASK               = 0xf,
@@ -233,6 +235,7 @@ enum {
        IRQD_MANAGED_SHUTDOWN           = (1 << 23),
        IRQD_SINGLE_TARGET              = (1 << 24),
        IRQD_DEFAULT_TRIGGER_SET        = (1 << 25),
+       IRQD_AFFINITY_ON_ACTIVATE       = (1 << 29),
 };
 
 #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
@@ -377,6 +380,15 @@ static inline bool irqd_is_managed_and_shutdown(struct irq_data *d)
        return __irqd_to_state(d) & IRQD_MANAGED_SHUTDOWN;
 }
 
+static inline void irqd_set_affinity_on_activate(struct irq_data *d)
+{
+       __irqd_to_state(d) |= IRQD_AFFINITY_ON_ACTIVATE;
+}
+
+static inline bool irqd_affinity_on_activate(struct irq_data *d)
+{
+       return __irqd_to_state(d) & IRQD_AFFINITY_ON_ACTIVATE;
+}
 #undef __irqd_to_state
 
 static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
index ce6fba4468141fc48e1e7cfd21b4d57cfd8db790..3193be58805c22790bdfbc82b6fcf8fd86f832f9 100644 (file)
@@ -221,12 +221,16 @@ static bool irq_set_affinity_deactivated(struct irq_data *data,
        struct irq_desc *desc = irq_data_to_desc(data);
 
        /*
+        * Handle irq chips which can handle affinity only in activated
+        * state correctly
+        *
         * If the interrupt is not yet activated, just store the affinity
         * mask and do not call the chip driver at all. On activation the
         * driver has to make sure anyway that the interrupt is in a
         * useable state so startup works.
         */
-       if (!IS_ENABLED(CONFIG_IRQ_DOMAIN_HIERARCHY) || irqd_is_activated(data))
+       if (!IS_ENABLED(CONFIG_IRQ_DOMAIN_HIERARCHY) ||
+           irqd_is_activated(data) || !irqd_affinity_on_activate(data))
                return false;
 
        cpumask_copy(desc->irq_common_data.affinity, mask);