| Subject: genirq: Allow disabling of softirq processing in irq thread context |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Tue, 31 Jan 2012 13:01:27 +0100 |
| |
| 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 | 2 ++ |
| include/linux/irq.h | 5 ++++- |
| kernel/irq/manage.c | 13 ++++++++++++- |
| kernel/irq/settings.h | 12 ++++++++++++ |
| kernel/softirq.c | 7 +++++++ |
| 5 files changed, 37 insertions(+), 2 deletions(-) |
| |
| --- a/include/linux/interrupt.h |
| +++ b/include/linux/interrupt.h |
| @@ -58,6 +58,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_SHARED 0x00000080 |
| @@ -71,6 +72,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) |
| |
| --- a/include/linux/irq.h |
| +++ b/include/linux/irq.h |
| @@ -70,6 +70,7 @@ typedef void (*irq_preflow_handler_t)(st |
| * 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, |
| @@ -94,12 +95,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) |
| |
| --- a/kernel/irq/manage.c |
| +++ b/kernel/irq/manage.c |
| @@ -782,7 +782,15 @@ irq_forced_thread_fn(struct irq_desc *de |
| local_bh_disable(); |
| ret = action->thread_fn(action->irq, action->dev_id); |
| irq_finalize_oneshot(desc, action); |
| - 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; |
| } |
| |
| @@ -1127,6 +1135,9 @@ __setup_irq(unsigned int irq, struct irq |
| 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); |
| |
| --- a/kernel/irq/settings.h |
| +++ b/kernel/irq/settings.h |
| @@ -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->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; |
| --- a/kernel/softirq.c |
| +++ b/kernel/softirq.c |
| @@ -417,6 +417,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) |
| { |