Merge branches 'timers-for-linus-ntp' and 'irq-core-for-linus' of git://git.kernel...
[cascardo/linux.git] / kernel / irq / manage.c
index 8c68d5b..7305b29 100644 (file)
@@ -8,16 +8,15 @@
  */
 
 #include <linux/irq.h>
+#include <linux/kthread.h>
 #include <linux/module.h>
 #include <linux/random.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 
 #include "internals.h"
 
-#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
-cpumask_var_t irq_default_affinity;
-
 /**
  *     synchronize_irq - wait for pending IRQ handlers (on other CPUs)
  *     @irq: interrupt number to wait for
@@ -53,9 +52,18 @@ void synchronize_irq(unsigned int irq)
 
                /* Oops, that failed? */
        } while (status & IRQ_INPROGRESS);
+
+       /*
+        * We made sure that no hardirq handler is running. Now verify
+        * that no threaded handlers are active.
+        */
+       wait_event(desc->wait_for_threads, !atomic_read(&desc->threads_active));
 }
 EXPORT_SYMBOL(synchronize_irq);
 
+#ifdef CONFIG_SMP
+cpumask_var_t irq_default_affinity;
+
 /**
  *     irq_can_set_affinity - Check if the affinity of a given irq can be set
  *     @irq:           Interrupt to check
@@ -72,6 +80,26 @@ int irq_can_set_affinity(unsigned int irq)
        return 1;
 }
 
+/**
+ *     irq_set_thread_affinity - Notify irq threads to adjust affinity
+ *     @desc:          irq descriptor which has affitnity changed
+ *
+ *     We just set IRQTF_AFFINITY and delegate the affinity setting
+ *     to the interrupt thread itself. We can not call
+ *     set_cpus_allowed_ptr() here as we hold desc->lock and this
+ *     code can be called from hard interrupt context.
+ */
+void irq_set_thread_affinity(struct irq_desc *desc)
+{
+       struct irqaction *action = desc->action;
+
+       while (action) {
+               if (action->thread)
+                       set_bit(IRQTF_AFFINITY, &action->thread_flags);
+               action = action->next;
+       }
+}
+
 /**
  *     irq_set_affinity - Set the irq affinity of a given irq
  *     @irq:           Interrupt to set affinity
@@ -89,16 +117,21 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
        spin_lock_irqsave(&desc->lock, flags);
 
 #ifdef CONFIG_GENERIC_PENDING_IRQ
-       if (desc->status & IRQ_MOVE_PCNTXT || desc->status & IRQ_DISABLED) {
-               cpumask_copy(desc->affinity, cpumask);
-               desc->chip->set_affinity(irq, cpumask);
-       } else {
+       if (desc->status & IRQ_MOVE_PCNTXT) {
+               if (!desc->chip->set_affinity(irq, cpumask)) {
+                       cpumask_copy(desc->affinity, cpumask);
+                       irq_set_thread_affinity(desc);
+               }
+       }
+       else {
                desc->status |= IRQ_MOVE_PENDING;
                cpumask_copy(desc->pending_mask, cpumask);
        }
 #else
-       cpumask_copy(desc->affinity, cpumask);
-       desc->chip->set_affinity(irq, cpumask);
+       if (!desc->chip->set_affinity(irq, cpumask)) {
+               cpumask_copy(desc->affinity, cpumask);
+               irq_set_thread_affinity(desc);
+       }
 #endif
        desc->status |= IRQ_AFFINITY_SET;
        spin_unlock_irqrestore(&desc->lock, flags);
@@ -150,6 +183,8 @@ int irq_select_affinity_usr(unsigned int irq)
 
        spin_lock_irqsave(&desc->lock, flags);
        ret = setup_affinity(irq, desc);
+       if (!ret)
+               irq_set_thread_affinity(desc);
        spin_unlock_irqrestore(&desc->lock, flags);
 
        return ret;
@@ -195,9 +230,11 @@ void disable_irq_nosync(unsigned int irq)
        if (!desc)
                return;
 
+       chip_bus_lock(irq, desc);
        spin_lock_irqsave(&desc->lock, flags);
        __disable_irq(desc, irq, false);
        spin_unlock_irqrestore(&desc->lock, flags);
+       chip_bus_sync_unlock(irq, desc);
 }
 EXPORT_SYMBOL(disable_irq_nosync);
 
@@ -259,7 +296,8 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
  *     matches the last disable, processing of interrupts on this
  *     IRQ line is re-enabled.
  *
- *     This function may be called from IRQ context.
+ *     This function may be called from IRQ context only when
+ *     desc->chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
  */
 void enable_irq(unsigned int irq)
 {
@@ -269,9 +307,11 @@ void enable_irq(unsigned int irq)
        if (!desc)
                return;
 
+       chip_bus_lock(irq, desc);
        spin_lock_irqsave(&desc->lock, flags);
        __enable_irq(desc, irq, false);
        spin_unlock_irqrestore(&desc->lock, flags);
+       chip_bus_sync_unlock(irq, desc);
 }
 EXPORT_SYMBOL(enable_irq);
 
