2013-06-10 00:49:30

by Grant Likely

[permalink] [raw]
Subject: [RFC 00/10] Refactor irqdomain

I've done a bunch of refactoring work on the irq_domain infrastructure.
Some of these patches I've posted before, and some our brand new. The
goal of this is to greatly simplify how irq_domains work. With this
series, instead of there being multiple different types of irq domains,
each with different mapping rules, instead there is now only one time of
irq_domain that contains both kinds of map; the linear map for irqs
below a certain value, and the radix tree for large & sparse irq
controllers. As you can see from the following diffstat, the result is a
fair bit less code. It should make it easier to understand irqdomains
too.

arch/powerpc/platforms/cell/beat_interrupt.c | 2 +-
arch/powerpc/platforms/powermac/smp.c | 2 +-
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/irq-versatile-fpga.c | 104 ++++------
include/linux/irqdomain.h | 123 ++++++-----
kernel/irq/generic-chip.c | 6 +-
kernel/irq/irqdomain.c | 555
++++++++++++++++----------------------------------
7 files changed, 282 insertions(+), 511 deletions(-)

I've pushed this series out to my git server at the following branch:

git://git.secretlab.ca/git/linux irqdomain/next

It depends on the tip tree's irq/for-arm branch and also Linus' mainline
(they need to be merged). The branch above includes both.

I've tested this on ARM qemu models, but not much else. I'll test on
real hardware before pushing out, but I would appreciate anybody doing
additional testing, particularly on PowerPC and other non-ARM platforms.

Cheers,
g.


2013-06-10 00:49:34

by Grant Likely

[permalink] [raw]
Subject: [RFC 01/10] irqdomain: Relax failure path on setting up mappings

Commit 98aa468e, "irqdomain: Support for static IRQ mapping and
association" introduced an API for directly associating blocks of hwirqs
to linux irqs. However, if any irq in that block failed to map (say if
the mapping functions returns an error because the irq is already
mapped) then the whole thing will fail and roll back. This is probably
too aggressive since there are valid reasons why a mapping may fail.
ie. Firmware may have a particular IRQ marked as unusable.

This patch drops the error path out of irq_domain_associate(). If a
mapping fails, then it is simply skipped. There is no reason to fail the
entire allocation.

v2: Still output an information message on failed mappings and make sure
attempted mapping gets cleared out of the irq_data structure.

Signed-off-by: Grant Likely <[email protected]>
Cc: Paul Mundt <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Thomas Gleixner <[email protected]>
---
kernel/irq/irqdomain.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 20b677d..61d6d3c 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -464,23 +464,15 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
/*
* If map() returns -EPERM, this interrupt is protected
* by the firmware or some other service and shall not
- * be mapped.
- *
- * Since on some platforms we blindly try to map everything
- * we end up with a log full of backtraces.
- *
- * So instead, we silently fail on -EPERM, it is the
- * responsibility of the PIC driver to display a relevant
- * message if needed.
+ * be mapped. Don't bother telling the user about it.
*/
if (ret != -EPERM) {
- pr_err("irq-%i==>hwirq-0x%lx mapping failed: %d\n",
- virq, hwirq, ret);
- WARN_ON(1);
+ pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
+ of_node_full_name(domain->of_node), hwirq, virq, ret);
}
irq_data->domain = NULL;
irq_data->hwirq = 0;
- goto err_unmap;
+ continue;
}
}

--
1.8.1.2

2013-06-10 00:49:38

by Grant Likely

[permalink] [raw]
Subject: [RFC 03/10] irqdomain: Add a name field

This patch adds a name field to the irq_domain structure to help mere
mortals understand the mappings between irq domains and virqs. It also
converts a number of places that have open-coded some kind of fudging
an irqdomain name to use the new field. This means a more consistent
display of names in irq domain log messages and debugfs output.

Signed-off-by: Grant Likely <[email protected]>
---
include/linux/irqdomain.h | 1 +
kernel/irq/generic-chip.c | 1 +
kernel/irq/irqdomain.c | 19 ++++++-------------
3 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 6f06241..e5e513c 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -89,6 +89,7 @@ struct irq_domain_chip_generic;
*/
struct irq_domain {
struct list_head link;
+ const char *name;

/* type of reverse mapping_technique */
unsigned int revmap_type;
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index 95575d8..ca98cc5 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -305,6 +305,7 @@ int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
/* Calc pointer to the next generic chip */
tmp += sizeof(*gc) + num_ct * sizeof(struct irq_chip_type);
}
+ d->name = name;
return 0;
}
EXPORT_SYMBOL_GPL(irq_alloc_domain_generic_chips);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 1ac8cf4..b1b5e67 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -410,12 +410,15 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
*/
if (ret != -EPERM) {
pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
- of_node_full_name(domain->of_node), hwirq, virq, ret);
+ domain->name, hwirq, virq, ret);
}
irq_data->domain = NULL;
irq_data->hwirq = 0;
continue;
}
+ /* If not already assigned, give the domain the chip's name */
+ if (!domain->name && irq_data->chip)
+ domain->name = irq_data->chip->name;
}

switch (domain->revmap_type) {
@@ -708,8 +711,6 @@ static int virq_debug_show(struct seq_file *m, void *private)
{
unsigned long flags;
struct irq_desc *desc;
- const char *p;
- static const char none[] = "none";
void *data;
int i;

@@ -731,20 +732,12 @@ static int virq_debug_show(struct seq_file *m, void *private)
seq_printf(m, "0x%05lx ", desc->irq_data.hwirq);

chip = irq_desc_get_chip(desc);
- if (chip && chip->name)
- p = chip->name;
- else
- p = none;
- seq_printf(m, "%-15s ", p);
+ seq_printf(m, "%-15s ", (chip && chip->name) ? chip->name : "none");

data = irq_desc_get_chip_data(desc);
seq_printf(m, data ? "0x%p " : " %p ", data);

- if (desc->irq_data.domain)
- p = of_node_full_name(desc->irq_data.domain->of_node);
- else
- p = none;
- seq_printf(m, "%s\n", p);
+ seq_printf(m, "%s\n", desc->irq_data.domain->name);
}

raw_spin_unlock_irqrestore(&desc->lock, flags);
--
1.8.1.2

2013-06-10 00:49:43

by Grant Likely

[permalink] [raw]
Subject: [RFC 06/10] irqdomain: Clean up aftermath of irq_domain refactoring

After refactoring the irqdomain code, there are a number of API
functions that are merely empty wrappers around core code. Drop those
wrappers out of the C file and replace them with static inlines in the
header.

Signed-off-by: Grant Likely <[email protected]>
---
arch/powerpc/platforms/cell/beat_interrupt.c | 2 +-
arch/powerpc/platforms/powermac/smp.c | 2 +-
include/linux/irqdomain.h | 31 +++++--
kernel/irq/irqdomain.c | 127 ++++++++-------------------
4 files changed, 62 insertions(+), 100 deletions(-)

diff --git a/arch/powerpc/platforms/cell/beat_interrupt.c b/arch/powerpc/platforms/cell/beat_interrupt.c
index 8c6dc42..9e5dfbc 100644
--- a/arch/powerpc/platforms/cell/beat_interrupt.c
+++ b/arch/powerpc/platforms/cell/beat_interrupt.c
@@ -239,7 +239,7 @@ void __init beatic_init_IRQ(void)
ppc_md.get_irq = beatic_get_irq;

/* Allocate an irq host */
- beatic_host = irq_domain_add_nomap(NULL, 0, &beatic_pic_host_ops, NULL);
+ beatic_host = irq_domain_add_nomap(NULL, ~0, &beatic_pic_host_ops, NULL);
BUG_ON(beatic_host == NULL);
irq_set_default_host(beatic_host);
}
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c
index bdb738a..f921067 100644
--- a/arch/powerpc/platforms/powermac/smp.c
+++ b/arch/powerpc/platforms/powermac/smp.c
@@ -192,7 +192,7 @@ static int psurge_secondary_ipi_init(void)
{
int rc = -ENOMEM;

- psurge_host = irq_domain_add_nomap(NULL, 0, &psurge_host_ops, NULL);
+ psurge_host = irq_domain_add_nomap(NULL, ~0, &psurge_host_ops, NULL);

if (psurge_host)
psurge_secondary_virq = irq_create_direct_mapping(psurge_host);
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 51ef84a..fd4b26f 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -110,6 +110,10 @@ struct irq_domain {
};

#ifdef CONFIG_IRQ_DOMAIN
+struct irq_domain *__irq_domain_add(struct device_node *of_node,
+ int size, int direct_max,
+ const struct irq_domain_ops *ops,
+ void *host_data);
struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
unsigned int size,
unsigned int first_irq,
@@ -121,17 +125,30 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
irq_hw_number_t first_hwirq,
const struct irq_domain_ops *ops,
void *host_data);
-struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
+extern struct irq_domain *irq_find_host(struct device_node *node);
+extern void irq_set_default_host(struct irq_domain *host);
+
+/**
+ * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain.
+ * @of_node: pointer to interrupt controller's device tree node.
+ * @size: Number of interrupts in the domain.
+ * @ops: map/unmap domain callbacks
+ * @host_data: Controller private data pointer
+ */
+static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
unsigned int size,
const struct irq_domain_ops *ops,
- void *host_data);
-struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
+ void *host_data)
+{
+ return __irq_domain_add(of_node, size, 0, ops, host_data);
+}
+static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
unsigned int max_irq,
const struct irq_domain_ops *ops,
- void *host_data);
-extern struct irq_domain *irq_find_host(struct device_node *node);
-extern void irq_set_default_host(struct irq_domain *host);
-
+ void *host_data)
+{
+ return __irq_domain_add(of_node, 0, max_irq, ops, host_data);
+}
static inline struct irq_domain *irq_domain_add_legacy_isa(
struct device_node *of_node,
const struct irq_domain_ops *ops,
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index c38be78..e0db59e 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -23,8 +23,11 @@ static DEFINE_MUTEX(revmap_trees_mutex);
static struct irq_domain *irq_default_domain;

/**
- * irq_domain_alloc() - Allocate a new irq_domain data structure
+ * __irq_domain_add() - Allocate a new irq_domain data structure
* @of_node: optional device-tree node of the interrupt controller
+ * @size: Size of linear map; 0 for radix mapping only
+ * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
+ * direct mapping
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
@@ -32,10 +35,10 @@ static struct irq_domain *irq_default_domain;
* register allocated irq_domain with irq_domain_register(). Returns pointer
* to IRQ domain, or NULL on failure.
*/
-static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
- int size,
- const struct irq_domain_ops *ops,
- void *host_data)
+struct irq_domain *__irq_domain_add(struct device_node *of_node,
+ int size, int direct_max,
+ const struct irq_domain_ops *ops,
+ void *host_data)
{
struct irq_domain *domain;

@@ -50,23 +53,16 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
domain->host_data = host_data;
domain->of_node = of_node_get(of_node);
domain->revmap_size = size;
+ domain->revmap_direct_max_irq = direct_max;

- return domain;
-}
-
-static void irq_domain_free(struct irq_domain *domain)
-{
- of_node_put(domain->of_node);
- kfree(domain);
-}
-
-static void irq_domain_add(struct irq_domain *domain)
-{
mutex_lock(&irq_domain_mutex);
list_add(&domain->link, &irq_domain_list);
mutex_unlock(&irq_domain_mutex);
+
pr_debug("Added domain %s\n", domain->name);
+ return domain;
}
+EXPORT_SYMBOL_GPL(__irq_domain_add);

