Merge branch 'irq/for-block' into irq/core
authorThomas Gleixner <tglx@linutronix.de>
Mon, 4 Jul 2016 10:26:05 +0000 (12:26 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Mon, 4 Jul 2016 10:26:05 +0000 (12:26 +0200)
Pull the irq affinity managing code which is in a seperate branch for block
developers to pull.

1  2 
drivers/irqchip/irq-mips-gic.c
include/linux/irq.h
include/linux/irqdomain.h
kernel/irq/internals.h
kernel/irq/irqdomain.c
kernel/irq/manage.c
kernel/irq/proc.c

@@@ -746,6 -746,12 +746,12 @@@ static int gic_irq_domain_alloc(struct 
                /* verify that it doesn't conflict with an IPI irq */
                if (test_bit(spec->hwirq, ipi_resrv))
                        return -EBUSY;
+               hwirq = GIC_SHARED_TO_HWIRQ(spec->hwirq);
+               return irq_domain_set_hwirq_and_chip(d, virq, hwirq,
+                                                    &gic_level_irq_controller,
+                                                    NULL);
        } else {
                base_hwirq = find_first_bit(ipi_resrv, gic_shared_intrs);
                if (base_hwirq == gic_shared_intrs) {
@@@ -867,10 -873,14 +873,14 @@@ static int gic_dev_domain_alloc(struct 
                                                    &gic_level_irq_controller,
                                                    NULL);
                if (ret)
-                       return ret;
+                       goto error;
        }
  
        return 0;
+ error:
+       irq_domain_free_irqs_parent(d, virq, nr_irqs);
+       return ret;
  }
  
  void gic_dev_domain_free(struct irq_domain *d, unsigned int virq,
@@@ -1032,14 -1042,12 +1042,14 @@@ static void __init __gic_init(unsigned 
                                               &gic_irq_domain_ops, NULL);
        if (!gic_irq_domain)
                panic("Failed to add GIC IRQ domain");
 +      gic_irq_domain->name = "mips-gic-irq";
  
        gic_dev_domain = irq_domain_add_hierarchy(gic_irq_domain, 0,
                                                  GIC_NUM_LOCAL_INTRS + gic_shared_intrs,
                                                  node, &gic_dev_domain_ops, NULL);
        if (!gic_dev_domain)
                panic("Failed to add GIC DEV domain");
 +      gic_dev_domain->name = "mips-gic-dev";
  
        gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain,
                                                  IRQ_DOMAIN_FLAG_IPI_PER_CPU,
        if (!gic_ipi_domain)
                panic("Failed to add GIC IPI domain");
  
 +      gic_ipi_domain->name = "mips-gic-ipi";
        gic_ipi_domain->bus_token = DOMAIN_BUS_IPI;
  
        if (node &&
diff --combined include/linux/irq.h
@@@ -197,6 -197,7 +197,7 @@@ struct irq_data 
   * IRQD_IRQ_INPROGRESS                - In progress state of the interrupt
   * IRQD_WAKEUP_ARMED          - Wakeup mode armed
   * IRQD_FORWARDED_TO_VCPU     - The interrupt is forwarded to a VCPU
+  * IRQD_AFFINITY_MANAGED      - Affinity is auto-managed by the kernel
   */
  enum {
        IRQD_TRIGGER_MASK               = 0xf,
        IRQD_IRQ_INPROGRESS             = (1 << 18),
        IRQD_WAKEUP_ARMED               = (1 << 19),
        IRQD_FORWARDED_TO_VCPU          = (1 << 20),
+       IRQD_AFFINITY_MANAGED           = (1 << 21),
  };
  
  #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
@@@ -305,6 -307,11 +307,11 @@@ static inline void irqd_clr_forwarded_t
        __irqd_to_state(d) &= ~IRQD_FORWARDED_TO_VCPU;
  }
  
+ static inline bool irqd_affinity_is_managed(struct irq_data *d)
+ {
+       return __irqd_to_state(d) & IRQD_AFFINITY_MANAGED;
+ }
  #undef __irqd_to_state
  
  static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
  /**
   * struct irq_chip - hardware interrupt chip descriptor
   *
 + * @parent_device:    pointer to parent device for irqchip
   * @name:             name for /proc/interrupts
   * @irq_startup:      start up the interrupt (defaults to ->enable if NULL)
   * @irq_shutdown:     shut down the interrupt (defaults to ->disable if NULL)
   * @flags:            chip specific flags
   */
  struct irq_chip {
 +      struct device   *parent_device;
        const char      *name;
        unsigned int    (*irq_startup)(struct irq_data *data);
        void            (*irq_shutdown)(struct irq_data *data);
@@@ -484,15 -489,12 +491,15 @@@ extern void handle_fasteoi_irq(struct i
  extern void handle_edge_irq(struct irq_desc *desc);
  extern void handle_edge_eoi_irq(struct irq_desc *desc);
  extern void handle_simple_irq(struct irq_desc *desc);
 +extern void handle_untracked_irq(struct irq_desc *desc);
  extern void handle_percpu_irq(struct irq_desc *desc);
  extern void handle_percpu_devid_irq(struct irq_desc *desc);
  extern void handle_bad_irq(struct irq_desc *desc);
  extern void handle_nested_irq(unsigned int irq);
  
  extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
 +extern int irq_chip_pm_get(struct irq_data *data);
 +extern int irq_chip_pm_put(struct irq_data *data);
  #ifdef        CONFIG_IRQ_DOMAIN_HIERARCHY
  extern void irq_chip_enable_parent(struct irq_data *data);
  extern void irq_chip_disable_parent(struct irq_data *data);
@@@ -706,11 -708,11 +713,11 @@@ static inline struct cpumask *irq_data_
  unsigned int arch_dynirq_lower_bound(unsigned int from);
  
  int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
-               struct module *owner);
+                     struct module *owner, const struct cpumask *affinity);
  
  /* use macros to avoid needing export.h for THIS_MODULE */
  #define irq_alloc_descs(irq, from, cnt, node) \
-       __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE)
+       __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE, NULL)
  
  #define irq_alloc_desc(node)                  \
        irq_alloc_descs(-1, 0, 1, node)