@@ -401,6 +441,165 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
        return ret;
 }
 
+/*
+ * Default primary interrupt handler for threaded interrupts. Is
+ * assigned as primary handler when request_threaded_irq is called
+ * with handler == NULL. Useful for oneshot interrupts.
+ */
+static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)
+{
+       return IRQ_WAKE_THREAD;
+}
+
+/*
+ * Primary handler for nested threaded interrupts. Should never be
+ * called.
+ */
+static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
+{
+       WARN(1, "Primary handler called for nested irq %d\n", irq);
+       return IRQ_NONE;
+}
+
+static int irq_wait_for_interrupt(struct irqaction *action)
+{
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (test_and_clear_bit(IRQTF_RUNTHREAD,
+                                      &action->thread_flags)) {
+                       __set_current_state(TASK_RUNNING);
+                       return 0;
+               }
+               schedule();
+       }
+       return -1;
+}
+
+/*
+ * Oneshot interrupts keep the irq line masked until the threaded
+ * handler finished. unmask if the interrupt has not been disabled and
+ * is marked MASKED.
+ */
+static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)
+{
+       chip_bus_lock(irq, desc);
+       spin_lock_irq(&desc->lock);
+       if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {
+               desc->status &= ~IRQ_MASKED;
+               desc->chip->unmask(irq);
+       }
+       spin_unlock_irq(&desc->lock);
+       chip_bus_sync_unlock(irq, desc);
+}
+
+#ifdef CONFIG_SMP
+/*
+ * Check whether we need to change the affinity of the interrupt thread.
+ */
+static void
+irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action)
+{
+       cpumask_var_t mask;
+
+       if (!test_and_clear_bit(IRQTF_AFFINITY, &action->thread_flags))
+               return;
+
+       /*
+        * In case we are out of memory we set IRQTF_AFFINITY again and
+        * try again next time
+        */
+       if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
+               set_bit(IRQTF_AFFINITY, &action->thread_flags);
+               return;
+       }
+
+       spin_lock_irq(&desc->lock);
+       cpumask_copy(mask, desc->affinity);
+       spin_unlock_irq(&desc->lock);
+
+       set_cpus_allowed_ptr(current, mask);
+       free_cpumask_var(mask);
+}
+#else
+static inline void
+irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action) { }
+#endif
+
+/*
+ * Interrupt handler thread
+ */
+static int irq_thread(void *data)
+{
+       struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, };
+       struct irqaction *action = data;
+       struct irq_desc *desc = irq_to_desc(action->irq);
+       int wake, oneshot = desc->status & IRQ_ONESHOT;
+
+       sched_setscheduler(current, SCHED_FIFO, &param);
+       current->irqaction = action;
+
+       while (!irq_wait_for_interrupt(action)) {
+
+               irq_thread_check_affinity(desc, action);
+
+               atomic_inc(&desc->threads_active);
+
+               spin_lock_irq(&desc->lock);
+               if (unlikely(desc->status & IRQ_DISABLED)) {
+                       /*
+                        * CHECKME: We might need a dedicated
+                        * IRQ_THREAD_PENDING flag here, which
+                        * retriggers the thread in check_irq_resend()
+                        * but AFAICT IRQ_PENDING should be fine as it
+                        * retriggers the interrupt itself --- tglx
+                        */
+                       desc->status |= IRQ_PENDING;
+                       spin_unlock_irq(&desc->lock);
+               } else {
+                       spin_unlock_irq(&desc->lock);
+
+                       action->thread_fn(action->irq, action->dev_id);
+
+                       if (oneshot)
+                               irq_finalize_oneshot(action->irq, desc);
+               }
+
+               wake = atomic_dec_and_test(&desc->threads_active);
+
+               if (wake && waitqueue_active(&desc->wait_for_threads))
+                       wake_up(&desc->wait_for_threads);
+       }
+
+       /*
+        * Clear irqaction. Otherwise exit_irq_thread() would make
+        * fuzz about an active irq thread going into nirvana.
+        */
+       current->irqaction = NULL;
+       return 0;
+}
+
+/*
+ * Called from do_exit()
+ */
+void exit_irq_thread(void)
+{
+       struct task_struct *tsk = current;
+
+       if (!tsk->irqaction)
+               return;
+
+       printk(KERN_ERR
+              "exiting task \"%s\" (%d) is an active IRQ thread (irq %d)\n",
+              tsk->comm ? tsk->comm : "", tsk->pid, tsk->irqaction->irq);
+
+       /*
+        * Set the THREAD DIED flag to prevent further wakeups of the
+        * soon to be gone threaded handler.
+        */
+       set_bit(IRQTF_DIED, &tsk->irqaction->flags);
+}
+
 /*
  * Internal function to register an irqaction - typically used to
  * allocate special interrupts that are part of the architecture.
@@ -411,7 +610,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
        struct irqaction *old, **old_ptr;
        const char *old_name = NULL;
        unsigned long flags;
-       int shared = 0;
+       int nested, shared = 0;
        int ret;
 
        if (!desc)
@@ -436,6 +635,47 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                rand_initialize_irq(irq);
        }
 
+       /* Oneshot interrupts are not allowed with shared */
+       if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED))
+               return -EINVAL;
+
+       /*
+        * Check whether the interrupt nests into another interrupt
+        * thread.
+        */
+       nested = desc->status & IRQ_NESTED_THREAD;
+       if (nested) {
+               if (!new->thread_fn)
+                       return -EINVAL;
+               /*
+                * Replace the primary handler which was provided from
+                * the driver for non nested interrupt handling by the
+                * dummy function which warns when called.
+                */
+               new->handler = irq_nested_primary_handler;
+       }
+
+       /*
+        * Create a handler thread when a thread function is supplied
+        * and the interrupt does not nest into another interrupt
+        * thread.
+        */
+       if (new->thread_fn && !nested) {
+               struct task_struct *t;
+
+               t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
+                                  new->name);
+               if (IS_ERR(t))
+                       return PTR_ERR(t);
+               /*
+                * We keep the reference to the task struct even if
+                * the thread dies to avoid that the interrupt code
+                * references an already freed task_struct.
+                */
+               get_task_struct(t);
+               new->thread = t;
+       }
+
        /*
         * The following block of code has to be executed atomically
         */