/**
* irq_domain_remove() - Remove an irq domain.
@@ -99,30 +95,28 @@ void irq_domain_remove(struct irq_domain *domain)

pr_debug("Removed domain %s\n", domain->name);

- irq_domain_free(domain);
+ of_node_put(domain->of_node);
+ kfree(domain);
}
EXPORT_SYMBOL_GPL(irq_domain_remove);

/**
- * irq_domain_add_simple() - Allocate and register a simple irq_domain.
+ * irq_domain_add_simple() - Register an irq_domain and optionally map a range of irqs
* @of_node: pointer to interrupt controller's device tree node.
* @size: total number of irqs in mapping
* @first_irq: first number of irq block assigned to the domain,
- * pass zero to assign irqs on-the-fly. This will result in a
- * linear IRQ domain so it is important to use irq_create_mapping()
- * for each used IRQ, especially when SPARSE_IRQ is enabled.
+ * pass zero to assign irqs on-the-fly. If first_irq is non-zero, then
+ * pre-map all of the irqs in the domain to virqs starting at first_irq.
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
- * Allocates a legacy irq_domain if irq_base is positive or a linear
- * domain otherwise. For the legacy domain, IRQ descriptors will also
- * be allocated.
+ * Allocates an irq_domain, and optionally if first_irq is positive then also
+ * allocate irq_descs and map all of the hwirqs to virqs starting at first_irq.
*
* This is intended to implement the expected behaviour for most
- * interrupt controllers which is that a linear mapping should
- * normally be used unless the system requires a legacy mapping in
- * order to support supplying interrupt numbers during non-DT
- * registration of devices.
+ * interrupt controllers. If device tree is used, then first_irq will be 0 and
+ * irqs get mapped dynamically on the fly. However, if the controller requires
+ * static virq assignments (non-DT boot) then it will set that up correctly.
*/
struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
unsigned int size,
@@ -130,33 +124,25 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data)
{
- if (first_irq > 0) {
- int irq_base;
+ struct irq_domain *domain;
+
+ domain = __irq_domain_add(of_node, size, 0, ops, host_data);
+ if (!domain)
+ return NULL;

+ if (first_irq > 0) {
if (IS_ENABLED(CONFIG_SPARSE_IRQ)) {
- /*
- * Set the descriptor allocator to search for a
- * 1-to-1 mapping, such as irq_alloc_desc_at().
- * Use of_node_to_nid() which is defined to
- * numa_node_id() on platforms that have no custom
- * implementation.
- */
- irq_base = irq_alloc_descs(first_irq, first_irq, size,
- of_node_to_nid(of_node));
- if (irq_base < 0) {
+ /* attempt to allocated irq_descs */
+ int rc = irq_alloc_descs(first_irq, first_irq, size,
+ of_node_to_nid(of_node));
+ if (rc < 0)
pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
first_irq);
- irq_base = first_irq;
- }
- } else
- irq_base = first_irq;
-
- return irq_domain_add_legacy(of_node, size, irq_base, 0,
- ops, host_data);
+ }
+ WARN_ON(irq_domain_associate_many(domain, first_irq, 0, size));
}

- /* A linear domain is the default */
- return irq_domain_add_linear(of_node, size, ops, host_data);
+ return domain;
}
EXPORT_SYMBOL_GPL(irq_domain_add_simple);

@@ -184,11 +170,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
{
struct irq_domain *domain;

- pr_debug("Setting up legacy domain virq[%i:%i] ==> hwirq[%i:%i]\n",
- first_irq, first_irq + size - 1,
- (int)first_hwirq, (int)first_hwirq + size -1);
-
- domain = irq_domain_add_linear(of_node, first_hwirq + size, ops, host_data);
+ domain = __irq_domain_add(of_node, first_hwirq + size, 0, ops, host_data);
if (!domain)
return NULL;

@@ -199,43 +181,6 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
EXPORT_SYMBOL_GPL(irq_domain_add_legacy);

/**
- * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain.
- * @of_node: pointer to interrupt controller's device tree node.
- * @size: Number of interrupts in the domain.
- * @ops: map/unmap domain callbacks
- * @host_data: Controller private data pointer
- */
-struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
- unsigned int size,
- const struct irq_domain_ops *ops,
- void *host_data)
-{
- struct irq_domain *domain;
-
- domain = irq_domain_alloc(of_node, size, ops, host_data);
- if (!domain)
- return NULL;
-
- irq_domain_add(domain);
- return domain;
-}
-EXPORT_SYMBOL_GPL(irq_domain_add_linear);
-
-struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
- unsigned int max_irq,
- const struct irq_domain_ops *ops,
- void *host_data)
-{
- struct irq_domain *domain = irq_domain_alloc(of_node, 0, ops, host_data);
- if (domain) {
- domain->revmap_direct_max_irq = max_irq ? max_irq : ~0;
- irq_domain_add(domain);
- }
- return domain;
-}
-EXPORT_SYMBOL_GPL(irq_domain_add_nomap);
-
-/**
* irq_find_host() - Locates a domain for a given device node
* @node: device-tree node of the interrupt controller
*/
--
1.8.1.2

2013-06-10 00:49:49

by Grant Likely

[permalink] [raw]
Subject: [RFC 08/10] irqdomain: Refactor irq_domain_associate_many()

Originally, irq_domain_associate_many() was designed to unwind the
mapped irqs on a failure of any individual association. However, that
proved to be a problem with certain IRQ controllers. Some of them only
support a subset of irqs, and will fail when attempting to map a
reserved IRQ. In those cases we want to map as many IRQs as possible, so
instead it is better for irq_domain_associate_many() to make a
best-effort attempt to map irqs, but not fail if any or all of them
don't succeed. If a caller really cares about how many irqs got
associated, then it should instead go back and check that all of the
irqs is cares about were mapped.

The original design open-coded the individual association code into the
body of irq_domain_associate_many(), but with no longer needing to
unwind associations, the code becomes simpler to split out
irq_domain_associate() to contain the bulk of the logic, and
irq_domain_associate_many() to be a simple loop wrapper.

This patch also adds a new error check to the associate path to make
sure it isn't called for an irq larger than the controller can handle,
and adds locking so that the irq_domain_mutex is held while setting up a
new association.

Signed-off-by: Grant Likely <[email protected]>
---
include/linux/irqdomain.h | 22 +++---
kernel/irq/irqdomain.c | 185 +++++++++++++++++++++++-----------------------
2 files changed, 101 insertions(+), 106 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index fd4b26f..f9e8e06 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -103,6 +103,7 @@ struct irq_domain {
struct irq_domain_chip_generic *gc;

/* reverse map data. The linear map gets appended to the irq_domain */
+ irq_hw_number_t hwirq_max;
unsigned int revmap_direct_max_irq;
unsigned int revmap_size;
struct radix_tree_root revmap_tree;
@@ -110,8 +111,8 @@ struct irq_domain {
};

#ifdef CONFIG_IRQ_DOMAIN
-struct irq_domain *__irq_domain_add(struct device_node *of_node,
- int size, int direct_max,
+struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
+ irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
void *host_data);
struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
@@ -140,14 +141,14 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no
const struct irq_domain_ops *ops,
void *host_data)
{
- return __irq_domain_add(of_node, size, 0, ops, host_data);
+ return __irq_domain_add(of_node, size, size, 0, ops, host_data);
}
static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
unsigned int max_irq,
const struct irq_domain_ops *ops,
void *host_data)
{
- return __irq_domain_add(of_node, 0, max_irq, ops, host_data);
+ return __irq_domain_add(of_node, 0, max_irq, max_irq, ops, host_data);
}
static inline struct irq_domain *irq_domain_add_legacy_isa(
struct device_node *of_node,
@@ -166,14 +167,11 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node

extern void irq_domain_remove(struct irq_domain *host);

-extern int irq_domain_associate_many(struct irq_domain *domain,
- unsigned int irq_base,
- irq_hw_number_t hwirq_base, int count);
-static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
- irq_hw_number_t hwirq)
-{
- return irq_domain_associate_many(domain, irq, hwirq, 1);
-}
+extern int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq);
+extern void irq_domain_associate_many(struct irq_domain *domain,
+ unsigned int irq_base,
+ irq_hw_number_t hwirq_base, int count);

extern unsigned int irq_create_mapping(struct irq_domain *host,
irq_hw_number_t hwirq);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 280b804..80e9249 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -35,8 +35,8 @@ static struct irq_domain *irq_default_domain;
* register allocated irq_domain with irq_domain_register(). Returns pointer
* to IRQ domain, or NULL on failure.
*/
-struct irq_domain *__irq_domain_add(struct device_node *of_node,
- int size, int direct_max,
+struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
+ irq_hw_number_t hwirq_max, int direct_max,
const struct irq_domain_ops *ops,
void *host_data)
{
@@ -52,6 +52,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node,
domain->ops = ops;
domain->host_data = host_data;
domain->of_node = of_node_get(of_node);
+ domain->hwirq_max = hwirq_max;
domain->revmap_size = size;
domain->revmap_direct_max_irq = direct_max;

@@ -126,7 +127,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
{
struct irq_domain *domain;

- domain = __irq_domain_add(of_node, size, 0, ops, host_data);
+ domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
if (!domain)
return NULL;

@@ -139,7 +140,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
first_irq);
}
- WARN_ON(irq_domain_associate_many(domain, first_irq, 0, size));
+ irq_domain_associate_many(domain, first_irq, 0, size);
}

return domain;
@@ -170,11 +171,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
{
struct irq_domain *domain;

- domain = __irq_domain_add(of_node, first_hwirq + size, 0, ops, host_data);
+ domain = __irq_domain_add(of_node, first_hwirq + size,
+ first_hwirq + size, 0, ops, host_data);
if (!domain)
return NULL;

- WARN_ON(irq_domain_associate_many(domain, first_irq, first_hwirq, size));
+ irq_domain_associate_many(domain, first_irq, first_hwirq, size);

return domain;
}
@@ -228,109 +230,109 @@ void irq_set_default_host(struct irq_domain *domain)
}
EXPORT_SYMBOL_GPL(irq_set_default_host);

