Commit 399b5da29b9f851eb7b96e2882097127f003e87c

Authored by Thomas Gleixner
1 parent 70aedd24d2

genirq: Support nested threaded irq handling

Interrupt chips which are behind a slow bus (i2c, spi ...) and
demultiplex other interrupt sources need to run their interrupt
handler in a thread.

The demultiplexed interrupt handlers need to run in thread context as
well and need to finish before the demux handler thread can reenable
the interrupt line. So the easiest way is to run the sub device
handlers in the context of the demultiplexing handler thread.

To avoid that a separate thread is created for the subdevices the
function set_nested_irq_thread() is provided which sets the
IRQ_NESTED_THREAD flag in the interrupt descriptor.

A driver which calls request_threaded_irq() must not be aware of the
fact that the threaded handler is called in the context of the
demultiplexing handler thread. The setup code checks the
IRQ_NESTED_THREAD flag which was set from the irq chip setup code and
does not setup a separate thread for the interrupt. The primary
function which is provided by the device driver is replaced by an
internal dummy function which warns when it is called.

For the demultiplexing handler a helper function handle_nested_irq()
is provided which calls the demux interrupt thread function in the
context of the caller and does the proper interrupt accounting and
takes the interrupt disabled status of the demultiplexed subdevice
into account.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Trilok Soni <soni.trilok@gmail.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Brian Swetland <swetland@google.com>
Cc: Joonyoung Shim <jy0922.shim@samsung.com>
Cc: m.szyprowski@samsung.com
Cc: t.fujak@samsung.com
Cc: kyungmin.park@samsung.com,
Cc: David Brownell <david-b@pacbell.net>
Cc: Daniel Ribeiro <drwyrm@gmail.com>
Cc: arve@android.com
Cc: Barry Song <21cnbao@gmail.com>

Showing 3 changed files with 101 additions and 3 deletions Side-by-side Diff

... ... @@ -70,6 +70,7 @@
70 70 #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/
71 71 #define IRQ_SUSPENDED 0x04000000 /* IRQ has gone through suspend sequence */
72 72 #define IRQ_ONESHOT 0x08000000 /* IRQ is not unmasked after hardirq */
  73 +#define IRQ_NESTED_THREAD 0x10000000 /* IRQ is nested into another, no own handler thread */
73 74  
74 75 #ifdef CONFIG_IRQ_PER_CPU
75 76 # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU)
... ... @@ -385,6 +386,8 @@
385 386 {
386 387 __set_irq_handler(irq, handle, 1, NULL);
387 388 }
  389 +
  390 +extern void set_irq_nested_thread(unsigned int irq, int nest);
388 391  
389 392 extern void set_irq_noprobe(unsigned int irq);
390 393 extern void set_irq_probe(unsigned int irq);
... ... @@ -222,6 +222,34 @@
222 222 }
223 223 EXPORT_SYMBOL(set_irq_chip_data);
224 224  
  225 +/**
  226 + * set_irq_nested_thread - Set/Reset the IRQ_NESTED_THREAD flag of an irq
  227 + *
  228 + * @irq: Interrupt number
  229 + * @nest: 0 to clear / 1 to set the IRQ_NESTED_THREAD flag
  230 + *
  231 + * The IRQ_NESTED_THREAD flag indicates that on
  232 + * request_threaded_irq() no separate interrupt thread should be
  233 + * created for the irq as the handler are called nested in the
  234 + * context of a demultiplexing interrupt handler thread.
  235 + */
  236 +void set_irq_nested_thread(unsigned int irq, int nest)
  237 +{
  238 + struct irq_desc *desc = irq_to_desc(irq);
  239 + unsigned long flags;
  240 +
  241 + if (!desc)
  242 + return;
  243 +
  244 + spin_lock_irqsave(&desc->lock, flags);
  245 + if (nest)
  246 + desc->status |= IRQ_NESTED_THREAD;
  247 + else
  248 + desc->status &= ~IRQ_NESTED_THREAD;
  249 + spin_unlock_irqrestore(&desc->lock, flags);
  250 +}
  251 +EXPORT_SYMBOL_GPL(set_irq_nested_thread);
  252 +