@@@ -39,6 -39,7 +39,7 @@@ struct irq_domain
  struct of_device_id;
  struct irq_chip;
  struct irq_data;
+ struct cpumask;
  
  /* Number of irqs reserved for a legacy isa controller */
  #define NUM_ISA_INTERRUPTS    16
@@@ -217,7 -218,8 +218,8 @@@ extern struct irq_domain *irq_find_matc
                                                   enum irq_domain_bus_token bus_token);
  extern void irq_set_default_host(struct irq_domain *host);
  extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
-                                 irq_hw_number_t hwirq, int node);
+                                 irq_hw_number_t hwirq, int node,
+                                 const struct cpumask *affinity);
  
  static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
  {
@@@ -389,7 -391,7 +391,7 @@@ static inline struct irq_domain *irq_do
  
  extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
                                   unsigned int nr_irqs, int node, void *arg,
-                                  bool realloc);
+                                  bool realloc, const struct cpumask *affinity);
  extern void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs);
  extern void irq_domain_activate_irq(struct irq_data *irq_data);
  extern void irq_domain_deactivate_irq(struct irq_data *irq_data);
  static inline int irq_domain_alloc_irqs(struct irq_domain *domain,
                        unsigned int nr_irqs, int node, void *arg)
  {
-       return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false);
+       return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false,
+                                      NULL);
  }
  
  extern int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
@@@ -452,9 -455,6 +455,9 @@@ static inline int irq_domain_alloc_irqs
        return -1;
  }
  
 +static inline void irq_domain_free_irqs(unsigned int virq,
 +                                      unsigned int nr_irqs) { }
 +
  static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
  {
        return false;
diff --combined kernel/irq/internals.h
@@@ -7,7 -7,6 +7,7 @@@
   */
  #include <linux/irqdesc.h>
  #include <linux/kernel_stat.h>
 +#include <linux/pm_runtime.h>
  
  #ifdef CONFIG_SPARSE_IRQ
  # define IRQ_BITMAP_BITS      (NR_IRQS + 8196)
@@@ -84,7 -83,6 +84,7 @@@ extern void irq_mark_irq(unsigned int i
  
  extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr);
  
 +irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags);
  irqreturn_t handle_irq_event_percpu(struct irq_desc *desc);
  irqreturn_t handle_irq_event(struct irq_desc *desc);
  
@@@ -107,6 -105,8 +107,8 @@@ static inline void unregister_handler_p
                                           struct irqaction *action) { }
  #endif
  
+ extern bool irq_can_set_affinity_usr(unsigned int irq);
  extern int irq_select_affinity_usr(unsigned int irq, struct cpumask *mask);
  
  extern void irq_set_thread_affinity(struct irq_desc *desc);