-static void irq_domain_disassociate_many(struct irq_domain *domain,
- unsigned int irq_base, int count)
+static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
{
- /*
- * disassociate in reverse order;
- * not strictly necessary, but nice for unwinding
- */
- while (count--) {
- int irq = irq_base + count;
- struct irq_data *irq_data = irq_get_irq_data(irq);
- irq_hw_number_t hwirq;
+ struct irq_data *irq_data = irq_get_irq_data(irq);
+ irq_hw_number_t hwirq;

- if (WARN_ON(!irq_data || irq_data->domain != domain))
- continue;
+ if (WARN(!irq_data || irq_data->domain != domain,
+ "virq%i doesn't exist; cannot disassociate\n", irq))
+ return;

- hwirq = irq_data->hwirq;
- irq_set_status_flags(irq, IRQ_NOREQUEST);
+ hwirq = irq_data->hwirq;
+ irq_set_status_flags(irq, IRQ_NOREQUEST);

- /* remove chip and handler */
- irq_set_chip_and_handler(irq, NULL, NULL);
+ /* remove chip and handler */
+ irq_set_chip_and_handler(irq, NULL, NULL);

- /* Make sure it's completed */
- synchronize_irq(irq);
+ /* Make sure it's completed */
+ synchronize_irq(irq);

- /* Tell the PIC about it */
- if (domain->ops->unmap)
- domain->ops->unmap(domain, irq);
- smp_mb();
+ /* Tell the PIC about it */
+ if (domain->ops->unmap)
+ domain->ops->unmap(domain, irq);
+ smp_mb();

- irq_data->domain = NULL;
- irq_data->hwirq = 0;
+ irq_data->domain = NULL;
+ irq_data->hwirq = 0;

- /* Clear reverse map for this hwirq */
- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = 0;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_delete(&domain->revmap_tree, hwirq);
- mutex_unlock(&revmap_trees_mutex);
- }
+ /* Clear reverse map for this hwirq */
+ if (hwirq < domain->revmap_size) {
+ domain->linear_revmap[hwirq] = 0;
+ } else {
+ mutex_lock(&revmap_trees_mutex);
+ radix_tree_delete(&domain->revmap_tree, hwirq);
+ mutex_unlock(&revmap_trees_mutex);
}
}

-int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
- irq_hw_number_t hwirq_base, int count)
+int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
+ irq_hw_number_t hwirq)
{
- unsigned int virq = irq_base;
- irq_hw_number_t hwirq = hwirq_base;
- int i, ret;
+ struct irq_data *irq_data = irq_get_irq_data(virq);
+ int ret;

- pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
- of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
+ if (WARN(hwirq >= domain->hwirq_max,
+ "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
+ return -EINVAL;
+ if (WARN(!irq_data, "error: virq%i is not allocated", virq))
+ return -EINVAL;
+ if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
+ return -EINVAL;

- for (i = 0; i < count; i++) {
- struct irq_data *irq_data = irq_get_irq_data(virq + i);
-
- if (WARN(!irq_data, "error: irq_desc not allocated; "
- "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
- return -EINVAL;
- if (WARN(irq_data->domain, "error: irq_desc already associated; "
- "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
- return -EINVAL;
- };
-
- for (i = 0; i < count; i++, virq++, hwirq++) {
- struct irq_data *irq_data = irq_get_irq_data(virq);
-
- irq_data->hwirq = hwirq;
- irq_data->domain = domain;
- if (domain->ops->map) {
- ret = domain->ops->map(domain, virq, hwirq);
- if (ret != 0) {
- /*
- * If map() returns -EPERM, this interrupt is protected
- * by the firmware or some other service and shall not
- * be mapped. Don't bother telling the user about it.
- */
- if (ret != -EPERM) {
- pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
- domain->name, hwirq, virq, ret);
- }
- irq_data->domain = NULL;
- irq_data->hwirq = 0;
- continue;
+ mutex_lock(&irq_domain_mutex);
+ irq_data->hwirq = hwirq;
+ irq_data->domain = domain;
+ if (domain->ops->map) {
+ ret = domain->ops->map(domain, virq, hwirq);
+ if (ret != 0) {
+ /*
+ * If map() returns -EPERM, this interrupt is protected
+ * by the firmware or some other service and shall not
+ * be mapped. Don't bother telling the user about it.
+ */
+ if (ret != -EPERM) {
+ pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
+ domain->name, hwirq, virq, ret);
}
- /* If not already assigned, give the domain the chip's name */
- if (!domain->name && irq_data->chip)
- domain->name = irq_data->chip->name;
+ irq_data->domain = NULL;
+ irq_data->hwirq = 0;
+ mutex_unlock(&irq_domain_mutex);
+ return ret;
}

- if (hwirq < domain->revmap_size) {
- domain->linear_revmap[hwirq] = virq;
- } else {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
- mutex_unlock(&revmap_trees_mutex);
- }
+ /* If not already assigned, give the domain the chip's name */
+ if (!domain->name && irq_data->chip)
+ domain->name = irq_data->chip->name;
+ }

- irq_clear_status_flags(virq, IRQ_NOREQUEST);
+ if (hwirq < domain->revmap_size) {
+ domain->linear_revmap[hwirq] = virq;
+ } else {
+ mutex_lock(&revmap_trees_mutex);
+ radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
+ mutex_unlock(&revmap_trees_mutex);
}
+ mutex_unlock(&irq_domain_mutex);
+
+ irq_clear_status_flags(virq, IRQ_NOREQUEST);

return 0;
}
+EXPORT_SYMBOL_GPL(irq_domain_associate);
+
+void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
+ irq_hw_number_t hwirq_base, int count)
+{
+ int i;
+
+ pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
+ of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
+
+ for (i = 0; i < count; i++) {
+ irq_domain_associate(domain, irq_base + i, hwirq_base + i);
+ }
+}
EXPORT_SYMBOL_GPL(irq_domain_associate_many);

/**
@@ -460,12 +462,7 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
if (unlikely(ret < 0))
return ret;

- ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count);
- if (unlikely(ret < 0)) {
- irq_free_descs(irq_base, count);
- return ret;
- }
-
+ irq_domain_associate_many(domain, irq_base, hwirq_base, count);
return 0;
}
EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
@@ -535,7 +532,7 @@ void irq_dispose_mapping(unsigned int virq)
if (WARN_ON(domain == NULL))
return;

- irq_domain_disassociate_many(domain, virq, 1);
+ irq_domain_disassociate(domain, virq);
irq_free_desc(virq);
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
--
1.8.1.2

2013-06-10 00:49:53

by Grant Likely

[permalink] [raw]
Subject: [RFC 10/10] irqchip: Make versatile fpga irq driver a generic chip

This is an RFC patch to convert the versatile FPGA irq controller driver
to use generic irq chip. It builds on the series that extends the
generic chip code to allow a linear irq domain to contain one or more
generic irq chips so that each interrupt controller doesn't need to hand
code the generic chip setup.

I've written this as a proof of concept to see if the new generic irq
code does what it needs to. I had to extend it slightly to properly
handle the valid mask used by the versatile FPGA driver.

Tested on QEMU, but not on real hardware.

Signed-off-by: Grant Likely <[email protected]>
Cc: Russell King <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Thomas Gleixner <[email protected]>
---
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/irq-versatile-fpga.c | 104 +++++++++++++----------------------
2 files changed, 39 insertions(+), 66 deletions(-)

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4a33351..8765502 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -14,6 +14,7 @@ config ARM_VIC
bool
select IRQ_DOMAIN
select MULTI_IRQ_HANDLER
+ select GENERIC_IRQ_CHIP

config ARM_VIC_NR
int
diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c
index 47a52ab..8c7097b 100644
--- a/drivers/irqchip/irq-versatile-fpga.c
+++ b/drivers/irqchip/irq-versatile-fpga.c
@@ -34,37 +34,18 @@
* @used_irqs: number of active IRQs on this controller
*/
struct fpga_irq_data {
- void __iomem *base;
- struct irq_chip chip;
- u32 valid;
struct irq_domain *domain;
- u8 used_irqs;
};

/* we cannot allocate memory when the controllers are initially registered */
static struct fpga_irq_data fpga_irq_devices[CONFIG_VERSATILE_FPGA_IRQ_NR];
static int fpga_irq_id;

-static void fpga_irq_mask(struct irq_data *d)
-{
- struct fpga_irq_data *f = irq_data_get_irq_chip_data(d);
- u32 mask = 1 << d->hwirq;
-
- writel(mask, f->base + IRQ_ENABLE_CLEAR);
-}
-
-static void fpga_irq_unmask(struct irq_data *d)
-{
- struct fpga_irq_data *f = irq_data_get_irq_chip_data(d);
- u32 mask = 1 << d->hwirq;
-
- writel(mask, f->base + IRQ_ENABLE_SET);
-}
-
static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc)
{
- struct fpga_irq_data *f = irq_desc_get_handler_data(desc);
- u32 status = readl(f->base + IRQ_STATUS);
+ struct irq_domain *domain = irq_desc_get_handler_data(desc);
+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
+ u32 status = readl(gc->reg_base + IRQ_STATUS);

if (status == 0) {
do_bad_IRQ(irq, desc);
@@ -74,7 +55,7 @@ static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc)
do {
irq = ffs(status) - 1;
status &= ~(1 << irq);
- generic_handle_irq(irq_find_mapping(f->domain, irq));
+ generic_handle_irq(irq_find_mapping(domain, irq));
} while (status);
}

@@ -85,11 +66,12 @@ static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc)
*/
static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs)
{
+ struct irq_chip_generic *gc = irq_get_domain_generic_chip(f->domain, 0);
int handled = 0;
int irq;
u32 status;

- while ((status = readl(f->base + IRQ_STATUS))) {
+ while ((status = readl(gc->reg_base + IRQ_STATUS))) {
irq = ffs(status) - 1;
handle_IRQ(irq_find_mapping(f->domain, irq), regs);
handled = 1;
@@ -112,63 +94,53 @@ asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs)
} while (handled);
}

-static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
-{
- struct fpga_irq_data *f = d->host_data;
-
- /* Skip invalid IRQs, only register handlers for the real ones */
- if (!(f->valid & BIT(hwirq)))
- return -EPERM;
- irq_set_chip_data(irq, f);
- irq_set_chip_and_handler(irq, &f->chip,
- handle_level_irq);
- set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
- return 0;
-}
-
-static struct irq_domain_ops fpga_irqdomain_ops = {
- .map = fpga_irqdomain_map,
- .xlate = irq_domain_xlate_onetwocell,
-};
-
void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
int parent_irq, u32 valid, struct device_node *node)
{
+ struct irq_chip_generic *gc;
struct fpga_irq_data *f;
- int i;
+ int ret;

if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) {
pr_err("%s: too few FPGA IRQ controllers, increase CONFIG_VERSATILE_FPGA_IRQ_NR\n", __func__);
return;
}
f = &fpga_irq_devices[fpga_irq_id];
- f->base = base;
- f->chip.name = name;
- f->chip.irq_ack = fpga_irq_mask;
- f->chip.irq_mask = fpga_irq_mask;
- f->chip.irq_unmask = fpga_irq_unmask;
- f->valid = valid;
+
+ /* This will also allocate irq descriptors */
+ f->domain = irq_domain_add_linear(node, fls(valid),
+ &irq_generic_chip_ops, f);
+ if (!f->domain) {
+ pr_err("FPGA IRQ setup failed allocation domain\n");
+ return;
+ }
+
+ ret = irq_alloc_domain_generic_chips(f->domain, fls(valid), 1,
+ name, handle_level_irq,
+ IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0, 0);
+ if (ret) {
+ pr_err("FPGA IRQ setup failed allocating generic chip\n");
+ return;
+ }
+
+ gc = irq_get_domain_generic_chip(f->domain, 0);
+ gc->reg_base = base;
+ gc->unused = ~valid;
+ gc->chip_types[0].regs.enable = IRQ_ENABLE_SET;
+ gc->chip_types[0].regs.disable = IRQ_ENABLE_CLEAR;
+ gc->chip_types[0].chip.irq_ack = irq_gc_mask_disable_reg;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;

if (parent_irq != -1) {
- irq_set_handler_data(parent_irq, f);
+ irq_set_handler_data(parent_irq, f->domain);
irq_set_chained_handler(parent_irq, fpga_irq_handle);
}

- /* This will also allocate irq descriptors */
- f->domain = irq_domain_add_simple(node, fls(valid), irq_start,
- &fpga_irqdomain_ops, f);
-
- /* This will allocate all valid descriptors in the linear case */
- for (i = 0; i < fls(valid); i++)
- if (valid & BIT(i)) {
- if (!irq_start)
- irq_create_mapping(f->domain, i);
- f->used_irqs++;
- }
-
- pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs\n",
- fpga_irq_id, name, base, f->used_irqs);
+ if (irq_start)
+ irq_domain_associate_many(f->domain, irq_start, 0, fls(valid));
+
+ pr_info("FPGA IRQ chip %d \"%s\" @ %p\n", fpga_irq_id, name, base);

fpga_irq_id++;
}
--
1.8.1.2