@@ -473,15 +713,15 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
        if (!shared) {
                irq_chip_set_defaults(desc->chip);
 
+               init_waitqueue_head(&desc->wait_for_threads);
+
                /* Setup the type (level, edge polarity) if configured: */
                if (new->flags & IRQF_TRIGGER_MASK) {
                        ret = __irq_set_trigger(desc, irq,
                                        new->flags & IRQF_TRIGGER_MASK);
 
-                       if (ret) {
-                               spin_unlock_irqrestore(&desc->lock, flags);
-                               return ret;
-                       }
+                       if (ret)
+                               goto out_thread;
                } else
                        compat_irq_chip_set_default_handler(desc);
 #if defined(CONFIG_IRQ_PER_CPU)
@@ -489,9 +729,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                        desc->status |= IRQ_PER_CPU;
 #endif
 
-               desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
+               desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |
                                  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
 
+               if (new->flags & IRQF_ONESHOT)
+                       desc->status |= IRQ_ONESHOT;
+
                if (!(desc->status & IRQ_NOAUTOEN)) {
                        desc->depth = 0;
                        desc->status &= ~IRQ_DISABLED;
@@ -516,6 +759,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                                (int)(new->flags & IRQF_TRIGGER_MASK));
        }
 
+       new->irq = irq;
        *old_ptr = new;
 
        /* Reset broken irq detection when installing new handler */
@@ -533,7 +777,13 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
 
        spin_unlock_irqrestore(&desc->lock, flags);
 
-       new->irq = irq;
+       /*
+        * Strictly no need to wake it up, but hung_task complains
+        * when no hard interrupt wakes the thread up.
+        */
+       if (new->thread)
+               wake_up_process(new->thread);
+
        register_irq_proc(irq, desc);
        new->dir = NULL;
        register_handler_proc(irq, new);
@@ -549,8 +799,19 @@ mismatch:
                dump_stack();
        }
 #endif
+       ret = -EBUSY;
+
+out_thread:
        spin_unlock_irqrestore(&desc->lock, flags);