diff --combined kernel/irq/irqdomain.c
@@@ -481,7 -481,7 +481,7 @@@ unsigned int irq_create_mapping(struct 
        }
  
        /* Allocate a virtual interrupt number */
-       virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node));
+       virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), NULL);
        if (virq <= 0) {
                pr_debug("-> virq allocation failed\n");
                return 0;
@@@ -567,7 -567,6 +567,7 @@@ static void of_phandle_args_to_fwspec(s
  unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
  {
        struct irq_domain *domain;
 +      struct irq_data *irq_data;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
        int virq;
        if (irq_domain_translate(domain, fwspec, &hwirq, &type))
                return 0;
  
 -      if (irq_domain_is_hierarchy(domain)) {
 +      /*
 +       * WARN if the irqchip returns a type with bits
 +       * outside the sense mask set and clear these bits.
 +       */
 +      if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
 +              type &= IRQ_TYPE_SENSE_MASK;
 +
 +      /*
 +       * If we've already configured this interrupt,
 +       * don't do it again, or hell will break loose.
 +       */
 +      virq = irq_find_mapping(domain, hwirq);
 +      if (virq) {
                /*
 -               * If we've already configured this interrupt,
 -               * don't do it again, or hell will break loose.
 +               * If the trigger type is not specified or matches the
 +               * current trigger type then we are done so return the
 +               * interrupt number.
                 */
 -              virq = irq_find_mapping(domain, hwirq);
 -              if (virq)
 +              if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
                        return virq;
  
 +              /*
 +               * If the trigger type has not been set yet, then set
 +               * it now and return the interrupt number.
 +               */
 +              if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
 +                      irq_data = irq_get_irq_data(virq);
 +                      if (!irq_data)
 +                              return 0;
 +
 +                      irqd_set_trigger_type(irq_data, type);
 +                      return virq;
 +              }
 +
 +              pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
 +                      hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
 +              return 0;
 +      }
 +
 +      if (irq_domain_is_hierarchy(domain)) {
                virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
                if (virq <= 0)
                        return 0;
                        return virq;
        }
  
 -      /* Set type if specified and different than the current one */
 -      if (type != IRQ_TYPE_NONE &&
 -          type != irq_get_trigger_type(virq))
 -              irq_set_irq_type(virq, type);
 +      irq_data = irq_get_irq_data(virq);
 +      if (!irq_data) {
 +              if (irq_domain_is_hierarchy(domain))
 +                      irq_domain_free_irqs(virq, 1);
 +              else
 +                      irq_dispose_mapping(virq);
 +              return 0;
 +      }
 +
 +      /* Store trigger type */
 +      irqd_set_trigger_type(irq_data, type);
 +
        return virq;
  }
  EXPORT_SYMBOL_GPL(irq_create_fwspec_mapping);
@@@ -680,12 -640,8 +680,12 @@@ void irq_dispose_mapping(unsigned int v
        if (WARN_ON(domain == NULL))
                return;
  
 -      irq_domain_disassociate(domain, virq);
 -      irq_free_desc(virq);
 +      if (irq_domain_is_hierarchy(domain)) {
 +              irq_domain_free_irqs(virq, 1);
 +      } else {
 +              irq_domain_disassociate(domain, virq);
 +              irq_free_desc(virq);
 +      }
  }
  EXPORT_SYMBOL_GPL(irq_dispose_mapping);
  
@@@ -879,19 -835,23 +879,23 @@@ const struct irq_domain_ops irq_domain_
  EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
  
  int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq,
-                          int node)
+                          int node, const struct cpumask *affinity)
  {
        unsigned int hint;
  
        if (virq >= 0) {
-               virq = irq_alloc_descs(virq, virq, cnt, node);
+               virq = __irq_alloc_descs(virq, virq, cnt, node, THIS_MODULE,
+                                        affinity);
        } else {
                hint = hwirq % nr_irqs;
                if (hint == 0)
                        hint++;
-               virq = irq_alloc_descs_from(hint, cnt, node);
-               if (virq <= 0 && hint > 1)
-                       virq = irq_alloc_descs_from(1, cnt, node);
+               virq = __irq_alloc_descs(-1, hint, cnt, node, THIS_MODULE,
+                                        affinity);
+               if (virq <= 0 && hint > 1) {
+                       virq = __irq_alloc_descs(-1, 1, cnt, node, THIS_MODULE,
+                                                affinity);
+               }
        }
  
        return virq;
@@@ -1204,6 -1164,7 +1208,7 @@@ int irq_domain_alloc_irqs_recursive(str
   * @node:     NUMA node id for memory allocation
   * @arg:      domain specific argument
   * @realloc:  IRQ descriptors have already been allocated if true
+  * @affinity: Optional irq affinity mask for multiqueue devices
   *
   * Allocate IRQ numbers and initialized all data structures to support
   * hierarchy IRQ domains.
   */
  int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
                            unsigned int nr_irqs, int node, void *arg,
-                           bool realloc)
+                           bool realloc, const struct cpumask *affinity)
  {
        int i, ret, virq;
  
        if (realloc && irq_base >= 0) {
                virq = irq_base;
        } else {
-               virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node);
+               virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node,
+                                             affinity);
                if (virq < 0) {
                        pr_debug("cannot allocate IRQ(base %d, count %d)\n",
                                 irq_base, nr_irqs);
diff --combined kernel/irq/manage.c
@@@ -115,12 -115,12 +115,12 @@@ EXPORT_SYMBOL(synchronize_irq)
  #ifdef CONFIG_SMP
  cpumask_var_t irq_default_affinity;
  
- static int __irq_can_set_affinity(struct irq_desc *desc)
+ static bool __irq_can_set_affinity(struct irq_desc *desc)
  {
        if (!desc || !irqd_can_balance(&desc->irq_data) ||
            !desc->irq_data.chip || !desc->irq_data.chip->irq_set_affinity)
-               return 0;
-       return 1;
+               return false;
+       return true;
  }
  
  /**
@@@ -133,6 -133,21 +133,21 @@@ int irq_can_set_affinity(unsigned int i
        return __irq_can_set_affinity(irq_to_desc(irq));
  }
  
+ /**
+  * irq_can_set_affinity_usr - Check if affinity of a irq can be set from user space
+  * @irq:      Interrupt to check
+  *
+  * Like irq_can_set_affinity() above, but additionally checks for the
+  * AFFINITY_MANAGED flag.
+  */
+ bool irq_can_set_affinity_usr(unsigned int irq)
+ {
+       struct irq_desc *desc = irq_to_desc(irq);
+       return __irq_can_set_affinity(desc) &&
+               !irqd_affinity_is_managed(&desc->irq_data);
+ }
  /**
   *    irq_set_thread_affinity - Notify irq threads to adjust affinity
   *    @desc:          irq descriptor which has affitnity changed
@@@ -338,10 -353,11 +353,11 @@@ static int setup_affinity(struct irq_de
                return 0;
  
        /*
-        * Preserve an userspace affinity setup, but make sure that
-        * one of the targets is online.
+        * Preserve the managed affinity setting and an userspace affinity
+        * setup, but make sure that one of the targets is online.
         */
-       if (irqd_has_set(&desc->irq_data, IRQD_AFFINITY_SET)) {
+       if (irqd_affinity_is_managed(&desc->irq_data) ||
+           irqd_has_set(&desc->irq_data, IRQD_AFFINITY_SET)) {
                if (cpumask_intersects(desc->irq_common_data.affinity,
                                       cpu_online_mask))
                        set = desc->irq_common_data.affinity;
@@@ -1116,13 -1132,6 +1132,13 @@@ __setup_irq(unsigned int irq, struct ir
  
        new->irq = irq;
  
 +      /*
 +       * If the trigger type is not specified by the caller,
 +       * then use the default for this interrupt.
 +       */
 +      if (!(new->flags & IRQF_TRIGGER_MASK))
 +              new->flags |= irqd_get_trigger_type(&desc->irq_data);
 +
        /*
         * Check whether the interrupt nests into another interrupt
         * thread.
@@@ -1416,18 -1425,10 +1432,18 @@@ int setup_irq(unsigned int irq, struct 
  
        if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
                return -EINVAL;
 +
 +      retval = irq_chip_pm_get(&desc->irq_data);
 +      if (retval < 0)
 +              return retval;
 +
        chip_bus_lock(desc);
        retval = __setup_irq(irq, desc, act);
        chip_bus_sync_unlock(desc);
  
 +      if (retval)
 +              irq_chip_pm_put(&desc->irq_data);
 +
        return retval;
  }
  EXPORT_SYMBOL_GPL(setup_irq);
@@@ -1521,7 -1522,6 +1537,7 @@@ static struct irqaction *__free_irq(uns
                }
        }
  
 +      irq_chip_pm_put(&desc->irq_data);
        module_put(desc->owner);
        kfree(action->secondary);
        return action;
@@@ -1664,16 -1664,11 +1680,16 @@@ int request_threaded_irq(unsigned int i
        action->name = devname;
        action->dev_id = dev_id;
  
 +      retval = irq_chip_pm_get(&desc->irq_data);
 +      if (retval < 0)
 +              return retval;
 +
        chip_bus_lock(desc);
        retval = __setup_irq(irq, desc, action);
        chip_bus_sync_unlock(desc);
  
        if (retval) {
 +              irq_chip_pm_put(&desc->irq_data);
                kfree(action->secondary);
                kfree(action);
        }
@@@ -1751,14 -1746,7 +1767,14 @@@ void enable_percpu_irq(unsigned int irq
        if (!desc)
                return;
  
 +      /*
 +       * If the trigger type is not specified by the caller, then
 +       * use the default for this interrupt.
 +       */
        type &= IRQ_TYPE_SENSE_MASK;
 +      if (type == IRQ_TYPE_NONE)
 +              type = irqd_get_trigger_type(&desc->irq_data);
 +
        if (type != IRQ_TYPE_NONE) {
                int ret;
  
@@@ -1850,7 -1838,6 +1866,7 @@@ static struct irqaction *__free_percpu_
  
        unregister_handler_proc(irq, action);
  
 +      irq_chip_pm_put(&desc->irq_data);
        module_put(desc->owner);
        return action;
  
@@@ -1913,18 -1900,10 +1929,18 @@@ int setup_percpu_irq(unsigned int irq, 
  
        if (!desc || !irq_settings_is_per_cpu_devid(desc))
                return -EINVAL;
 +
 +      retval = irq_chip_pm_get(&desc->irq_data);
 +      if (retval < 0)
 +              return retval;
 +
        chip_bus_lock(desc);
        retval = __setup_irq(irq, desc, act);
        chip_bus_sync_unlock(desc);
  
 +      if (retval)
 +              irq_chip_pm_put(&desc->irq_data);
 +
        return retval;
  }
  
@@@ -1968,18 -1947,12 +1984,18 @@@ int request_percpu_irq(unsigned int irq
        action->name = devname;
        action->percpu_dev_id = dev_id;
  
 +      retval = irq_chip_pm_get(&desc->irq_data);
 +      if (retval < 0)
 +              return retval;
 +
        chip_bus_lock(desc);
        retval = __setup_irq(irq, desc, action);
        chip_bus_sync_unlock(desc);
  
 -      if (retval)
 +      if (retval) {
 +              irq_chip_pm_put(&desc->irq_data);
                kfree(action);
 +      }
  
        return retval;
  }
diff --combined kernel/irq/proc.c
@@@ -96,7 -96,7 +96,7 @@@ static ssize_t write_irq_affinity(int t
        cpumask_var_t new_value;
        int err;
  
-       if (!irq_can_set_affinity(irq) || no_irq_affinity)
+       if (!irq_can_set_affinity_usr(irq) || no_irq_affinity)
                return -EIO;
  
        if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
@@@ -311,6 -311,7 +311,6 @@@ void register_handler_proc(unsigned in
                                        !name_unique(irq, action))
                return;
  
 -      memset(name, 0, MAX_NAMELEN);
        snprintf(name, MAX_NAMELEN, "%s", action->name);
  
        /* create /proc/irq/1234/handler/ */
@@@ -339,6 -340,7 +339,6 @@@ void register_irq_proc(unsigned int irq
        if (desc->dir)
                goto out_unlock;
  
 -      memset(name, 0, MAX_NAMELEN);
        sprintf(name, "%d", irq);
  
        /* create /proc/irq/1234 */
@@@ -384,6 -386,7 +384,6 @@@ void unregister_irq_proc(unsigned int i
  #endif
        remove_proc_entry("spurious", desc->dir);
  
 -      memset(name, 0, MAX_NAMELEN);
        sprintf(name, "%u", irq);
        remove_proc_entry(name, root_irq_dir);
  }
@@@ -418,8 -421,12 +418,8 @@@ void init_irq_proc(void
        /*
         * Create entries for all existing IRQs.
         */
 -      for_each_irq_desc(irq, desc) {
 -              if (!desc)
 -                      continue;
 -
 +      for_each_irq_desc(irq, desc)
                register_irq_proc(irq, desc);
 -      }
  }
  
  #ifdef CONFIG_GENERIC_IRQ_SHOW