2013-06-10 00:49:47

by Grant Likely

[permalink] [raw]
Subject: [RFC 09/10] irqdomain: remove irq_domain_generate_simple()

Nobody calls it; remove the function

Signed-off-by: Grant Likely <[email protected]>
---
include/linux/irqdomain.h | 8 --------
kernel/irq/irqdomain.c | 15 ---------------
2 files changed, 23 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index f9e8e06..fe7c57d 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -205,14 +205,6 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);

-#if defined(CONFIG_OF_IRQ)
-extern void irq_domain_generate_simple(const struct of_device_id *match,
- u64 phys_base, unsigned int irq_start);
-#else /* CONFIG_OF_IRQ */
-static inline void irq_domain_generate_simple(const struct of_device_id *match,
- u64 phys_base, unsigned int irq_start) { }
-#endif /* !CONFIG_OF_IRQ */
-
#else /* CONFIG_IRQ_DOMAIN */
static inline void irq_dispose_mapping(unsigned int virq) { }
#endif /* !CONFIG_IRQ_DOMAIN */
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 80e9249..e47b356 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -741,18 +741,3 @@ const struct irq_domain_ops irq_domain_simple_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
-
-#ifdef CONFIG_OF_IRQ
-void irq_domain_generate_simple(const struct of_device_id *match,
- u64 phys_base, unsigned int irq_start)
-{
- struct device_node *node;
- pr_debug("looking for phys_base=%llx, irq_start=%i\n",
- (unsigned long long) phys_base, (int) irq_start);
- node = of_find_matching_node_by_address(NULL, match, phys_base);
- if (node)
- irq_domain_add_legacy(node, 32, irq_start, 0,
- &irq_domain_simple_ops, NULL);
-}
-EXPORT_SYMBOL_GPL(irq_domain_generate_simple);
-#endif
--
1.8.1.2

2013-06-10 00:49:41

by Grant Likely

[permalink] [raw]
Subject: [RFC 02/10] irqdomain: Replace LEGACY mapping with LINEAR

The LEGACY mapping unnecessarily complicates the irqdomain code and
can easily be implemented with a linear mapping. By ripping it out
and replacing it with the LINEAR mapping the object size of
irqdomain.c shrinks by about 330 bytes (ARMv7) which offsets the
additional allocation required by the linear map. It also makes it
possible for current LEGACY map users to pre-allocate irq_descs for a
subset of the hwirqs and dynamically allocate the rest as needed.

Signed-off-by: Grant Likely <[email protected]>
Cc: Paul Mundt <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Rob Herring <[email protected]>
---
include/linux/irqdomain.h | 7 ----
kernel/irq/irqdomain.c | 84 ++++-------------------------------------------
2 files changed, 6 insertions(+), 85 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index ba2c708..6f06241 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -95,11 +95,6 @@ struct irq_domain {
union {
struct {
unsigned int size;
- unsigned int first_irq;
- irq_hw_number_t first_hwirq;
- } legacy;
- struct {
- unsigned int size;
unsigned int *revmap;
} linear;
struct {
@@ -117,8 +112,6 @@ struct irq_domain {
struct irq_domain_chip_generic *gc;
};

-#define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs.
- * ie. legacy 8259, gets irqs 1..15 */
#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 61d6d3c..1ac8cf4 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -82,13 +82,6 @@ void irq_domain_remove(struct irq_domain *domain)
mutex_lock(&irq_domain_mutex);

switch (domain->revmap_type) {
- case IRQ_DOMAIN_MAP_LEGACY:
- /*
- * Legacy domains don't manage their own irq_desc
- * allocations, we expect the caller to handle irq_desc
- * freeing on their own.
- */
- break;
case IRQ_DOMAIN_MAP_TREE:
/*
* radix_tree_delete() takes care of destroying the root
@@ -122,17 +115,6 @@ void irq_domain_remove(struct irq_domain *domain)
}
EXPORT_SYMBOL_GPL(irq_domain_remove);

-static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain,
- irq_hw_number_t hwirq)
-{
- irq_hw_number_t first_hwirq = domain->revmap_data.legacy.first_hwirq;
- int size = domain->revmap_data.legacy.size;
-
- if (WARN_ON(hwirq < first_hwirq || hwirq >= first_hwirq + size))
- return 0;
- return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq;
-}
-
/**
* irq_domain_add_simple() - Allocate and register a simple irq_domain.
* @of_node: pointer to interrupt controller's device tree node.
@@ -213,57 +195,17 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
void *host_data)
{
struct irq_domain *domain;
- unsigned int i;

- domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data);
+ pr_debug("Setting up legacy domain virq[%i:%i] ==> hwirq[%i:%i]\n",
+ first_irq, first_irq + size - 1,
+ (int)first_hwirq, (int)first_hwirq + size -1);
+
+ domain = irq_domain_add_linear(of_node, first_hwirq + size, ops, host_data);
if (!domain)
return NULL;

- domain->revmap_data.legacy.first_irq = first_irq;
- domain->revmap_data.legacy.first_hwirq = first_hwirq;
- domain->revmap_data.legacy.size = size;
-
- mutex_lock(&irq_domain_mutex);
- /* Verify that all the irqs are available */
- for (i = 0; i < size; i++) {
- int irq = first_irq + i;
- struct irq_data *irq_data = irq_get_irq_data(irq);
-
- if (WARN_ON(!irq_data || irq_data->domain)) {
- mutex_unlock(&irq_domain_mutex);
- irq_domain_free(domain);
- return NULL;
- }
- }
+ WARN_ON(irq_domain_associate_many(domain, first_irq, first_hwirq, size));

- /* Claim all of the irqs before registering a legacy domain */
- for (i = 0; i < size; i++) {
- struct irq_data *irq_data = irq_get_irq_data(first_irq + i);
- irq_data->hwirq = first_hwirq + i;
- irq_data->domain = domain;
- }
- mutex_unlock(&irq_domain_mutex);
-
- for (i = 0; i < size; i++) {
- int irq = first_irq + i;
- int hwirq = first_hwirq + i;
-
- /* IRQ0 gets ignored */
- if (!irq)
- continue;
-
- /* Legacy flags are left to default at this point,
- * one can then use irq_create_mapping() to
- * explicitly change them
- */
- if (ops->map)
- ops->map(domain, irq, hwirq);
-
- /* Clear norequest flags */
- irq_clear_status_flags(irq, IRQ_NOREQUEST);
- }
-
- irq_domain_add(domain);
return domain;
}
EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
@@ -492,10 +434,6 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
}

return 0;
-
- err_unmap:
- irq_domain_disassociate_many(domain, irq_base, i);
- return -EINVAL;
}
EXPORT_SYMBOL_GPL(irq_domain_associate_many);

@@ -575,10 +513,6 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
return virq;
}

- /* Get a virtual interrupt number */
- if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
- return irq_domain_legacy_revmap(domain, hwirq);
-
/* Allocate a virtual interrupt number */
hint = hwirq % nr_irqs;
if (hint == 0)
@@ -706,10 +640,6 @@ void irq_dispose_mapping(unsigned int virq)
if (WARN_ON(domain == NULL))
return;

- /* Never unmap legacy interrupts */
- if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
- return;
-
irq_domain_disassociate_many(domain, virq, 1);
irq_free_desc(virq);
}
@@ -732,8 +662,6 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
return 0;

