Merge tag 'irq-core-2022-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 23 May 2022 23:58:49 +0000 (16:58 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 23 May 2022 23:58:49 +0000 (16:58 -0700)
Pull interrupt handling updates from Thomas Gleixner:
 "Core code:

   - Make the managed interrupts more robust by shutting them down in
     the core code when the assigned affinity mask does not contain
     online CPUs.

   - Make the irq simulator chip work on RT

   - A small set of cpumask and power manageent cleanups

  Drivers:

   - A set of changes which mark GPIO interrupt chips immutable to
     prevent the GPIO subsystem from modifying it under the hood. This
     provides the necessary infrastructure and converts a set of GPIO
     and pinctrl drivers over.

   - A set of changes to make the pseudo-NMI handling for GICv3 more
     robust: a missing barrier and consistent handling of the priority
     mask.

   - Another set of GICv3 improvements and fixes, but nothing
     outstanding

   - The usual set of improvements and cleanups all over the place

   - No new irqchip drivers and not even a new device tree binding!
     100+ interrupt chips are truly enough"

* tag 'irq-core-2022-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (39 commits)
  irqchip: Add Kconfig symbols for sunxi drivers
  irqchip/gic-v3: Fix priority mask handling
  irqchip/gic-v3: Refactor ISB + EOIR at ack time
  irqchip/gic-v3: Ensure pseudo-NMIs have an ISB between ack and handling
  genirq/irq_sim: Make the irq_work always run in hard irq context
  irqchip/armada-370-xp: Do not touch Performance Counter Overflow on A375, A38x, A39x
  irqchip/gic: Improved warning about incorrect type
  irqchip/csky: Return true/false (not 1/0) from bool functions
  irqchip/imx-irqsteer: Add runtime PM support
  irqchip/imx-irqsteer: Constify irq_chip struct
  irqchip/armada-370-xp: Enable MSI affinity configuration
  irqchip/aspeed-scu-ic: Fix irq_of_parse_and_map() return value
  irqchip/aspeed-i2c-ic: Fix irq_of_parse_and_map() return value
  irqchip/sun6i-r: Use NULL for chip_data
  irqchip/xtensa-mx: Fix initial IRQ affinity in non-SMP setup
  irqchip/exiu: Fix acknowledgment of edge triggered interrupts
  irqchip/gic-v3: Claim iomem resources
  dt-bindings: interrupt-controller: arm,gic-v3: Make the v2 compat requirements explicit
  irqchip/gic-v3: Relax polling of GIC{R,D}_CTLR.RWP
  irqchip/gic-v3: Detect LPI invalidation MMIO registers
  ...

1  2 
drivers/gpio/gpiolib.c
kernel/irq/manage.c

diff --combined drivers/gpio/gpiolib.c
@@@ -1433,19 -1433,21 +1433,21 @@@ static int gpiochip_to_irq(struct gpio_
        return irq_create_mapping(domain, offset);
  }
  
static int gpiochip_irq_reqres(struct irq_data *d)
+ int gpiochip_irq_reqres(struct irq_data *d)
  {
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
  
        return gpiochip_reqres_irq(gc, d->hwirq);
  }
+ EXPORT_SYMBOL(gpiochip_irq_reqres);
  
static void gpiochip_irq_relres(struct irq_data *d)
+ void gpiochip_irq_relres(struct irq_data *d)
  {
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
  
        gpiochip_relres_irq(gc, d->hwirq);
  }
