genirq: Allow fwnode to carry name information only
authorThomas Gleixner <tglx@linutronix.de>
Mon, 19 Jun 2017 23:37:05 +0000 (01:37 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 22 Jun 2017 16:21:08 +0000 (18:21 +0200)
In order to provide proper debug interface it's required to have domain
names available when the domain is added. Non fwnode based architectures
like x86 have no way to do so.

It's not possible to use domain ops or host data for this as domain ops
might be the same for several instances, but the names have to be unique.

Extend the irqchip fwnode to allow transporting the domain name. If no node
is supplied, create a 'unknown-N' placeholder.

Warn if an invalid node is supplied and treat it like no node. This happens
e.g. with i2 devices on x86 which hand in an ACPI type node which has no
interface for retrieving the name.

[ Folded a fix from Marc to make DT name parsing work ]

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Keith Busch <keith.busch@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Christoph Hellwig <hch@lst.de>
Link: http://lkml.kernel.org/r/20170619235443.588784933@linutronix.de
include/linux/irqdomain.h
kernel/irq/irqdomain.c

index 9f3616085423cfca654264a4f5b9fed022431997..9cf32a2fbe692d700d25bd13e4ca5d727258d456 100644 (file)
@@ -189,6 +189,9 @@ enum {
        /* Irq domain implements MSI remapping */
        IRQ_DOMAIN_FLAG_MSI_REMAP       = (1 << 5),
 
+       /* Irq domain name was allocated in __irq_domain_add() */
+       IRQ_DOMAIN_NAME_ALLOCATED       = (1 << 6),
+
        /*
         * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
         * for implementation specific purposes and ignored by the
@@ -203,7 +206,33 @@ static inline struct device_node *irq_domain_get_of_node(struct irq_domain *d)
 }
 
 #ifdef CONFIG_IRQ_DOMAIN
-struct fwnode_handle *irq_domain_alloc_fwnode(void *data);
+struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
+                                               const char *name, void *data);
+
+enum {
+       IRQCHIP_FWNODE_REAL,
+       IRQCHIP_FWNODE_NAMED,
+       IRQCHIP_FWNODE_NAMED_ID,
+};
+
+static inline
+struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name)
+{
+       return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL);
+}
+
+static inline
+struct fwnode_handle *irq_domain_alloc_named_id_fwnode(const char *name, int id)
+{
+       return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name,
+                                        NULL);
+}
+
+static inline struct fwnode_handle *irq_domain_alloc_fwnode(void *data)
+{
+       return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, data);
+}
+
 void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
 struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    irq_hw_number_t hwirq_max, int direct_max,
index 70b9da72018ba0163d7df463f6847621e56636be..e1b925bea205816f06075ec0ed9d8b51a612ffa5 100644 (file)
@@ -26,39 +26,61 @@ static struct irq_domain *irq_default_domain;
 static void irq_domain_check_hierarchy(struct irq_domain *domain);
 
 struct irqchip_fwid {
-       struct fwnode_handle fwnode;
-       char *name;
-       void *data;
+       struct fwnode_handle    fwnode;
+       unsigned int            type;
+       char                    *name;
+       void                    *data;
 };
 
 /**
  * irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for
  *                           identifying an irq domain
- * @data: optional user-provided data
+ * @type:      Type of irqchip_fwnode. See linux/irqdomain.h
+ * @name:      Optional user provided domain name
+ * @id:                Optional user provided id if name != NULL
+ * @data:      Optional user-provided data
  *
- * Allocate a struct device_node, and return a poiner to the embedded
+ * Allocate a struct irqchip_fwid, and return a poiner to the embedded
  * fwnode_handle (or NULL on failure).
+ *
+ * Note: The types IRQCHIP_FWNODE_NAMED and IRQCHIP_FWNODE_NAMED_ID are
+ * solely to transport name information to irqdomain creation code. The
+ * node is not stored. For other types the pointer is kept in the irq
+ * domain struct.
  */
-struct fwnode_handle *irq_domain_alloc_fwnode(void *data)
+struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
+                                               const char *name, void *data)
 {
        struct irqchip_fwid *fwid;
-       char *name;
+       char *n;
 
        fwid = kzalloc(sizeof(*fwid), GFP_KERNEL);
-       name = kasprintf(GFP_KERNEL, "irqchip@%p", data);
 
-       if (!fwid || !name) {
+       switch (type) {
+       case IRQCHIP_FWNODE_NAMED:
+               n = kasprintf(GFP_KERNEL, "%s", name);
+               break;
+       case IRQCHIP_FWNODE_NAMED_ID:
+               n = kasprintf(GFP_KERNEL, "%s-%d", name, id);
+               break;
+       default:
+               n = kasprintf(GFP_KERNEL, "irqchip@%p", data);
+               break;
+       }
+
+       if (!fwid || !n) {
                kfree(fwid);
-               kfree(name);
+               kfree(n);
                return NULL;
        }
 
-       fwid->name = name;
+       fwid->type = type;
+       fwid->name = n;
        fwid->data = data;
        fwid->fwnode.type = FWNODE_IRQCHIP;
        return &fwid->fwnode;
 }
-EXPORT_SYMBOL_GPL(irq_domain_alloc_fwnode);
+EXPORT_SYMBOL_GPL(__irq_domain_alloc_fwnode);
 
 /**
  * irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle
@@ -97,20 +119,75 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    void *host_data)
 {
        struct device_node *of_node = to_of_node(fwnode);
+       struct irqchip_fwid *fwid;
        struct irq_domain *domain;
 
+       static atomic_t unknown_domains;
+
        domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
                              GFP_KERNEL, of_node_to_nid(of_node));
        if (WARN_ON(!domain))
                return NULL;
 
+       if (fwnode && is_fwnode_irqchip(fwnode)) {
+               fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
+
+               switch (fwid->type) {
+               case IRQCHIP_FWNODE_NAMED:
+               case IRQCHIP_FWNODE_NAMED_ID:
+                       domain->name = kstrdup(fwid->name, GFP_KERNEL);
+                       if (!domain->name) {
+                               kfree(domain);
+                               return NULL;
+                       }
+                       domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
+                       break;
+               default:
+                       domain->fwnode = fwnode;
+                       domain->name = fwid->name;
+                       break;
+               }
+       } else if (of_node) {
+               char *name;
+
+               /*
+                * DT paths contain '/', which debugfs is legitimately
+                * unhappy about. Replace them with ':', which does
+                * the trick and is not as offensive as '\'...
+                */
+               name = kstrdup(of_node_full_name(of_node), GFP_KERNEL);
+               if (!name) {
+                       kfree(domain);
+                       return NULL;
+               }
+
+               strreplace(name, '/', ':');
+
+               domain->name = name;
+               domain->fwnode = fwnode;
+               domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
+       }
+
+       if (!domain->name) {
+               if (fwnode) {
+                       pr_err("Invalid fwnode type (%d) for irqdomain\n",
+                              fwnode->type);
+               }
+               domain->name = kasprintf(GFP_KERNEL, "unknown-%d",
+                                        atomic_inc_return(&unknown_domains));
+               if (!domain->name) {
+                       kfree(domain);
+                       return NULL;
+               }
+               domain->flags |= IRQ_DOMAIN_NAME_ALLOCATED;
+       }
+
        of_node_get(of_node);
 
        /* Fill structure */
        INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
        domain->ops = ops;
        domain->host_data = host_data;
-       domain->fwnode = fwnode;
        domain->hwirq_max = hwirq_max;
        domain->revmap_size = size;
        domain->revmap_direct_max_irq = direct_max;
@@ -152,6 +229,8 @@ void irq_domain_remove(struct irq_domain *domain)
        pr_debug("Removed domain %s\n", domain->name);
 
        of_node_put(irq_domain_get_of_node(domain));
+       if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
+               kfree(domain->name);
        kfree(domain);
 }
 EXPORT_SYMBOL_GPL(irq_domain_remove);