switch (domain->revmap_type) {
- case IRQ_DOMAIN_MAP_LEGACY:
- return irq_domain_legacy_revmap(domain, hwirq);
case IRQ_DOMAIN_MAP_LINEAR:
return irq_linear_revmap(domain, hwirq);
case IRQ_DOMAIN_MAP_TREE:
--
1.8.1.2

2013-06-10 00:50:54

by Grant Likely

[permalink] [raw]
Subject: [RFC 07/10] irqdomain: Beef up debugfs output

This patch increases the amount of output produced by the
irq_domain_mapping debugfs file by first listing all of the registered
irq domains at the beginning of the output, and then by including all
mapped IRQs in the output, not just the active ones. It is very useful
when debugging irqdomain issues to be able to see the entire list of
mapped irqs, not just the ones that happen to be connected to devices.

Signed-off-by: Grant Likely <[email protected]>
---
kernel/irq/irqdomain.c | 35 ++++++++++++++++++++++++++++++-----
1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index e0db59e..280b804 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -596,12 +596,29 @@ static int virq_debug_show(struct seq_file *m, void *private)
{
unsigned long flags;
struct irq_desc *desc;
- void *data;
+ struct irq_domain *domain;
+ struct radix_tree_iter iter;
+ void *data, **slot;
int i;

- seq_printf(m, "%-5s %-7s %-15s %-*s %s\n", "irq", "hwirq",
+ seq_printf(m, " %-16s %-6s %-10s %-10s %s\n",
+ "name", "mapped", "linear-max", "direct-max", "devtree-node");
+ mutex_lock(&irq_domain_mutex);
+ list_for_each_entry(domain, &irq_domain_list, link) {
+ int count = 0;
+ radix_tree_for_each_slot(slot, &domain->revmap_tree, &iter, 0)
+ count++;
+ seq_printf(m, "%c%-16s %6u %10u %10u %s\n",
+ domain == irq_default_domain ? '*' : ' ', domain->name,
+ domain->revmap_size + count, domain->revmap_size,
+ domain->revmap_direct_max_irq,
+ domain->of_node ? of_node_full_name(domain->of_node) : "");
+ }
+ mutex_unlock(&irq_domain_mutex);
+
+ seq_printf(m, "%-5s %-7s %-15s %-*s %6s %-14s %s\n", "irq", "hwirq",
"chip name", (int)(2 * sizeof(void *) + 2), "chip data",
- "domain name");
+ "active", "type", "domain");

for (i = 1; i < nr_irqs; i++) {
desc = irq_to_desc(i);
@@ -609,12 +626,15 @@ static int virq_debug_show(struct seq_file *m, void *private)
continue;

raw_spin_lock_irqsave(&desc->lock, flags);
+ domain = desc->irq_data.domain;

- if (desc->action && desc->action->handler) {
+ if (domain) {
struct irq_chip *chip;
+ int hwirq = desc->irq_data.hwirq;
+ bool direct;

seq_printf(m, "%5d ", i);
- seq_printf(m, "0x%05lx ", desc->irq_data.hwirq);
+ seq_printf(m, "0x%05x ", hwirq);

chip = irq_desc_get_chip(desc);
seq_printf(m, "%-15s ", (chip && chip->name) ? chip->name : "none");
@@ -622,6 +642,11 @@ static int virq_debug_show(struct seq_file *m, void *private)
data = irq_desc_get_chip_data(desc);
seq_printf(m, data ? "0x%p " : " %p ", data);

+ seq_printf(m, " %c ", (desc->action && desc->action->handler) ? '*' : ' ');
+ direct = (i == hwirq) && (i < domain->revmap_direct_max_irq);
+ seq_printf(m, "%6s%-8s ",
+ (hwirq < domain->revmap_size) ? "LINEAR" : "RADIX",
+ direct ? "(DIRECT)" : "");
seq_printf(m, "%s\n", desc->irq_data.domain->name);
}

--
1.8.1.2

2013-06-10 00:51:24

by Grant Likely

[permalink] [raw]
Subject: [RFC 04/10] irqdomain: merge linear and tree reverse mappings.

From: Grant Likely <[email protected]>

Keeping them separate makes irq_domain more complex and adds a lot of
code (as proven by the diffstat). Merging them simplifies the whole
scheme. This change makes it so both the tree and linear methods can be
used by the same irq_domain instance. If the hwirq is less than the
->linear_size, then the linear map is used to reverse map the hwirq.
Otherwise the radix tree is used. The test for which map to use is no
more expensive that the existing code, so the performance of fast path
is preserved.

It also means that complex interrupt controllers can use both the
linear map and a tree in the same domain. This may be useful for an
interrupt controller with a base set of core irqs and a large number
of GPIOs which might be used as irqs. The linear map could cover the
core irqs, and the tree used for thas irqs. The linear map could
cover the core irqs, and the tree used for the gpios.

v2: Drop reorganization of revmap data

Signed-off-by: Grant Likely <[email protected]>
Cc: Paul Mundt <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Rob Herring <[email protected]>
---
include/linux/irqdomain.h | 18 ++++----
kernel/irq/irqdomain.c | 107 +++++++++++++---------------------------------
2 files changed, 39 insertions(+), 86 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index e5e513c..1cbb741 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -75,7 +75,6 @@ struct irq_domain_chip_generic;
* @link: Element in global irq_domain list.
* @revmap_type: Method used for reverse mapping hwirq numbers to linux irq. This
* will be one of the IRQ_DOMAIN_MAP_* values.
- * @revmap_data: Revmap method specific data.
* @ops: pointer to irq_domain methods
* @host_data: private data pointer for use by owner. Not touched by irq_domain
* core code.
@@ -93,10 +92,9 @@ struct irq_domain {

/* type of reverse mapping_technique */
unsigned int revmap_type;
- union {
+ struct {
struct {
unsigned int size;
- unsigned int *revmap;
} linear;
struct {
unsigned int max_irq;
@@ -111,11 +109,13 @@ struct irq_domain {
struct device_node *of_node;
/* Optional pointer to generic interrupt chips */
struct irq_domain_chip_generic *gc;
+
+ /* Linear reverse map */
+ unsigned int linear_revmap[];
};

#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
-#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */

#ifdef CONFIG_IRQ_DOMAIN
struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
@@ -137,10 +137,6 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
unsigned int max_irq,
const struct irq_domain_ops *ops,
void *host_data);
-struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
- const struct irq_domain_ops *ops,
- void *host_data);
-
extern struct irq_domain *irq_find_host(struct device_node *node);
extern void irq_set_default_host(struct irq_domain *host);

@@ -152,6 +148,12 @@ static inline struct irq_domain *irq_domain_add_legacy_isa(
return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops,
host_data);
}
+static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
+ const struct irq_domain_ops *ops,
+ void *host_data)
+{
+ return irq_domain_add_linear(of_node, 0, ops, host_data);
+}

extern void irq_domain_remove(struct irq_domain *host);

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index b1b5e67..5a1d8ec 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -34,22 +34,24 @@ static struct irq_domain *irq_default_domain;
* to IRQ domain, or NULL on failure.
*/
static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
- unsigned int revmap_type,
+ unsigned int revmap_type, int size,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;

- domain = kzalloc_node(sizeof(*domain), GFP_KERNEL,
- of_node_to_nid(of_node));
+ domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
+ GFP_KERNEL, of_node_to_nid(of_node));
if (WARN_ON(!domain))
return NULL;

/* Fill structure */
+ INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL);
domain->revmap_type = revmap_type;
domain->ops = ops;
domain->host_data = host_data;
domain->of_node = of_node_get(of_node);
+ domain->revmap_data.linear.size = size;

return domain;
}
@@ -81,22 +83,12 @@ void irq_domain_remove(struct irq_domain *domain)
{
mutex_lock(&irq_domain_mutex);

- switch (domain->revmap_type) {
- case IRQ_DOMAIN_MAP_TREE:
- /*
- * radix_tree_delete() takes care of destroying the root
- * node when all entries are removed. Shout if there are
- * any mappings left.
- */
- WARN_ON(domain->revmap_data.tree.height);
- break;
- case IRQ_DOMAIN_MAP_LINEAR:
- kfree(domain->revmap_data.linear.revmap);
- domain->revmap_data.linear.size = 0;
- break;
- case IRQ_DOMAIN_MAP_NOMAP:
- break;
- }
+ /*
+ * radix_tree_delete() takes care of destroying the root
+ * node when all entries are removed. Shout if there are
+ * any mappings left.
+ */
+ WARN_ON(domain->revmap_data.tree.height);

list_del(&domain->link);

@@ -223,20 +215,11 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
void *host_data)
{
struct irq_domain *domain;
- unsigned int *revmap;

- revmap = kzalloc_node(sizeof(*revmap) * size, GFP_KERNEL,
- of_node_to_nid(of_node));
- if (WARN_ON(!revmap))
+ domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, size, ops, host_data);
+ if (!domain)
return NULL;

- domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, ops, host_data);
- if (!domain) {
- kfree(revmap);
- return NULL;
- }
- domain->revmap_data.linear.size = size;
- domain->revmap_data.linear.revmap = revmap;
irq_domain_add(domain);
return domain;
}
@@ -248,7 +231,7 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
void *host_data)
{
struct irq_domain *domain = irq_domain_alloc(of_node,
- IRQ_DOMAIN_MAP_NOMAP, ops, host_data);
+ IRQ_DOMAIN_MAP_NOMAP, 0, ops, host_data);
if (domain) {
domain->revmap_data.nomap.max_irq = max_irq ? max_irq : ~0;
irq_domain_add(domain);
@@ -258,28 +241,6 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
EXPORT_SYMBOL_GPL(irq_domain_add_nomap);

/**
- * irq_domain_add_tree()
- * @of_node: pointer to interrupt controller's device tree node.
- * @ops: map/unmap domain callbacks
- *
- * Note: The radix tree will be allocated later during boot automatically
- * (the reverse mapping will use the slow path until that happens).
- */
-struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
- const struct irq_domain_ops *ops,
- void *host_data)
-{
- struct irq_domain *domain = irq_domain_alloc(of_node,
- IRQ_DOMAIN_MAP_TREE, ops, host_data);
- if (domain) {
- INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL);
- irq_domain_add(domain);
- }
- return domain;
-}
-EXPORT_SYMBOL_GPL(irq_domain_add_tree);
-
-/**
* irq_find_host() - Locates a domain for a given device node
* @node: device-tree node of the interrupt controller
*/
@@ -359,17 +320,13 @@ static void irq_domain_disassociate_many(struct irq_domain *domain,
irq_data->domain = NULL;
irq_data->hwirq = 0;

- /* Clear reverse map */
- switch(domain->revmap_type) {
- case IRQ_DOMAIN_MAP_LINEAR:
- if (hwirq < domain->revmap_data.linear.size)
- domain->revmap_data.linear.revmap[hwirq] = 0;
- break;
- case IRQ_DOMAIN_MAP_TREE:
+ /* Clear reverse map for this hwirq */
+ if (hwirq < domain->revmap_data.linear.size) {
+ domain->linear_revmap[hwirq] = 0;
+ } else {
mutex_lock(&revmap_trees_mutex);
radix_tree_delete(&domain->revmap_data.tree, hwirq);
mutex_unlock(&revmap_trees_mutex);
- break;
}
}
}
@@ -421,16 +378,12 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
domain->name = irq_data->chip->name;
}