+ EXPORT_SYMBOL(gpiochip_irq_relres);
  
  static void gpiochip_irq_mask(struct irq_data *d)
  {
@@@ -1485,6 -1487,11 +1487,11 @@@ static void gpiochip_set_irq_hooks(stru
  {
        struct irq_chip *irqchip = gc->irq.chip;
  
+       if (irqchip->flags & IRQCHIP_IMMUTABLE)
+               return;
+       chip_warn(gc, "not an immutable chip, please consider fixing it!\n");
        if (!irqchip->irq_request_resources &&
            !irqchip->irq_release_resources) {
                irqchip->irq_request_resources = gpiochip_irq_reqres;
@@@ -1601,6 -1608,8 +1608,6 @@@ static int gpiochip_add_irqchip(struct 
  
        gpiochip_set_irq_hooks(gc);
  
 -      acpi_gpiochip_request_interrupts(gc);
 -
        /*
         * Using barrier() here to prevent compiler from reordering
         * gc->irq.initialized before initialization of above
  
        gc->irq.initialized = true;
  
 +      acpi_gpiochip_request_interrupts(gc);
 +
        return 0;
  }
  
@@@ -1652,7 -1659,7 +1659,7 @@@ static void gpiochip_irqchip_remove(str
                irq_domain_remove(gc->irq.domain);
        }
  
-       if (irqchip) {
+       if (irqchip && !(irqchip->flags & IRQCHIP_IMMUTABLE)) {
                if (irqchip->irq_request_resources == gpiochip_irq_reqres) {
                        irqchip->irq_request_resources = NULL;
                        irqchip->irq_release_resources = NULL;
diff --combined kernel/irq/manage.c
@@@ -222,11 -222,16 +222,16 @@@ int irq_do_set_affinity(struct irq_dat
  {
        struct irq_desc *desc = irq_data_to_desc(data);
        struct irq_chip *chip = irq_data_get_irq_chip(data);
+       const struct cpumask  *prog_mask;
        int ret;
  
+       static DEFINE_RAW_SPINLOCK(tmp_mask_lock);
+       static struct cpumask tmp_mask;
        if (!chip || !chip->irq_set_affinity)
                return -EINVAL;
  
+       raw_spin_lock(&tmp_mask_lock);
        /*
         * If this is a managed interrupt and housekeeping is enabled on
         * it check whether the requested affinity mask intersects with
         */
        if (irqd_affinity_is_managed(data) &&
            housekeeping_enabled(HK_TYPE_MANAGED_IRQ)) {
-               const struct cpumask *hk_mask, *prog_mask;
-               static DEFINE_RAW_SPINLOCK(tmp_mask_lock);
-               static struct cpumask tmp_mask;
+               const struct cpumask *hk_mask;
  
                hk_mask = housekeeping_cpumask(HK_TYPE_MANAGED_IRQ);
  
-               raw_spin_lock(&tmp_mask_lock);
                cpumask_and(&tmp_mask, mask, hk_mask);
                if (!cpumask_intersects(&tmp_mask, cpu_online_mask))
                        prog_mask = mask;
                else
                        prog_mask = &tmp_mask;
-               ret = chip->irq_set_affinity(data, prog_mask, force);
-               raw_spin_unlock(&tmp_mask_lock);
        } else {
-               ret = chip->irq_set_affinity(data, mask, force);
+               prog_mask = mask;
        }
+       /*
+        * Make sure we only provide online CPUs to the irqchip,
+        * unless we are being asked to force the affinity (in which
+        * case we do as we are told).
+        */
+       cpumask_and(&tmp_mask, prog_mask, cpu_online_mask);
+       if (!force && !cpumask_empty(&tmp_mask))
+               ret = chip->irq_set_affinity(data, &tmp_mask, force);
+       else if (force)
+               ret = chip->irq_set_affinity(data, mask, force);
+       else
+               ret = -EINVAL;
+       raw_spin_unlock(&tmp_mask_lock);
        switch (ret) {
        case IRQ_SET_MASK_OK:
        case IRQ_SET_MASK_OK_DONE:
@@@ -1249,31 -1264,6 +1264,31 @@@ static void irq_wake_secondary(struct i
  }
  
  /*
 + * Internal function to notify that a interrupt thread is ready.
 + */
 +static void irq_thread_set_ready(struct irq_desc *desc,
 +                               struct irqaction *action)
 +{
 +      set_bit(IRQTF_READY, &action->thread_flags);
 +      wake_up(&desc->wait_for_threads);
 +}
 +
 +/*
 + * Internal function to wake up a interrupt thread and wait until it is
 + * ready.
 + */
 +static void wake_up_and_wait_for_irq_thread_ready(struct irq_desc *desc,
 +                                                struct irqaction *action)
 +{
 +      if (!action || !action->thread)
 +              return;
 +
 +      wake_up_process(action->thread);
 +      wait_event(desc->wait_for_threads,
 +                 test_bit(IRQTF_READY, &action->thread_flags));
 +}
 +
 +/*
   * Interrupt handler thread
   */
  static int irq_thread(void *data)
        irqreturn_t (*handler_fn)(struct irq_desc *desc,
                        struct irqaction *action);
  
 +      irq_thread_set_ready(desc, action);
 +
        sched_set_fifo(current);
  
        if (force_irqthreads() && test_bit(IRQTF_FORCED_THREAD,
@@@ -1710,6 -1698,8 +1725,6 @@@ __setup_irq(unsigned int irq, struct ir
        }
  
        if (!shared) {
 -              init_waitqueue_head(&desc->wait_for_threads);
 -
                /* Setup the type (level, edge polarity) if configured: */
                if (new->flags & IRQF_TRIGGER_MASK) {
                        ret = __irq_set_trigger(desc,
  
        irq_setup_timings(desc, new);
  
 -      /*
 -       * Strictly no need to wake it up, but hung_task complains
 -       * when no hard interrupt wakes the thread up.
 -       */
 -      if (new->thread)
 -              wake_up_process(new->thread);
 -      if (new->secondary)
 -              wake_up_process(new->secondary->thread);
 +      wake_up_and_wait_for_irq_thread_ready(desc, new);
 +      wake_up_and_wait_for_irq_thread_ready(desc, new->secondary);
  
        register_irq_proc(irq, desc);
        new->dir = NULL;