-       return -EBUSY;
+       if (new->thread) {
+               struct task_struct *t = new->thread;
+
+               new->thread = NULL;
+               if (likely(!test_bit(IRQTF_DIED, &new->thread_flags)))
+                       kthread_stop(t);
+               put_task_struct(t);
+       }
+       return ret;
 }
 
 /**
@@ -622,6 +883,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
                else
                        desc->chip->disable(irq);
        }
+
        spin_unlock_irqrestore(&desc->lock, flags);
 
        unregister_handler_proc(irq, action);
@@ -644,6 +906,13 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
                local_irq_restore(flags);
        }
 #endif
+
+       if (action->thread) {
+               if (!test_bit(IRQTF_DIED, &action->thread_flags))
+                       kthread_stop(action->thread);
+               put_task_struct(action->thread);
+       }
+
        return action;
 }
 
@@ -676,14 +945,26 @@ EXPORT_SYMBOL_GPL(remove_irq);
  */
 void free_irq(unsigned int irq, void *dev_id)
 {
+       struct irq_desc *desc = irq_to_desc(irq);
+
+       if (!desc)
+               return;
+
+       chip_bus_lock(irq, desc);
        kfree(__free_irq(irq, dev_id));
+       chip_bus_sync_unlock(irq, desc);
 }
 EXPORT_SYMBOL(free_irq);
 
 /**
- *     request_irq - allocate an interrupt line
+ *     request_threaded_irq - allocate an interrupt line
  *     @irq: Interrupt line to allocate
- *     @handler: Function to be called when the IRQ occurs
+ *     @handler: Function to be called when the IRQ occurs.
+ *               Primary handler for threaded interrupts
+ *               If NULL and thread_fn != NULL the default
+ *               primary handler is installed
+ *     @thread_fn: Function called from the irq handler thread
+ *                 If NULL, no irq thread is created
  *     @irqflags: Interrupt type flags
  *     @devname: An ascii name for the claiming device
  *     @dev_id: A cookie passed back to the handler function
@@ -695,6 +976,15 @@ EXPORT_SYMBOL(free_irq);
  *     raises, you must take care both to initialise your hardware
  *     and to set up the interrupt handler in the right order.
  *
+ *     If you want to set up a threaded irq handler for your device
+ *     then you need to supply @handler and @thread_fn. @handler ist
+ *     still called in hard interrupt context and has to check
+ *     whether the interrupt originates from the device. If yes it
+ *     needs to disable the interrupt on the device and return
+ *     IRQ_WAKE_THREAD which will wake up the handler thread and run
+ *     @thread_fn. This split handler design is necessary to support
+ *     shared interrupts.
+ *
  *     Dev_id must be globally unique. Normally the address of the
  *     device data structure is used as the cookie. Since the handler
  *     receives this value it makes sense to use it.
@@ -710,8 +1000,9 @@ EXPORT_SYMBOL(free_irq);
  *     IRQF_TRIGGER_*          Specify active edge(s) or level
  *
  */
-int request_irq(unsigned int irq, irq_handler_t handler,
-               unsigned long irqflags, const char *devname, void *dev_id)
+int request_threaded_irq(unsigned int irq, irq_handler_t handler,
+                        irq_handler_t thread_fn, unsigned long irqflags,
+                        const char *devname, void *dev_id)
 {
        struct irqaction *action;
        struct irq_desc *desc;
@@ -751,19 +1042,27 @@ int request_irq(unsigned int irq, irq_handler_t handler,
 
        if (desc->status & IRQ_NOREQUEST)
                return -EINVAL;
-       if (!handler)
-               return -EINVAL;
+
+       if (!handler) {
+               if (!thread_fn)
+                       return -EINVAL;
+               handler = irq_default_primary_handler;
+       }
 
        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
        if (!action)
                return -ENOMEM;
 
        action->handler = handler;
+       action->thread_fn = thread_fn;
        action->flags = irqflags;
        action->name = devname;
        action->dev_id = dev_id;
 
+       chip_bus_lock(irq, desc);
        retval = __setup_irq(irq, desc, action);
+       chip_bus_sync_unlock(irq, desc);
+
        if (retval)
                kfree(action);
 
@@ -788,4 +1087,4 @@ int request_irq(unsigned int irq, irq_handler_t handler,
 #endif
        return retval;
 }
-EXPORT_SYMBOL(request_irq);
+EXPORT_SYMBOL(request_threaded_irq);