- switch (domain->revmap_type) {
- case IRQ_DOMAIN_MAP_LINEAR:
- if (hwirq < domain->revmap_data.linear.size)
- domain->revmap_data.linear.revmap[hwirq] = virq;
- break;
- case IRQ_DOMAIN_MAP_TREE:
+ if (hwirq < domain->revmap_data.linear.size) {
+ domain->linear_revmap[hwirq] = virq;
+ } else {
mutex_lock(&revmap_trees_mutex);
radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
mutex_unlock(&revmap_trees_mutex);
- break;
}

irq_clear_status_flags(virq, IRQ_NOREQUEST);
@@ -667,13 +620,6 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
switch (domain->revmap_type) {
case IRQ_DOMAIN_MAP_LINEAR:
return irq_linear_revmap(domain, hwirq);
- case IRQ_DOMAIN_MAP_TREE:
- rcu_read_lock();
- data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
- rcu_read_unlock();
- if (data)
- return data->irq;
- break;
case IRQ_DOMAIN_MAP_NOMAP:
data = irq_get_irq_data(hwirq);
if (data && (data->domain == domain) && (data->hwirq == hwirq))
@@ -696,13 +642,18 @@ EXPORT_SYMBOL_GPL(irq_find_mapping);
unsigned int irq_linear_revmap(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
+ struct irq_data *data;
BUG_ON(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR);

/* Check revmap bounds; complain if exceeded */
- if (WARN_ON(hwirq >= domain->revmap_data.linear.size))
- return 0;
+ if (hwirq >= domain->revmap_data.linear.size) {
+ rcu_read_lock();
+ data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
+ rcu_read_unlock();
+ return data ? data->irq : 0;
+ }

- return domain->revmap_data.linear.revmap[hwirq];
+ return domain->linear_revmap[hwirq];
}
EXPORT_SYMBOL_GPL(irq_linear_revmap);

--
1.8.1.2

2013-06-10 00:51:22

by Grant Likely

[permalink] [raw]
Subject: [RFC 05/10] irqdomain: Eliminate revmap type

The NOMAP irq_domain type is only used by a handful of interrupt
controllers and it unnecessarily complicates the code by adding special
cases on how to look up mappings and different revmap functions are used
for each type which need to validate the correct type is passed to it
before performing the reverse map. Eliminating the revmap_type and
making a single reverse mapping function simplifies the code. It also
shouldn't be any slower than having separate revmap functions because
the type of the revmap needed to be checked anyway.

The linear and tree revmap types were already merged in a previous
patch. This patch rolls the NOMAP or direct mapping behaviour into the
same domain code making is possible for an irq domain to do any mapping
type; linear, tree or direct; and that the mapping will be transparent
to the interrupt controller driver.

With this change, direct mappings will get stored in the linear or tree
mapping for consistency. Reverse mapping from the hwirq to virq will go
through the normal lookup process. However, any controller using a
direct mapping can take advantage of knowing that hwirq==virq for any
mapped interrupts skip doing a revmap lookup when handling IRQs.

Signed-off-by: Grant Likely <[email protected]>
---
include/linux/irqdomain.h | 48 +++++++++++++++++------------------------
kernel/irq/generic-chip.c | 5 +----
kernel/irq/irqdomain.c | 55 +++++++++++++++++++----------------------------
3 files changed, 43 insertions(+), 65 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 1cbb741..51ef84a 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -73,50 +73,42 @@ struct irq_domain_chip_generic;
/**
* struct irq_domain - Hardware interrupt number translation object
* @link: Element in global irq_domain list.
- * @revmap_type: Method used for reverse mapping hwirq numbers to linux irq. This
- * will be one of the IRQ_DOMAIN_MAP_* values.
+ * @name: Name of interrupt domain
* @ops: pointer to irq_domain methods
* @host_data: private data pointer for use by owner. Not touched by irq_domain
* core code.
- * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator
- * of the irq_domain is responsible for allocating the array of
- * irq_desc structures.
- * @nr_irq: Number of irqs managed by the irq domain
- * @hwirq_base: Starting number for hwirqs managed by the irq domain
- * @of_node: (optional) Pointer to device tree nodes associated with the
- * irq_domain. Used when decoding device tree interrupt specifiers.
+ *
+ * Optional elements
+ * @of_node: Pointer to device tree nodes associated with the irq_domain. Used
+ * when decoding device tree interrupt specifiers.
+ * @gc: Pointer to a list of generic chips. There is a helper function for
+ * setting up one or more generic chips for interrupt controllers
+ * drivers using the generic chip library which uses this pointer.
+ *
+ * Revmap data, used internally by irq_domain
+ * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that
+ * support direct mapping
+ * @revmap_size: Size of the linear map table @linear_revmap[]
+ * @revmap_tree: Radix map tree for hwirqs that don't fit in the linear map
+ * @linear_revmap: Linear table of hwirq->virq reverse mappings
*/
struct irq_domain {
struct list_head link;
const char *name;
-
- /* type of reverse mapping_technique */
- unsigned int revmap_type;
- struct {
- struct {
- unsigned int size;
- } linear;
- struct {
- unsigned int max_irq;
- } nomap;
- struct radix_tree_root tree;
- } revmap_data;
const struct irq_domain_ops *ops;
void *host_data;
- irq_hw_number_t inval_irq;

- /* Optional device node pointer */
+ /* Optional data */
struct device_node *of_node;
- /* Optional pointer to generic interrupt chips */
struct irq_domain_chip_generic *gc;

- /* Linear reverse map */
+ /* reverse map data. The linear map gets appended to the irq_domain */
+ unsigned int revmap_direct_max_irq;
+ unsigned int revmap_size;
+ struct radix_tree_root revmap_tree;
unsigned int linear_revmap[];
};

-#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */
-#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */
-
#ifdef CONFIG_IRQ_DOMAIN
struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
unsigned int size,
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index ca98cc5..4b01106 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -270,10 +270,7 @@ int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
if (d->gc)
return -EBUSY;

- if (d->revmap_type != IRQ_DOMAIN_MAP_LINEAR)
- return -EINVAL;
-
- numchips = d->revmap_data.linear.size / irqs_per_chip;
+ numchips = d->revmap_size / irqs_per_chip;
if (!numchips)
return -EINVAL;

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 5a1d8ec..c38be78 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -25,7 +25,6 @@ static struct irq_domain *irq_default_domain;
/**
* irq_domain_alloc() - Allocate a new irq_domain data structure
* @of_node: optional device-tree node of the interrupt controller
- * @revmap_type: type of reverse mapping to use
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
@@ -34,7 +33,7 @@ static struct irq_domain *irq_default_domain;
* to IRQ domain, or NULL on failure.
*/
static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
- unsigned int revmap_type, int size,
+ int size,
const struct irq_domain_ops *ops,
void *host_data)
{
@@ -46,12 +45,11 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
return NULL;

/* Fill structure */
- INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL);
- domain->revmap_type = revmap_type;
+ INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
domain->ops = ops;
domain->host_data = host_data;
domain->of_node = of_node_get(of_node);
- domain->revmap_data.linear.size = size;
+ domain->revmap_size = size;

return domain;
}
@@ -67,8 +65,7 @@ static void irq_domain_add(struct irq_domain *domain)
mutex_lock(&irq_domain_mutex);
list_add(&domain->link, &irq_domain_list);
mutex_unlock(&irq_domain_mutex);
- pr_debug("Allocated domain of type %d @0x%p\n",
- domain->revmap_type, domain);
+ pr_debug("Added domain %s\n", domain->name);
}

/**
@@ -88,7 +85,7 @@ void irq_domain_remove(struct irq_domain *domain)
* node when all entries are removed. Shout if there are
* any mappings left.
*/
- WARN_ON(domain->revmap_data.tree.height);
+ WARN_ON(domain->revmap_tree.height);

list_del(&domain->link);

@@ -100,8 +97,7 @@ void irq_domain_remove(struct irq_domain *domain)

mutex_unlock(&irq_domain_mutex);

- pr_debug("Removed domain of type %d @0x%p\n",
- domain->revmap_type, domain);
+ pr_debug("Removed domain %s\n", domain->name);

irq_domain_free(domain);
}
@@ -216,7 +212,7 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
{
struct irq_domain *domain;

- domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, size, ops, host_data);
+ domain = irq_domain_alloc(of_node, size, ops, host_data);
if (!domain)
return NULL;

@@ -230,10 +226,9 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data)
{
- struct irq_domain *domain = irq_domain_alloc(of_node,
- IRQ_DOMAIN_MAP_NOMAP, 0, ops, host_data);
+ struct irq_domain *domain = irq_domain_alloc(of_node, 0, ops, host_data);
if (domain) {
- domain->revmap_data.nomap.max_irq = max_irq ? max_irq : ~0;
+ domain->revmap_direct_max_irq = max_irq ? max_irq : ~0;
irq_domain_add(domain);
}
return domain;
@@ -321,11 +316,11 @@ static void irq_domain_disassociate_many(struct irq_domain *domain,
irq_data->hwirq = 0;

/* Clear reverse map for this hwirq */
- if (hwirq < domain->revmap_data.linear.size) {
+ if (hwirq < domain->revmap_size) {
domain->linear_revmap[hwirq] = 0;
} else {
mutex_lock(&revmap_trees_mutex);
- radix_tree_delete(&domain->revmap_data.tree, hwirq);
+ radix_tree_delete(&domain->revmap_tree, hwirq);
mutex_unlock(&revmap_trees_mutex);
}
}
@@ -378,11 +373,11 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
domain->name = irq_data->chip->name;
}

- if (hwirq < domain->revmap_data.linear.size) {
+ if (hwirq < domain->revmap_size) {
domain->linear_revmap[hwirq] = virq;
} else {
mutex_lock(&revmap_trees_mutex);
- radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
+ radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
mutex_unlock(&revmap_trees_mutex);
}

@@ -399,7 +394,9 @@ EXPORT_SYMBOL_GPL(irq_domain_associate_many);
*
* This routine is used for irq controllers which can choose the hardware
* interrupt numbers they generate. In such a case it's simplest to use
- * the linux irq as the hardware interrupt number.
+ * the linux irq as the hardware interrupt number. It still uses the linear
+ * or radix tree to store the mapping, but the irq controller can optimize
+ * the revmap path by using the hwirq directly.
*/
unsigned int irq_create_direct_mapping(struct irq_domain *domain)
{
@@ -408,17 +405,14 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
if (domain == NULL)
domain = irq_default_domain;

- if (WARN_ON(!domain || domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP))
- return 0;
-
virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
if (!virq) {
pr_debug("create_direct virq allocation failed\n");
return 0;
}
- if (virq >= domain->revmap_data.nomap.max_irq) {
+ if (virq >= domain->revmap_direct_max_irq) {
pr_err("ERROR: no free irqs available below %i maximum\n",
- domain->revmap_data.nomap.max_irq);
+ domain->revmap_direct_max_irq);
irq_free_desc(virq);
return 0;
}
@@ -617,17 +611,13 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
if (domain == NULL)
return 0;

- switch (domain->revmap_type) {
- case IRQ_DOMAIN_MAP_LINEAR:
- return irq_linear_revmap(domain, hwirq);
- case IRQ_DOMAIN_MAP_NOMAP:
+ if (hwirq < domain->revmap_direct_max_irq) {
data = irq_get_irq_data(hwirq);
if (data && (data->domain == domain) && (data->hwirq == hwirq))
return hwirq;
- break;
}

- return 0;
+ return irq_linear_revmap(domain, hwirq);
}
EXPORT_SYMBOL_GPL(irq_find_mapping);

@@ -643,12 +633,11 @@ unsigned int irq_linear_revmap(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
struct irq_data *data;
- BUG_ON(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR);

/* Check revmap bounds; complain if exceeded */
- if (hwirq >= domain->revmap_data.linear.size) {
+ if (hwirq >= domain->revmap_size) {
rcu_read_lock();
- data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
+ data = radix_tree_lookup(&domain->revmap_tree, hwirq);
rcu_read_unlock();
return data ? data->irq : 0;
}
--
1.8.1.2

2013-06-10 07:40:10

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC 10/10] irqchip: Make versatile fpga irq driver a generic chip

On Mon, Jun 10, 2013 at 2:49 AM, Grant Likely <[email protected]> wrote:

> This is an RFC patch to convert the versatile FPGA irq controller driver
> to use generic irq chip. It builds on the series that extends the
> generic chip code to allow a linear irq domain to contain one or more
> generic irq chips so that each interrupt controller doesn't need to hand
> code the generic chip setup.
>
> I've written this as a proof of concept to see if the new generic irq
> code does what it needs to. I had to extend it slightly to properly
> handle the valid mask used by the versatile FPGA driver.
>
> Tested on QEMU, but not on real hardware.

Is this the same as the one I tested previously?

If it need re-testing please push a branch and I'll take it
for a spin.

Yours.
Linus Walleij

2013-06-10 09:04:13

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [RFC 10/10] irqchip: Make versatile fpga irq driver a generic chip

On Mon, Jun 10, 2013 at 01:49:22AM +0100, Grant Likely wrote:
> This is an RFC patch to convert the versatile FPGA irq controller driver
> to use generic irq chip. It builds on the series that extends the
> generic chip code to allow a linear irq domain to contain one or more
> generic irq chips so that each interrupt controller doesn't need to hand
> code the generic chip setup.

NAK, this makes functional changes. You assume that the validity mask is
a set of zeros followed by a set of ones. This is not always the case.
The PIC on Integrator/CP only has bits 29-22 and 11-0 set because 21-12
are not valid.

2013-06-10 10:33:41

by Grant Likely

[permalink] [raw]
Subject: Re: [RFC 10/10] irqchip: Make versatile fpga irq driver a generic chip

On Mon, Jun 10, 2013 at 10:03 AM, Russell King - ARM Linux
<[email protected]> wrote:
> On Mon, Jun 10, 2013 at 01:49:22AM +0100, Grant Likely wrote:
>> This is an RFC patch to convert the versatile FPGA irq controller driver
>> to use generic irq chip. It builds on the series that extends the
>> generic chip code to allow a linear irq domain to contain one or more
>> generic irq chips so that each interrupt controller doesn't need to hand
>> code the generic chip setup.
>
> NAK, this makes functional changes. You assume that the validity mask is
> a set of zeros followed by a set of ones. This is not always the case.
> The PIC on Integrator/CP only has bits 29-22 and 11-0 set because 21-12
> are not valid.

Actually, I don't (at least I don't intend to). It chooses the size of
the irq_domain based on fls(), but that is only because the size has
to allocates for both the holes and the active irqs. You'll notice
that gc->unused is set to ~valid, and the generic chip code will
refuse to map in interrupt that isn't supported. If you see something
different, please point out where because that would be a bug.

g.

2013-06-10 10:50:29

by Grant Likely

[permalink] [raw]
Subject: Re: [RFC 10/10] irqchip: Make versatile fpga irq driver a generic chip

On Mon, Jun 10, 2013 at 8:40 AM, Linus Walleij <[email protected]> wrote:
> On Mon, Jun 10, 2013 at 2:49 AM, Grant Likely <[email protected]> wrote:
>
>> This is an RFC patch to convert the versatile FPGA irq controller driver
>> to use generic irq chip. It builds on the series that extends the
>> generic chip code to allow a linear irq domain to contain one or more
>> generic irq chips so that each interrupt controller doesn't need to hand
>> code the generic chip setup.
>>
>> I've written this as a proof of concept to see if the new generic irq
>> code does what it needs to. I had to extend it slightly to properly
>> handle the valid mask used by the versatile FPGA driver.
>>
>> Tested on QEMU, but not on real hardware.
>
> Is this the same as the one I tested previously?
>
> If it need re-testing please push a branch and I'll take it
> for a spin.

Yes, it's the same, but if you can test the branch I would appreciate
it. I've done some heavy rework on the irqdomain code that makes
everything simpler, but also makes it likely that I've broken
something.

git://git.secretlab.ca/git/linux.git irqdomain/test

g.

2013-06-15 21:19:45

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC 10/10] irqchip: Make versatile fpga irq driver a generic chip

On Mon, Jun 10, 2013 at 12:50 PM, Grant Likely <[email protected]> wrote:
> On Mon, Jun 10, 2013 at 8:40 AM, Linus Walleij <[email protected]> wrote:
>> On Mon, Jun 10, 2013 at 2:49 AM, Grant Likely <[email protected]> wrote:
>>
>>> This is an RFC patch to convert the versatile FPGA irq controller driver
>>> to use generic irq chip. It builds on the series that extends the
>>> generic chip code to allow a linear irq domain to contain one or more
>>> generic irq chips so that each interrupt controller doesn't need to hand
>>> code the generic chip setup.
>>>
>>> I've written this as a proof of concept to see if the new generic irq
>>> code does what it needs to. I had to extend it slightly to properly
>>> handle the valid mask used by the versatile FPGA driver.
>>>
>>> Tested on QEMU, but not on real hardware.
>>
>> Is this the same as the one I tested previously?
>>
>> If it need re-testing please push a branch and I'll take it
>> for a spin.
>
> Yes, it's the same, but if you can test the branch I would appreciate
> it. I've done some heavy rework on the irqdomain code that makes
> everything simpler, but also makes it likely that I've broken
> something.
>
> git://git.secretlab.ca/git/linux.git irqdomain/test

It still works like a charm on the Integrator/AP.
Tested-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2013-06-15 21:22:39

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC 10/10] irqchip: Make versatile fpga irq driver a generic chip

On Sat, Jun 15, 2013 at 11:19 PM, Linus Walleij
<[email protected]> wrote:
> On Mon, Jun 10, 2013 at 12:50 PM, Grant Likely <[email protected]> wrote:
>> On Mon, Jun 10, 2013 at 8:40 AM, Linus Walleij <[email protected]> wrote:
>>> On Mon, Jun 10, 2013 at 2:49 AM, Grant Likely <[email protected]> wrote:
>>>
>>>> This is an RFC patch to convert the versatile FPGA irq controller driver
>>>> to use generic irq chip. It builds on the series that extends the
>>>> generic chip code to allow a linear irq domain to contain one or more
>>>> generic irq chips so that each interrupt controller doesn't need to hand
>>>> code the generic chip setup.
>>>>
>>>> I've written this as a proof of concept to see if the new generic irq
>>>> code does what it needs to. I had to extend it slightly to properly
>>>> handle the valid mask used by the versatile FPGA driver.
>>>>
>>>> Tested on QEMU, but not on real hardware.
>>>
>>> Is this the same as the one I tested previously?
>>>
>>> If it need re-testing please push a branch and I'll take it
>>> for a spin.
>>
>> Yes, it's the same, but if you can test the branch I would appreciate
>> it. I've done some heavy rework on the irqdomain code that makes
>> everything simpler, but also makes it likely that I've broken
>> something.
>>
>> git://git.secretlab.ca/git/linux.git irqdomain/test
>
> It still works like a charm on the Integrator/AP.
> Tested-by: Linus Walleij <[email protected]>

BTW here is the new hwirq output in /proc/interrupts and it's really nice:

root@integrator:/proc cat interrupts
CPU0
17: 1845 pic 1 uart-pl010
22: 13716 pic 6 timer
24: 0 pic 8 rtc-pl030
Err: 0

Yours,
Linus Walleij

2013-06-15 22:48:43

by Grant Likely

[permalink] [raw]
Subject: Re: [RFC 10/10] irqchip: Make versatile fpga irq driver a generic chip

On Sat, Jun 15, 2013 at 10:22 PM, Linus Walleij
<[email protected]> wrote:
> On Sat, Jun 15, 2013 at 11:19 PM, Linus Walleij
> <[email protected]> wrote:
>> It still works like a charm on the Integrator/AP.
>> Tested-by: Linus Walleij <[email protected]>
>
> BTW here is the new hwirq output in /proc/interrupts and it's really nice:
>
> root@integrator:/proc cat interrupts
> CPU0
> 17: 1845 pic 1 uart-pl010
> 22: 13716 pic 6 timer
> 24: 0 pic 8 rtc-pl030
> Err: 0

Glad you like it. :-)

