All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marc Zyngier <maz@kernel.org>
To: linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Cc: Thierry Reding <thierry.reding@gmail.com>,
	Jonathan Hunter <jonathanh@nvidia.com>,
	Dmitry Osipenko <digetx@gmail.com>,
	Sowjanya Komatineni <skomatineni@nvidia.com>,
	Venkat Reddy Talla <vreddytalla@nvidia.com>,
	Thomas Gleixner <tglx@linutronix.de>,
	kernel-team@android.com
Subject: [PATCH v3 1/4] genirq/irqdomain: Allow partial trimming of irq_data hierarchy
Date: Wed,  7 Oct 2020 13:45:41 +0100	[thread overview]
Message-ID: <20201007124544.1397322-2-maz@kernel.org> (raw)
In-Reply-To: <20201007124544.1397322-1-maz@kernel.org>

It appears that some HW is ugly enough that not all the interrupts
connected to a particular interrupt controller end up with the same
hierarchy depth (some of them are terminated early). This leaves
the irqchip hacker with only two choices, both equally bad:

- create discrete domain chains, one for each "hierarchy depth",
  which is very hard to maintain

- create fake hierarchy levels for the shallow paths, leading
  to all kind of problems (what are the safe hwirq values for these
  fake levels?)

Implement the ability to cut short a single interrupt hierarchy
from the first level that doesn't have a corresponding irqchip.
As this is never a valid option (we have the no_irq_chip chip
for the "do nothing" case), the hierarchy can be trimmed from
that level.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 kernel/irq/irqdomain.c | 58 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 6 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 76cd7ebd1178..375eb2b79fe5 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1136,6 +1136,17 @@ static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
 	return irq_data;
 }
 
+static void __irq_domain_free_hierarchy(struct irq_data *irq_data)
+{
+	struct irq_data *tmp;
+
+	while (irq_data) {
+		tmp = irq_data;
+		irq_data = irq_data->parent_data;
+		kfree(tmp);
+	}
+}
+
 static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
 {
 	struct irq_data *irq_data, *tmp;
@@ -1147,14 +1158,47 @@ static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
 		irq_data->parent_data = NULL;
 		irq_data->domain = NULL;
 
-		while (tmp) {
-			irq_data = tmp;
-			tmp = tmp->parent_data;
-			kfree(irq_data);
-		}
+		__irq_domain_free_hierarchy(tmp);
 	}
 }
 
+/**
+ * irq_domain_trim_hierarchy - Trim the uninitialized part of a irq hierarchy
+ * @virq:	IRQ number to trim where the hierarchy is to be trimmed
+ *
+ * Drop the partial irq_data hierarchy from the level where the
+ * irq_data->chip is NULL.
+ *
+ * Its only use is to be able to trim levels of hierarchy that do not
+ * have any real meaning for this interrupt, and that the driver leaves
+ * uninitialized in its .alloc() callback.
+ */
+static void irq_domain_trim_hierarchy(unsigned int virq)
+{
+	struct irq_data *tail, *irq_data = irq_get_irq_data(virq);
+
+	/* It really needs to be a hierarchy, and not a single entry */
+	if (!irq_data->parent_data)
+		return;
+
+	/* Skip until we find a parent irq_data without a populated chip */
+	while (irq_data->parent_data && irq_data->parent_data->chip)
+		irq_data = irq_data->parent_data;
+
+	/* All levels populated */
+	if (!irq_data->parent_data)
+		return;
+
+	pr_info("IRQ%d: trimming hierarchy from %s\n",
+		virq, irq_data->parent_data->domain->name);
+
+	/* Sever the inner part of the hierarchy...  */
+	tail = irq_data->parent_data;
+	irq_data->parent_data = NULL;
+	__irq_domain_free_hierarchy(tail);
+}
+
+
 static int irq_domain_alloc_irq_data(struct irq_domain *domain,
 				     unsigned int virq, unsigned int nr_irqs)
 {
@@ -1362,8 +1406,10 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 		mutex_unlock(&irq_domain_mutex);
 		goto out_free_irq_data;
 	}
-	for (i = 0; i < nr_irqs; i++)
+	for (i = 0; i < nr_irqs; i++) {
+		irq_domain_trim_hierarchy(virq + i);
 		irq_domain_insert_irq(virq + i);
+	}
 	mutex_unlock(&irq_domain_mutex);
 
 	return virq;
-- 
2.28.0


WARNING: multiple messages have this Message-ID (diff)
From: Marc Zyngier <maz@kernel.org>
To: linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Cc: Venkat Reddy Talla <vreddytalla@nvidia.com>,
	Jonathan Hunter <jonathanh@nvidia.com>,
	Thierry Reding <thierry.reding@gmail.com>,
	Sowjanya Komatineni <skomatineni@nvidia.com>,
	Dmitry Osipenko <digetx@gmail.com>,
	kernel-team@android.com, Thomas Gleixner <tglx@linutronix.de>
Subject: [PATCH v3 1/4] genirq/irqdomain: Allow partial trimming of irq_data hierarchy
Date: Wed,  7 Oct 2020 13:45:41 +0100	[thread overview]
Message-ID: <20201007124544.1397322-2-maz@kernel.org> (raw)
In-Reply-To: <20201007124544.1397322-1-maz@kernel.org>

