[tip: irq/drivers] genirq/chip: Add guard_chained_irq() cleanup helper
From: tip-bot2 for Peng Fan
Date: Sun Nov 08 2026 - 16:54:09 EST
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 6cf0d999a18e799965cf2cf4a0157e257c805946
Gitweb: https://git.kernel.org/tip/6cf0d999a18e799965cf2cf4a0157e257c805946
Author: Peng Fan <peng.fan@xxxxxxx>
AuthorDate: Tue, 13 Oct 2026 10:08:45 +08:00
Committer: Thomas Gleixner <tglx@xxxxxxxxxx>
CommitterDate: Sun, 08 Nov 2026 22:43:43 +01:00
genirq/chip: Add guard_chained_irq() cleanup helper
Chained interrupt handlers bracket their work with a
chained_irq_enter() / chained_irq_exit() pair. Because the exit call
has to be repeated on every return path of the handler, it is easy to
forget one when a new early-return is added, leaving the parent chip
masked or un-EOIed.
Provide a scope-based cleanup helper built on the DEFINE_CLASS()
infrastructure:
guard_chained_irq(chip, desc);
invokes chained_irq_enter() immediately and arranges for the matching
chained_irq_exit() to run automatically when the enclosing function
(or compound statement) scope ends, no matter which return path is
taken. This removes the need to spell out the exit call by hand.
The chained_irq_enter() call and the context struct are built directly
in the DEFINE_CLASS() constructor expression, so no separate helper
function is required.
Signed-off-by: Peng Fan <peng.fan@xxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxx>
Link: https://patch.msgid.link/20261013-guard_chain_irq-v1-1-cdbf490510e5@xxxxxxx
---
include/linux/irqchip/chained_irq.h | 36 ++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+)
diff --git a/include/linux/irqchip/chained_irq.h b/include/linux/irqchip/chained_irq.h
index dd8b3c4..6a1a8e8 100644
--- a/include/linux/irqchip/chained_irq.h
+++ b/include/linux/irqchip/chained_irq.h
@@ -7,6 +7,7 @@
#ifndef __IRQCHIP_CHAINED_IRQ_H
#define __IRQCHIP_CHAINED_IRQ_H
+#include <linux/cleanup.h>
#include <linux/irq.h>
/*
@@ -38,4 +39,39 @@ static inline void chained_irq_exit(struct irq_chip *chip,
chip->irq_unmask(&desc->irq_data);
}
+/*
+ * Scope-based variant of the chained_irq_enter()/chained_irq_exit() pair.
+ *
+ * guard_chained_irq() invokes chained_irq_enter() immediately and arranges
+ * for the matching chained_irq_exit() to run automatically when the
+ * enclosing function (or compound statement) scope ends, no matter which
+ * return path is taken. This removes the need to spell out chained_irq_exit()
+ * on every exit path of a chained handler.
+ *
+ * Usage:
+ *
+ * static void foo_irq_handler(struct irq_desc *desc)
+ * {
+ * struct irq_chip *chip = irq_desc_get_chip(desc);
+ *
+ * guard_chained_irq(chip, desc);
+ * ... handle the interrupt, return freely ...
+ * }
+ */
+
+/* Internal context for guard_chained_irq(); not for driver use. */
+struct __chained_irq_guard_ctx {
+ struct irq_chip *chip;
+ struct irq_desc *desc;
+};
+
+DEFINE_CLASS(chained_irq, struct __chained_irq_guard_ctx,
+ chained_irq_exit(_T.chip, _T.desc),
+ ({ struct __chained_irq_guard_ctx _T = { .chip = chip, .desc = desc };
+ chained_irq_enter(_T.chip, _T.desc); _T; }),
+ struct irq_chip *chip, struct irq_desc *desc)
+
+#define guard_chained_irq(chip, desc) \
+ guard(chained_irq)(chip, desc)
+
#endif /* __IRQCHIP_CHAINED_IRQ_H */