genirq: Allow disabling of softirq processing in irq thread context
authorThomas Gleixner <tglx@linutronix.de>
Tue, 31 Jan 2012 12:01:27 +0000 (13:01 +0100)
committerClark Williams <williams@redhat.com>
Wed, 15 Feb 2012 16:32:53 +0000 (10:32 -0600)
The processing of softirqs in irq thread context is a performance gain
for the non-rt workloads of a system, but it's counterproductive for
interrupts which are explicitely related to the realtime
workload. Allow such interrupts to prevent softirq processing in their
thread context.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable-rt@vger.kernel.org
include/linux/interrupt.h
include/linux/irq.h
kernel/irq/manage.c
kernel/irq/settings.h
kernel/softirq.c

index bb4b441b1860290376849b75fb4169c3134c4a15..f70a65b2a05146de943a390509877f68f7e9028c 100644 (file)
@@ -61,6 +61,7 @@
  * IRQF_NO_THREAD - Interrupt cannot be threaded
  * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
  *                resume time.
+ * IRQF_NO_SOFTIRQ_CALL - Do not process softirqs in the irq thread context (RT)
  */
 #define IRQF_DISABLED          0x00000020
 #define IRQF_SAMPLE_RANDOM     0x00000040
@@ -75,6 +76,7 @@
 #define IRQF_FORCE_RESUME      0x00008000
 #define IRQF_NO_THREAD         0x00010000
 #define IRQF_EARLY_RESUME      0x00020000
+#define IRQF_NO_SOFTIRQ_CALL   0x00040000
 
 #define IRQF_TIMER             (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)
 
index bff29c58da23013e5e1e19f0cdeaf2ad45d602fc..3838b535554427526c82e0acbecda7d83b204c44 100644 (file)
@@ -67,6 +67,7 @@ typedef       void (*irq_preflow_handler_t)(struct irq_data *data);
  * IRQ_MOVE_PCNTXT             - Interrupt can be migrated from process context
  * IRQ_NESTED_TRHEAD           - Interrupt nests into another thread
  * IRQ_PER_CPU_DEVID           - Dev_id is a per-cpu variable
+ * IRQ_NO_SOFTIRQ_CALL         - No softirq processing in the irq thread context (RT)
  */
 enum {
        IRQ_TYPE_NONE           = 0x00000000,
@@ -90,12 +91,14 @@ enum {
        IRQ_NESTED_THREAD       = (1 << 15),
        IRQ_NOTHREAD            = (1 << 16),
        IRQ_PER_CPU_DEVID       = (1 << 17),
+       IRQ_NO_SOFTIRQ_CALL     = (1 << 18),
 };
 
 #define IRQF_MODIFY_MASK       \
        (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
         IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
-        IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID)
+        IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \
+        IRQ_NO_SOFTIRQ_CALL)
 
 #define IRQ_NO_BALANCING_MASK  (IRQ_PER_CPU | IRQ_NO_BALANCING)
 
index 09ba67d9ceb7b05c35638ffded15edc8df8a7450..fd301aa5665d51ba20f6a7bcb12eba91cb1d74d3 100644 (file)
@@ -742,7 +742,15 @@ irq_forced_thread_fn(struct irq_desc *desc, struct irqaction *action)
        local_bh_disable();
        ret = action->thread_fn(action->irq, action->dev_id);
        irq_finalize_oneshot(desc, action, false);
-       local_bh_enable();
+       /*
+        * Interrupts which have real time requirements can be set up
+        * to avoid softirq processing in the thread handler. This is
+        * safe as these interrupts do not raise soft interrupts.
+        */
+       if (irq_settings_no_softirq_call(desc))
+               _local_bh_enable();
+       else
+               local_bh_enable();
        return ret;
 }
 
@@ -1040,6 +1048,9 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                        irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
                }
 
+               if (new->flags & IRQF_NO_SOFTIRQ_CALL)
+                       irq_settings_set_no_softirq_call(desc);
+
                /* Set default affinity mask once everything is setup */
                setup_affinity(irq, desc, mask);
 
index 1162f1030f18f9326c23522417a3de87feb18ef4..0d2c381c2bdb349242e4416984f2baab56512c63 100644 (file)
@@ -14,6 +14,7 @@ enum {
        _IRQ_NO_BALANCING       = IRQ_NO_BALANCING,
        _IRQ_NESTED_THREAD      = IRQ_NESTED_THREAD,
        _IRQ_PER_CPU_DEVID      = IRQ_PER_CPU_DEVID,
+       _IRQ_NO_SOFTIRQ_CALL    = IRQ_NO_SOFTIRQ_CALL,
        _IRQF_MODIFY_MASK       = IRQF_MODIFY_MASK,
 };
 
@@ -26,6 +27,7 @@ enum {
 #define IRQ_NOAUTOEN           GOT_YOU_MORON
 #define IRQ_NESTED_THREAD      GOT_YOU_MORON
 #define IRQ_PER_CPU_DEVID      GOT_YOU_MORON
+#define IRQ_NO_SOFTIRQ_CALL    GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK       GOT_YOU_MORON
 
@@ -36,6 +38,16 @@ irq_settings_clr_and_set(struct irq_desc *desc, u32 clr, u32 set)
        desc->status_use_accessors |= (set & _IRQF_MODIFY_MASK);
 }
 
+static inline bool irq_settings_no_softirq_call(struct irq_desc *desc)
+{
+       return desc->status_use_accessors & _IRQ_NO_SOFTIRQ_CALL;
+}
+
+static inline void irq_settings_set_no_softirq_call(struct irq_desc *desc)
+{
+       desc->status_use_accessors |= _IRQ_NO_SOFTIRQ_CALL;
+}
+
 static inline bool irq_settings_is_per_cpu(struct irq_desc *desc)
 {
        return desc->status_use_accessors & _IRQ_PER_CPU;
index 92b4ca30eda27dbb5131b6210b61570a726d1bf1..bef08f3cd6d6b07600bca0eba57a74527be3c7cd 100644 (file)
@@ -425,6 +425,13 @@ void local_bh_enable_ip(unsigned long ip)
 }
 EXPORT_SYMBOL(local_bh_enable_ip);
 
+void _local_bh_enable(void)
+{
+       current->softirq_nestcnt--;
+       migrate_enable();
+}
+EXPORT_SYMBOL(_local_bh_enable);
+
 /* For tracing */
 int notrace __in_softirq(void)
 {