[patch/rfc 1/2] GENIRQ: add handle_threaded_irq() flow handler

David Brownell
2009-03-18T03:07:19Z

From: David Brownell <dbrownell@users.sourceforge.net>

Define a new flow handler, handle_threaded_irq(), for IRQ threads
to use when chaining IRQs.

Unlike existing flow handlers, handle_simple_irq() and siblings,
this one is used only from sleep-capable contexts. It always calls
irqaction handlers from that same (shared) sleep-capable context.

This is independent of Thomas' irq threading patchset, and can be
viewed as a complement to it. This adds support for IRQs whose
handlers must *ONLY* ever run in thread contexts ... instead of
offloading code from hardirq context into a thread. Another way
this differs is that it doesn't create more kernel threads; it
only leverages an existing thread.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
include/linux/irq.h | 7 ++++-
kernel/irq/chip.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+), 2 deletions(-)

--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -283,8 +283,8 @@ static inline int irq_balancing_disabled
extern int handle_IRQ_event(unsigned int irq, struct irqaction *action);

/*
- * Built-in IRQ handlers for various IRQ types,
- * callable via desc->chip->handle_irq()
+ * IRQ flow handlers for various IRQ types, callable via
+ * generic_handle_irq*() or desc->handle_irq()
*/
extern void handle_level_irq(unsigned int irq, struct irq_desc *desc);
extern void handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc);
@@ -293,6 +293,9 @@ extern void handle_simple_irq(unsigned i
extern void handle_percpu_irq(unsigned int irq, struct irq_desc *desc);
extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc);

+/* Flow handler that must only be called from sleeping context */
+extern void handle_threaded_irq(unsigned int irq, struct irq_desc *desc);
+
/*
* Monolithic do_IRQ implementation.
*/
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -295,6 +295,67 @@ static inline void mask_ack_irq(struct i
}

/**
+ * handle_threaded_irq - flow handler reusing current irq thread
+ * @irq: the interrupt number
+ * @desc: the interrupt description structure for this irq
+ * Context: irq thread, with IRQs enabled
+ *
+ * IRQ threads which demultiplex IRQs may use this flow handler
+ * to chain those demultiplexed IRQs to subsidiary handlers, when
+ * all that IRQ dispatch logic must run in sleeping contexts.
+ *
+ * Examples include some multifunction I2C and SPI based devices
+ * (where access to registers, including ones involved in IRQ
+ * dispatching, requires sleeping) that have multiple independent
+ * maskable interupts.
+ *
+ * The irq thread using this flow handler must handle any ack,
+ * clear, mask or unmask issues needed.
+ */
+void
+handle_threaded_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct irqaction *action;
+ irqreturn_t action_ret;
+
+ spin_lock_irq(&desc->lock);
+
+ if (unlikely(desc->status & IRQ_INPROGRESS))
+ goto out_unlock;
+ desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
+ kstat_incr_irqs_this_cpu(irq, desc);
+
+ action = desc->action;
+ if (unlikely(!action || (desc->status & IRQ_DISABLED)))
+ goto out_unlock;
+
+ desc->status |= IRQ_INPROGRESS;
+ spin_unlock_irq(&desc->lock);
+
+ /* simplified handle_IRQ_event(): no random sampling;
+ * IRQs are always enabled so action->handler may sleep;
+ * no hooks for handing off to yet another irq thread.
+ */
+ action_ret = IRQ_NONE;
+ do {
+ /* REVISIT can we get some explicit knowledge that this
+ * handler expects to run in thread context? Maybe an
+ * IRQF_THREADED check, or a new handler type ...
+ */
+ action_ret |= action->handler(irq, action->dev_id);
+ action = action->next;
+ } while (action);
+
+ if (!noirqdebug)
+ note_interrupt(irq, desc, action_ret);
+
+ spin_lock_irq(&desc->lock);
+ desc->status &= ~IRQ_INPROGRESS;
+out_unlock:
+ spin_unlock_irq(&desc->lock);
+}
+
+/**
* handle_simple_irq - Simple and software-decoded IRQs.
* @irq: the interrupt number
* @desc: the interrupt description structure for this irq