225 253 /*
226 254 * default enable function
227 255 */
... ... @@ -298,6 +326,45 @@
298 326 desc->chip->ack(irq);
299 327 }
300 328 }
  329 +
  330 +/*
  331 + * handle_nested_irq - Handle a nested irq from a irq thread
  332 + * @irq: the interrupt number
  333 + *
  334 + * Handle interrupts which are nested into a threaded interrupt
  335 + * handler. The handler function is called inside the calling
  336 + * threads context.
  337 + */
  338 +void handle_nested_irq(unsigned int irq)
  339 +{
  340 + struct irq_desc *desc = irq_to_desc(irq);
  341 + struct irqaction *action;
  342 + irqreturn_t action_ret;
  343 +
  344 + might_sleep();
  345 +
  346 + spin_lock_irq(&desc->lock);
  347 +
  348 + kstat_incr_irqs_this_cpu(irq, desc);
  349 +
  350 + action = desc->action;
  351 + if (unlikely(!action || (desc->status & IRQ_DISABLED)))
  352 + goto out_unlock;
  353 +
  354 + desc->status |= IRQ_INPROGRESS;
  355 + spin_unlock_irq(&desc->lock);
  356 +
  357 + action_ret = action->thread_fn(action->irq, action->dev_id);
  358 + if (!noirqdebug)
  359 + note_interrupt(irq, desc, action_ret);
  360 +
  361 + spin_lock_irq(&desc->lock);
  362 + desc->status &= ~IRQ_INPROGRESS;
  363 +
  364 +out_unlock:
  365 + spin_unlock_irq(&desc->lock);
  366 +}
  367 +EXPORT_SYMBOL_GPL(handle_nested_irq);
301 368  
302 369 /**
303 370 * handle_simple_irq - Simple and software-decoded IRQs.
... ... @@ -451,6 +451,16 @@
451 451 return IRQ_WAKE_THREAD;
452 452 }
453 453  
  454 +/*
  455 + * Primary handler for nested threaded interrupts. Should never be
  456 + * called.
  457 + */
  458 +static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
  459 +{
  460 + WARN(1, "Primary handler called for nested irq %d\n", irq);
  461 + return IRQ_NONE;
  462 +}
  463 +
454 464 static int irq_wait_for_interrupt(struct irqaction *action)
455 465 {
456 466 while (!kthread_should_stop()) {
... ... @@ -600,7 +610,7 @@
600 610 struct irqaction *old, **old_ptr;
601 611 const char *old_name = NULL;
602 612 unsigned long flags;
603   - int shared = 0;
  613 + int nested, shared = 0;
604 614 int ret;
605 615  
606 616 if (!desc)
607 617  
... ... @@ -630,9 +640,27 @@
630 640 return -EINVAL;
631 641  
632 642 /*
633   - * Threaded handler ?
  643 + * Check whether the interrupt nests into another interrupt
  644 + * thread.
634 645 */
635   - if (new->thread_fn) {
  646 + nested = desc->status & IRQ_NESTED_THREAD;
  647 + if (nested) {
  648 + if (!new->thread_fn)
  649 + return -EINVAL;
  650 + /*
  651 + * Replace the primary handler which was provided from
  652 + * the driver for non nested interrupt handling by the
  653 + * dummy function which warns when called.
  654 + */
  655 + new->handler = irq_nested_primary_handler;
  656 + }
  657 +
  658 + /*
  659 + * Create a handler thread when a thread function is supplied
  660 + * and the interrupt does not nest into another interrupt
  661 + * thread.
  662 + */
  663 + if (new->thread_fn && !nested) {
636 664 struct task_struct *t;
637 665  
638 666 t = kthread_create(irq_thread, new, "irq/%d-%s", irq,