[PATCH v5 4/7] genirq: Integrate GSIM into interrupt flow

Luigi Rizzo <[email protected]>
Newsgroups org.kernel.vger.linux-pci,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Hook GSIM into edge/fasteoi handlers and teardown.

Specifically:
- Hook GSIM into handle_edge_irq() and handle_fasteoi_irq().
- Introduce `irq_moderation_allow(desc, allow)` to validate if an IRQ
  is eligible for GSIM (non-oneshot, edge/fasteoi, single target, no bus lock)
  and set/clear the IRQ_MODERATABLE flag.
- Integrate GSIM setup in __setup_irq() using `irq_moderation_allow()`,
  defaulting to disabled (`use_moderation = false`).
- Clear GSIM state in __free_irq() and __cleanup_nmi().

Note: At this stage, GSIM is disabled by default (use_moderation = false) and
cannot be enabled at runtime yet. Subsequent commits will introduce procfs
configuration and a configurable default mode. For testing this commit,
use_moderation can be manually set to true in the code.

Signed-off-by: Luigi Rizzo <[email protected]>
---
 kernel/irq/chip.c           | 14 +++++++++++++
 kernel/irq/internals.h      |  8 +++++++
 kernel/irq/irq_moderation.c | 42 +++++++++++++++++++++++++++++++++++++
 kernel/irq/manage.c         | 10 +++++++++
 4 files changed, 74 insertions(+)

diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index de754db414d1d..04a30ba04e78b 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -20,6 +20,7 @@
 #include <trace/events/irq.h>
 
 #include "internals.h"
+#include "irq_moderation.h"
 
 static irqreturn_t bad_chained_irq(int irq, void *dev_id)
 {
@@ -500,6 +501,10 @@ static bool irq_can_handle_pm(struct irq_desc *desc)
 		return false;
 	}
 
+	/* Moderated interrupts have IRQD_IRQ_INPROGRESS and need early return. */
+	if (irqd_is_moderated(irqd))
+		return false;
+
 	/* Check whether the interrupt is polled on another CPU */
 	if (unlikely(desc->istate & IRQS_POLL_INPROGRESS)) {
 		if (WARN_ONCE(irq_poll_cpu == smp_processor_id(),
@@ -749,6 +754,10 @@ void handle_fasteoi_irq(struct irq_desc *desc)
 	 * handling the previous one - it may need to be resent.
 	 */
 	if (!irq_can_handle_pm(desc)) {
+		if (irqd_is_moderated(&desc->irq_data)) {
+			desc->istate |= IRQS_PENDING;
+			mask_irq(desc);
+		}
 		if (irqd_needs_resend_when_in_progress(&desc->irq_data))
 			desc->istate |= IRQS_PENDING;
 		cond_eoi_irq(chip, &desc->irq_data);
@@ -769,6 +778,9 @@ void handle_fasteoi_irq(struct irq_desc *desc)
 
 	cond_unmask_eoi_irq(desc, chip);
 
+	if (irq_start_moderation(desc))
+		return;
+
 	/*
 	 * When the race described above happens this will resend the interrupt.
 	 */
@@ -858,6 +870,8 @@ void handle_edge_irq(struct irq_desc *desc)
 
 		handle_irq_event(desc);
 
+		if (irq_start_moderation(desc))
+			break;
 	} while ((desc->istate & IRQS_PENDING) && !irqd_irq_disabled(&desc->irq_data));
 }
 EXPORT_SYMBOL(handle_edge_irq);
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 716a7c2e3633a..705c3bfe28e12 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -400,6 +400,14 @@ static inline void irq_moderation_init_fields(struct irq_desc *desc)
 {
 	INIT_LIST_HEAD(&desc->swmod_state.swmod_node);
 }
+
+int irq_moderation_allow(struct irq_desc *desc, bool allow);
+bool irq_moderation_supported(struct irq_desc *desc);
 #else
 static inline void irq_moderation_init_fields(struct irq_desc *desc) {}
+static inline int irq_moderation_allow(struct irq_desc *desc, bool allow)
+{
+	return allow ? -EOPNOTSUPP : 0;
+}
+static inline bool irq_moderation_supported(struct irq_desc *desc) { return false; }
 #endif
diff --git a/kernel/irq/irq_moderation.c b/kernel/irq/irq_moderation.c
index 8f8893d952de0..2c75feb6634f3 100644
--- a/kernel/irq/irq_moderation.c
+++ b/kernel/irq/irq_moderation.c
@@ -294,3 +294,45 @@ static int __init init_irq_moderation(void)
 	return ret;
 }
 device_initcall(init_irq_moderation);
+
+bool irq_moderation_supported(struct irq_desc *desc)
+{
+	struct irq_data *irqd = &desc->irq_data;
+	struct irq_chip *chip = irqd->chip;
+
+	/* GSIM does not support shared interrupts */
+	if (desc->action && desc->action->next)
+		return false;
+
+	if (desc->istate & IRQS_ONESHOT)
+		return false;
+	if (irqd_is_level_type(irqd))
+		return false;
+	if (!irqd_is_single_target(irqd))
+		return false;
+	if (chip->irq_bus_lock || chip->irq_bus_sync_unlock)
+		return false;
+	if (!chip->irq_mask || !chip->irq_unmask)
+		return false;
+	if (desc->handle_irq != handle_edge_irq && desc->handle_irq != handle_fasteoi_irq)
+		return false;
+	return true;
+}
+
+int irq_moderation_allow(struct irq_desc *desc, bool allow)
+{
+	lockdep_assert_held(&desc->lock);
+
+	if (!allow) {
+		irq_settings_clr_moderatable(desc);
+		return 0;
+	}
+
+	if (!irq_moderation_supported(desc)) {
+		irq_settings_clr_moderatable(desc);
+		return -EOPNOTSUPP;
+	}
+
+	irq_settings_set_moderatable(desc);
+	return 0;
+}
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 7eb07e3bdb4c2..ce92885e71323 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1470,6 +1470,12 @@ static bool valid_percpu_irqaction(struct irqaction *old, struct irqaction *new)
 static int
 __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
 {
+	/*
+	 * Choose the default moderation mode.
+	 * Hardcoded to false for now; configurable defaults are added later.
+	 * Set to true here to force-enable GSIM for testing this commit.
+	 */
+	bool use_moderation = false;
 	struct irqaction *old, **old_ptr;
 	unsigned long flags, thread_mask = 0;
 	int ret, nested, shared = 0;
@@ -1789,6 +1795,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
 
 	irq_pm_install_action(desc, new);
 
+	irq_moderation_allow(desc, use_moderation);
+
 	/* Reset broken irq detection when installing new handler */
 	desc->irq_count = 0;
 	desc->irqs_unhandled = 0;
@@ -1897,6 +1905,7 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
 	/* If this was the last handler, shut down the IRQ line: */
 	if (!desc->action) {
 		irq_settings_clr_disable_unlazy(desc);
+		irq_settings_clr_moderatable(desc);
 		/* Only shutdown. Deactivate after synchronize_hardirq() */
 		irq_shutdown(desc);
 	}
@@ -2046,6 +2055,7 @@ static const void *__cleanup_nmi(unsigned int irq, struct irq_desc *desc)
 		desc->action = NULL;
 
 		irq_settings_clr_disable_unlazy(desc);
+		irq_settings_clr_moderatable(desc);
 		irq_shutdown_and_deactivate(desc);
 	}
 
-- 
2.55.0.737.g08866a6d13-goog
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.