It appears that some HW is ugly enough that not all the interrupts
connected to a particular interrupt controller end up with the same
hierarchy depth (some of them are terminated early). This leaves
the irqchip hacker with only two choices, both equally bad:

- create discrete domain chains, one for each "hierarchy depth",
  which is very hard to maintain

- create fake hierarchy levels for the shallow paths, leading
  to all kind of problems (what are the safe hwirq values for these
  fake levels?)

Implement the ability to cut short a single interrupt hierarchy
from the first level that doesn't have a corresponding irqchip.
As this is never a valid option (we have the no_irq_chip chip
for the "do nothing" case), the hierarchy can be trimmed from
that level.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 kernel/irq/irqdomain.c | 58 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 6 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 76cd7ebd1178..375eb2b79fe5 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1136,6 +1136,17 @@ static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
 	return irq_data;
 }
 
+static void __irq_domain_free_hierarchy(struct irq_data *irq_data)
+{
+	struct irq_data *tmp;
+
+	while (irq_data) {
+		tmp = irq_data;
+		irq_data = irq_data->parent_data;
+		kfree(tmp);
+	}
+}
+
 static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
 {
 	struct irq_data *irq_data, *tmp;
@@ -1147,14 +1158,47 @@ static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
 		irq_data->parent_data = NULL;
 		irq_data->domain = NULL;
 
-		while (tmp) {
-			irq_data = tmp;
-			tmp = tmp->parent_data;
-			kfree(irq_data);
-		}
+		__irq_domain_free_hierarchy(tmp);
 	}
 }
 
+/**
+ * irq_domain_trim_hierarchy - Trim the uninitialized part of a irq hierarchy
+ * @virq:	IRQ number to trim where the hierarchy is to be trimmed
+ *
+ * Drop the partial irq_data hierarchy from the level where the
+ * irq_data->chip is NULL.
+ *
+ * Its only use is to be able to trim levels of hierarchy that do not
+ * have any real meaning for this interrupt, and that the driver leaves
+ * uninitialized in its .alloc() callback.
+ */
+static void irq_domain_trim_hierarchy(unsigned int virq)
+{
+	struct irq_data *tail, *irq_data = irq_get_irq_data(virq);
+
+	/* It really needs to be a hierarchy, and not a single entry */
+	if (!irq_data->parent_data)
+		return;
+
+	/* Skip until we find a parent irq_data without a populated chip */
+	while (irq_data->parent_data && irq_data->parent_data->chip)
+		irq_data = irq_data->parent_data;
+
+	/* All levels populated */
+	if (!irq_data->parent_data)
+		return;
+
+	pr_info("IRQ%d: trimming hierarchy from %s\n",
+		virq, irq_data->parent_data->domain->name);
+
+	/* Sever the inner part of the hierarchy...  */
+	tail = irq_data->parent_data;
+	irq_data->parent_data = NULL;
+	__irq_domain_free_hierarchy(tail);
+}
+
+
 static int irq_domain_alloc_irq_data(struct irq_domain *domain,
 				     unsigned int virq, unsigned int nr_irqs)
 {
@@ -1362,8 +1406,10 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
 		mutex_unlock(&irq_domain_mutex);
 		goto out_free_irq_data;
 	}
-	for (i = 0; i < nr_irqs; i++)
+	for (i = 0; i < nr_irqs; i++) {
+		irq_domain_trim_hierarchy(virq + i);
 		irq_domain_insert_irq(virq + i);
+	}
 	mutex_unlock(&irq_domain_mutex);
 
 	return virq;
-- 
2.28.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2020-10-07 12:45 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-07 12:45 [PATCH v3 0/4] soc/tegra: Prevent the PMC driver from corrupting interrupt routing Marc Zyngier
2020-10-07 12:45 ` Marc Zyngier
2020-10-07 12:45 ` Marc Zyngier [this message]
2020-10-07 12:45   ` [PATCH v3 1/4] genirq/irqdomain: Allow partial trimming of irq_data hierarchy Marc Zyngier
2020-10-08 11:22   ` Thomas Gleixner
2020-10-08 11:22     ` Thomas Gleixner
2020-10-08 13:06     ` Marc Zyngier
2020-10-08 13:06       ` Marc Zyngier
2020-10-08 20:47       ` Thomas Gleixner
2020-10-08 20:47         ` Thomas Gleixner
2020-10-10  9:42         ` Marc Zyngier
2020-10-10  9:42           ` Marc Zyngier
2020-10-07 12:45 ` [PATCH v3 2/4] gpio: tegra186: Allow optional irq parent callbacks Marc Zyngier
2020-10-07 12:45   ` Marc Zyngier
2020-10-07 12:45 ` [PATCH v3 3/4] soc/tegra: pmc: " Marc Zyngier
2020-10-07 12:45   ` Marc Zyngier
2020-10-07 12:45 ` [PATCH v3 4/4] soc/tegra: pmc: Don't create fake interrupt hierarchy levels Marc Zyngier
2020-10-07 12:45   ` Marc Zyngier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201007124544.1397322-2-maz@kernel.org \
    --to=maz@kernel.org \
    --cc=digetx@gmail.com \
    --cc=jonathanh@nvidia.com \
    --cc=kernel-team@android.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=skomatineni@nvidia.com \
    --cc=tglx@linutronix.de \
    --cc=thierry.reding@gmail.com \
    --cc=vreddytalla@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.