g.

2013-06-18 03:09:37

by Mike Qiu

[permalink] [raw]
Subject: Re: [RFC 08/10] irqdomain: Refactor irq_domain_associate_many()

于 2013/6/10 8:49, Grant Likely 写道:
> Originally, irq_domain_associate_many() was designed to unwind the
> mapped irqs on a failure of any individual association. However, that
> proved to be a problem with certain IRQ controllers. Some of them only
> support a subset of irqs, and will fail when attempting to map a
> reserved IRQ. In those cases we want to map as many IRQs as possible, so
> instead it is better for irq_domain_associate_many() to make a
> best-effort attempt to map irqs, but not fail if any or all of them
> don't succeed. If a caller really cares about how many irqs got
> associated, then it should instead go back and check that all of the
> irqs is cares about were mapped.
>
> The original design open-coded the individual association code into the
> body of irq_domain_associate_many(), but with no longer needing to
> unwind associations, the code becomes simpler to split out
> irq_domain_associate() to contain the bulk of the logic, and
> irq_domain_associate_many() to be a simple loop wrapper.
>
> This patch also adds a new error check to the associate path to make
> sure it isn't called for an irq larger than the controller can handle,
> and adds locking so that the irq_domain_mutex is held while setting up a
> new association.
>
> Signed-off-by: Grant Likely <[email protected]>
> ---
> include/linux/irqdomain.h | 22 +++---
> kernel/irq/irqdomain.c | 185 +++++++++++++++++++++++-----------------------
> 2 files changed, 101 insertions(+), 106 deletions(-)
>
> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> index fd4b26f..f9e8e06 100644
> --- a/include/linux/irqdomain.h
> +++ b/include/linux/irqdomain.h
> @@ -103,6 +103,7 @@ struct irq_domain {
> struct irq_domain_chip_generic *gc;
>
> /* reverse map data. The linear map gets appended to the irq_domain */
> + irq_hw_number_t hwirq_max;
> unsigned int revmap_direct_max_irq;
> unsigned int revmap_size;
> struct radix_tree_root revmap_tree;
> @@ -110,8 +111,8 @@ struct irq_domain {
> };
>
> #ifdef CONFIG_IRQ_DOMAIN
> -struct irq_domain *__irq_domain_add(struct device_node *of_node,
> - int size, int direct_max,
> +struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
> + irq_hw_number_t hwirq_max, int direct_max,
> const struct irq_domain_ops *ops,
> void *host_data);
> struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> @@ -140,14 +141,14 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> - return __irq_domain_add(of_node, size, 0, ops, host_data);
> + return __irq_domain_add(of_node, size, size, 0, ops, host_data);
> }
> static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
> unsigned int max_irq,
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> - return __irq_domain_add(of_node, 0, max_irq, ops, host_data);
> + return __irq_domain_add(of_node, 0, max_irq, max_irq, ops, host_data);
> }
> static inline struct irq_domain *irq_domain_add_legacy_isa(
> struct device_node *of_node,
> @@ -166,14 +167,11 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
>
> extern void irq_domain_remove(struct irq_domain *host);
>
> -extern int irq_domain_associate_many(struct irq_domain *domain,
> - unsigned int irq_base,
> - irq_hw_number_t hwirq_base, int count);
> -static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
> - irq_hw_number_t hwirq)
> -{
> - return irq_domain_associate_many(domain, irq, hwirq, 1);
> -}
> +extern int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
> + irq_hw_number_t hwirq);
> +extern void irq_domain_associate_many(struct irq_domain *domain,
> + unsigned int irq_base,
> + irq_hw_number_t hwirq_base, int count);
>
> extern unsigned int irq_create_mapping(struct irq_domain *host,
> irq_hw_number_t hwirq);
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index 280b804..80e9249 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -35,8 +35,8 @@ static struct irq_domain *irq_default_domain;
> * register allocated irq_domain with irq_domain_register(). Returns pointer
> * to IRQ domain, or NULL on failure.
> */
> -struct irq_domain *__irq_domain_add(struct device_node *of_node,
> - int size, int direct_max,
> +struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
> + irq_hw_number_t hwirq_max, int direct_max,
> const struct irq_domain_ops *ops,
> void *host_data)
> {
> @@ -52,6 +52,7 @@ struct irq_domain *__irq_domain_add(struct device_node *of_node,
> domain->ops = ops;
> domain->host_data = host_data;
> domain->of_node = of_node_get(of_node);
> + domain->hwirq_max = hwirq_max;
> domain->revmap_size = size;
> domain->revmap_direct_max_irq = direct_max;
>
> @@ -126,7 +127,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> {
> struct irq_domain *domain;
>
> - domain = __irq_domain_add(of_node, size, 0, ops, host_data);
> + domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
> if (!domain)
> return NULL;
>
> @@ -139,7 +140,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
> pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
> first_irq);
> }
> - WARN_ON(irq_domain_associate_many(domain, first_irq, 0, size));
> + irq_domain_associate_many(domain, first_irq, 0, size);
> }
>
> return domain;
> @@ -170,11 +171,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
> {
> struct irq_domain *domain;
>
> - domain = __irq_domain_add(of_node, first_hwirq + size, 0, ops, host_data);
> + domain = __irq_domain_add(of_node, first_hwirq + size,
> + first_hwirq + size, 0, ops, host_data);
> if (!domain)
> return NULL;
>
> - WARN_ON(irq_domain_associate_many(domain, first_irq, first_hwirq, size));
> + irq_domain_associate_many(domain, first_irq, first_hwirq, size);
>
> return domain;
> }
> @@ -228,109 +230,109 @@ void irq_set_default_host(struct irq_domain *domain)
> }
> EXPORT_SYMBOL_GPL(irq_set_default_host);
>
> -static void irq_domain_disassociate_many(struct irq_domain *domain,
> - unsigned int irq_base, int count)
> +static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
> {
> - /*
> - * disassociate in reverse order;
> - * not strictly necessary, but nice for unwinding
> - */
> - while (count--) {
> - int irq = irq_base + count;
> - struct irq_data *irq_data = irq_get_irq_data(irq);
> - irq_hw_number_t hwirq;
> + struct irq_data *irq_data = irq_get_irq_data(irq);
> + irq_hw_number_t hwirq;
>
> - if (WARN_ON(!irq_data || irq_data->domain != domain))
> - continue;
> + if (WARN(!irq_data || irq_data->domain != domain,
> + "virq%i doesn't exist; cannot disassociate\n", irq))
> + return;
>
> - hwirq = irq_data->hwirq;
> - irq_set_status_flags(irq, IRQ_NOREQUEST);
> + hwirq = irq_data->hwirq;
> + irq_set_status_flags(irq, IRQ_NOREQUEST);
>
> - /* remove chip and handler */
> - irq_set_chip_and_handler(irq, NULL, NULL);
> + /* remove chip and handler */
> + irq_set_chip_and_handler(irq, NULL, NULL);
>
> - /* Make sure it's completed */
> - synchronize_irq(irq);
> + /* Make sure it's completed */
> + synchronize_irq(irq);
>
> - /* Tell the PIC about it */
> - if (domain->ops->unmap)
> - domain->ops->unmap(domain, irq);
> - smp_mb();
> + /* Tell the PIC about it */
> + if (domain->ops->unmap)
> + domain->ops->unmap(domain, irq);
> + smp_mb();
>
> - irq_data->domain = NULL;
> - irq_data->hwirq = 0;
> + irq_data->domain = NULL;
> + irq_data->hwirq = 0;
>
> - /* Clear reverse map for this hwirq */
> - if (hwirq < domain->revmap_size) {
> - domain->linear_revmap[hwirq] = 0;
> - } else {
> - mutex_lock(&revmap_trees_mutex);
> - radix_tree_delete(&domain->revmap_tree, hwirq);
> - mutex_unlock(&revmap_trees_mutex);
> - }
> + /* Clear reverse map for this hwirq */
> + if (hwirq < domain->revmap_size) {
> + domain->linear_revmap[hwirq] = 0;
> + } else {
> + mutex_lock(&revmap_trees_mutex);
> + radix_tree_delete(&domain->revmap_tree, hwirq);
> + mutex_unlock(&revmap_trees_mutex);
> }
> }
>
> -int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
> - irq_hw_number_t hwirq_base, int count)
> +int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
> + irq_hw_number_t hwirq)
> {
> - unsigned int virq = irq_base;
> - irq_hw_number_t hwirq = hwirq_base;
> - int i, ret;
> + struct irq_data *irq_data = irq_get_irq_data(virq);
> + int ret;
>
> - pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
> - of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
> + if (WARN(hwirq >= domain->hwirq_max,
> + "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
> + return -EINVAL;
> + if (WARN(!irq_data, "error: virq%i is not allocated", virq))
> + return -EINVAL;
> + if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
> + return -EINVAL;

Hi Grant,

I have a qestion here, assume that we have three hwirqs and alloc three
virqs, for first irq, it check irq_data
and irq_data->domain pass, but the second is failed, then this code do
nothing with failed( in

irq_domain_associate_many()) and continue to associated the third one.


This should be very dangours, even though I have no idea of when this
could happen.

Thanks

Mike
> - for (i = 0; i < count; i++) {
> - struct irq_data *irq_data = irq_get_irq_data(virq + i);
> -
> - if (WARN(!irq_data, "error: irq_desc not allocated; "
> - "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
> - return -EINVAL;
> - if (WARN(irq_data->domain, "error: irq_desc already associated; "
> - "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
> - return -EINVAL;
> - };
> -
> - for (i = 0; i < count; i++, virq++, hwirq++) {
> - struct irq_data *irq_data = irq_get_irq_data(virq);
> -
> - irq_data->hwirq = hwirq;
> - irq_data->domain = domain;
> - if (domain->ops->map) {
> - ret = domain->ops->map(domain, virq, hwirq);
> - if (ret != 0) {
> - /*
> - * If map() returns -EPERM, this interrupt is protected
> - * by the firmware or some other service and shall not
> - * be mapped. Don't bother telling the user about it.
> - */
> - if (ret != -EPERM) {
> - pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
> - domain->name, hwirq, virq, ret);
> - }
> - irq_data->domain = NULL;
> - irq_data->hwirq = 0;
> - continue;
> + mutex_lock(&irq_domain_mutex);
> + irq_data->hwirq = hwirq;
> + irq_data->domain = domain;
> + if (domain->ops->map) {
> + ret = domain->ops->map(domain, virq, hwirq);
> + if (ret != 0) {
> + /*
> + * If map() returns -EPERM, this interrupt is protected
> + * by the firmware or some other service and shall not
> + * be mapped. Don't bother telling the user about it.
> + */
> + if (ret != -EPERM) {
> + pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
> + domain->name, hwirq, virq, ret);
> }
> - /* If not already assigned, give the domain the chip's name */
> - if (!domain->name && irq_data->chip)
> - domain->name = irq_data->chip->name;
> + irq_data->domain = NULL;
> + irq_data->hwirq = 0;
> + mutex_unlock(&irq_domain_mutex);
> + return ret;
> }
>
> - if (hwirq < domain->revmap_size) {
> - domain->linear_revmap[hwirq] = virq;
> - } else {
> - mutex_lock(&revmap_trees_mutex);
> - radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
> - mutex_unlock(&revmap_trees_mutex);
> - }
> + /* If not already assigned, give the domain the chip's name */
> + if (!domain->name && irq_data->chip)
> + domain->name = irq_data->chip->name;
> + }
>
> - irq_clear_status_flags(virq, IRQ_NOREQUEST);
> + if (hwirq < domain->revmap_size) {
> + domain->linear_revmap[hwirq] = virq;
> + } else {
> + mutex_lock(&revmap_trees_mutex);
> + radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
> + mutex_unlock(&revmap_trees_mutex);
> }
> + mutex_unlock(&irq_domain_mutex);
> +
> + irq_clear_status_flags(virq, IRQ_NOREQUEST);
>
> return 0;
> }
> +EXPORT_SYMBOL_GPL(irq_domain_associate);
> +
> +void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
> + irq_hw_number_t hwirq_base, int count)
> +{
> + int i;
> +
> + pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
> + of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
> +
> + for (i = 0; i < count; i++) {
> + irq_domain_associate(domain, irq_base + i, hwirq_base + i);
> + }
> +}
> EXPORT_SYMBOL_GPL(irq_domain_associate_many);
>
> /**
> @@ -460,12 +462,7 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
> if (unlikely(ret < 0))
> return ret;
>
> - ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count);
> - if (unlikely(ret < 0)) {
> - irq_free_descs(irq_base, count);
> - return ret;
> - }
> -
> + irq_domain_associate_many(domain, irq_base, hwirq_base, count);
> return 0;
> }
> EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
> @@ -535,7 +532,7 @@ void irq_dispose_mapping(unsigned int virq)
> if (WARN_ON(domain == NULL))
> return;
>
> - irq_domain_disassociate_many(domain, virq, 1);
> + irq_domain_disassociate(domain, virq);
> irq_free_desc(virq);
> }
> EXPORT_SYMBOL_GPL(irq_dispose_mapping);

2013-06-18 08:55:25

by Grant Likely

[permalink] [raw]
Subject: Re: [RFC 08/10] irqdomain: Refactor irq_domain_associate_many()

On Tue, Jun 18, 2013 at 4:09 AM, Mike Qiu <[email protected]> wrote:
> 于 2013/6/10 8:49, Grant Likely 写道:
>
>> Originally, irq_domain_associate_many() was designed to unwind the
>> mapped irqs on a failure of any individual association. However, that
>> proved to be a problem with certain IRQ controllers. Some of them only
>> support a subset of irqs, and will fail when attempting to map a
>> reserved IRQ. In those cases we want to map as many IRQs as possible, so
>> instead it is better for irq_domain_associate_many() to make a
>> best-effort attempt to map irqs, but not fail if any or all of them
>> don't succeed. If a caller really cares about how many irqs got
>> associated, then it should instead go back and check that all of the
>> irqs is cares about were mapped.
>>
>> The original design open-coded the individual association code into the
>> body of irq_domain_associate_many(), but with no longer needing to
>> unwind associations, the code becomes simpler to split out
>> irq_domain_associate() to contain the bulk of the logic, and
>> irq_domain_associate_many() to be a simple loop wrapper.
>>
>> This patch also adds a new error check to the associate path to make
>> sure it isn't called for an irq larger than the controller can handle,
>> and adds locking so that the irq_domain_mutex is held while setting up a
>> new association.
>>
>> Signed-off-by: Grant Likely <[email protected]>
[...]
>> -int irq_domain_associate_many(struct irq_domain *domain, unsigned int
>> irq_base,
>> - irq_hw_number_t hwirq_base, int count)
>> +int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
>> + irq_hw_number_t hwirq)
>> {
>> - unsigned int virq = irq_base;
>> - irq_hw_number_t hwirq = hwirq_base;
>> - int i, ret;>> + struct irq_data *irq_data = irq_get_irq_data(virq);
>> + int ret;
>>
>> - pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
>> - of_node_full_name(domain->of_node), irq_base,
>> (int)hwirq_base, count);
>> + if (WARN(hwirq >= domain->hwirq_max,
>> + "error: hwirq 0x%x is too large for %s\n", (int)hwirq,
>> domain->name))
>> + return -EINVAL;
>> + if (WARN(!irq_data, "error: virq%i is not allocated", virq))
>> + return -EINVAL;
>> + if (WARN(irq_data->domain, "error: virq%i is already associated",
>> virq))
>> + return -EINVAL;
>
>
> Hi Grant,
>
> I have a qestion here, assume that we have three hwirqs and alloc three
> virqs, for first irq, it check irq_data
> and irq_data->domain pass, but the second is failed, then this code do
> nothing with failed( in
>
> irq_domain_associate_many()) and continue to associated the third one.
>
>
> This should be very dangours, even though I have no idea of when this could
> happen.

Define what you mean by "dangerous". You are correct that it is a bad
situation, and the kernel will complain very loudly if it happens.
However, the users of irq_domain_associate_many() are usually irq
controllers using a legacy domain that needs to be set up during early
boot. If the IRQ controller setup fails and gets unwound, then it is
very likely that there will be no output at all from the kernel. This
has actually been a problem on a number of platforms and it is a big
part of why this irqdomain refactoring series wasn't merged 6 months
ago.

I think it is better to relax the behaviour of
irq_domain_associate_many() so that if something fails, then at least
it can limp along and try to get at least some of the irqs associated.
That way we can find and fix problematic platforms rather than failing
silently.

Also, I don't see anything particularly dangerous about this behaviour
other than having some of the hwirqs not being associated with a virq.
The code protects against this path and if an irq controller receives
an IRQ on a unassociated hwirq, then the kernel will also log that as
an error.

g.