[Skiboot] [PATCH] interrupts: Speed up opal interrupts scanning

Frederic Barrat fbarrat at linux.ibm.com
Thu Jun 16 00:21:41 AEST 2022


When looking for which interrupts are serviced by opal, we scan all
sources and query every single interrupt to know if it's for linux or
opal. An optimization was made so that if the source doesn't have an
'interrupt' op (=the handler) or an 'attributes' op (to do the
query), then we can skip the source. That's all good.

However, when xive was introduced, the 'irq_source' defining those ops
was wrapped in a 'xive_src' source which adds a level of indirection
for those 'attributes' and 'interrupt' ops. So the previous
optimization no longer works: the 'attributes' and 'interrupt' ops are
defined from the wrapper, but if we could look past the indirection,
we would realize they are not.

That is getting problematic for the rather large generic IPIs
source. We have 8 million such interrupts defined per chip on P10 and
because the above optimization is no longer kicking in, we are now
querying every single one of them to know if it is for opal. Real
hardware swallows it without much difficulty, but simulators
don't. Running qemu on my laptop, the full scan takes ~12 seconds per
chip!

This patch adds a callback for an interrupt source to report whether
it has opal interrupts. If the source doesn't define it, then we
fallback to looking at the 'interrupt' and 'attributes' ops, like
before, as it is still useful on P8. We can then define that new
callback on the xive sources, allowing to look past the indirection
level and skip scanning the source when appropriate.

Signed-off-by: Frederic Barrat <fbarrat at linux.ibm.com>
---
 core/interrupts.c    | 22 +++++++++++++++++-----
 hw/xive.c            | 10 ++++++++++
 hw/xive2.c           | 10 ++++++++++
 include/interrupts.h |  1 +
 4 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/core/interrupts.c b/core/interrupts.c
index 35571f28..03a37b2a 100644
--- a/core/interrupts.c
+++ b/core/interrupts.c
@@ -199,6 +199,21 @@ uint32_t get_ics_phandle(void)
 	abort();
 }
 
+static bool source_has_opal_interrupts(struct irq_source *is)
+{
+	/* check with the source first */
+	if (is->ops->has_opal_interrupts)
+		return is->ops->has_opal_interrupts(is);
+	/*
+	 * Default case: to handle an interrupt in opal, a source
+	 * needs at least an attribute callback to declare it and a
+	 * handler
+	 */
+	if (!is->ops->interrupt || !is->ops->attributes)
+		return false;
+	return true;
+}
+
 void add_opal_interrupts(void)
 {
 	struct irq_source *is;
@@ -214,11 +229,8 @@ void add_opal_interrupts(void)
 
 	lock(&irq_lock);
 	list_for_each(&irq_sources, is, link) {
-		/*
-		 * Don't even consider sources that don't have an interrupts
-		 * callback or don't have an attributes one.
-		 */
-		if (!is->ops->interrupt || !is->ops->attributes)
+
+		if (!source_has_opal_interrupts(is))
 			continue;
 		for (isn = is->start; isn < is->end; isn++) {
 			uint64_t attr = is->ops->attributes(is, isn);
diff --git a/hw/xive.c b/hw/xive.c
index 60552763..bf711ada 100644
--- a/hw/xive.c
+++ b/hw/xive.c
@@ -2520,6 +2520,15 @@ void xive_source_mask(struct irq_source *is, uint32_t isn)
 	xive_update_irq_mask(s, isn - s->esb_base, true);
 }
 
+static bool xive_has_opal_interrupts(struct irq_source *is)
+{
+	struct xive_src *s = container_of(is, struct xive_src, is);
+
+	if (!s->orig_ops || !s->orig_ops->attributes || !s->orig_ops->interrupt)
+		return false;
+	return true;
+}
+
 static const struct irq_source_ops xive_irq_source_ops = {
 	.get_xive = xive_source_get_xive,
 	.set_xive = xive_source_set_xive,
@@ -2527,6 +2536,7 @@ static const struct irq_source_ops xive_irq_source_ops = {
 	.interrupt = xive_source_interrupt,
 	.attributes = xive_source_attributes,
 	.name = xive_source_name,
+	.has_opal_interrupts = xive_has_opal_interrupts,
 };
 
 static void __xive_register_source(struct xive *x, struct xive_src *s,
diff --git a/hw/xive2.c b/hw/xive2.c
index 8e2a1f2d..d20e3c5e 100644
--- a/hw/xive2.c
+++ b/hw/xive2.c
@@ -2644,10 +2644,20 @@ void xive2_source_mask(struct irq_source *is, uint32_t isn)
 	xive_update_irq_mask(s, isn - s->esb_base, true);
 }
 
+static bool xive_has_opal_interrupts(struct irq_source *is)
+{
+	struct xive_src *s = container_of(is, struct xive_src, is);
+
+	if (!s->orig_ops || !s->orig_ops->attributes || !s->orig_ops->interrupt)
+		return false;
+	return true;
+}
+
 static const struct irq_source_ops xive_irq_source_ops = {
 	.interrupt = xive_source_interrupt,
 	.attributes = xive_source_attributes,
 	.name = xive_source_name,
+	.has_opal_interrupts = xive_has_opal_interrupts,
 };
 
 static void __xive_register_source(struct xive *x, struct xive_src *s,
diff --git a/include/interrupts.h b/include/interrupts.h
index 7ffcc6c4..6958fa62 100644
--- a/include/interrupts.h
+++ b/include/interrupts.h
@@ -153,6 +153,7 @@ struct irq_source_ops {
 	void (*interrupt)(struct irq_source *is, uint32_t isn);
 	void (*eoi)(struct irq_source *is, uint32_t isn);
 	char *(*name)(struct irq_source *is, uint32_t isn);
+	bool (*has_opal_interrupts)(struct irq_source *is);
 };
 
 struct irq_source {
-- 
2.35.3



More information about the Skiboot mailing list