[PATCH] fix arbitrarily long preemption lockout times [was: re: preempt-timing-2.6.8-rc1]

Joe Korty
2004-07-13T17:48:36Z

On Tue, Jul 13, 2004 at 07:40:28AM -0700, William Lee Irwin III wrote:
> On Tue, Jul 13, 2004 at 10:36:00AM -0400, Joe Korty wrote:
>> You preemption-block hold times will improve *enormously* if you move all
>> softirq processing down to the daemon (and possibly raise the daemon to
>> one of the higher SCHED_RR priorities, to compensate for softirq processing
>> no longer happening at interrupt level).
>
> Plausible. Got a patch?

Here it is. Against 2.6.7. Testing: compiles, boots, shutdown ok, ran
some disk and networking traffic under it.

IIRC, my first attempt had problems booting some configurations, my guess
is that some drivers try to raise softIRQs before the daemons came up.
So this version uses the global 'system_running' to switch at runtime
between the interrupt/daemon and daemon-only modes of operation.

This also has the advantage of switching back to the safer, interrupt/daemon
mode during system shutdown and while crash dumps are in progress (for
those who have applied that patch).

IMO, this patch would be better if it introduced a seperate, hi-priority
softirq daemon, as La Monte H.P. Yarroll's patch does.

Regards,
Joe

Signed-off-by: Joe Korty <joe.korty@ccur.com>

diff -ura base/arch/i386/kernel/irq.c new/arch/i386/kernel/irq.c
--- base/arch/i386/kernel/irq.c 2004-06-16 01:18:57.000000000 -0400
+++ new/arch/i386/kernel/irq.c 2004-07-13 12:52:33.000000000 -0400
@@ -1154,7 +1154,7 @@

extern asmlinkage void __do_softirq(void);

-asmlinkage void do_softirq(void)
+asmlinkage void do_softirq_arch(void)
{
unsigned long flags;
struct thread_info *curctx;
@@ -1189,5 +1189,5 @@
local_irq_restore(flags);
}

-EXPORT_SYMBOL(do_softirq);
+EXPORT_SYMBOL(do_softirq_arch);
#endif
diff -ura base/arch/ppc64/kernel/irq.c new/arch/ppc64/kernel/irq.c
--- base/arch/ppc64/kernel/irq.c 2004-06-16 01:19:22.000000000 -0400
+++ new/arch/ppc64/kernel/irq.c 2004-07-13 12:15:31.000000000 -0400
@@ -1015,7 +1015,7 @@
}
}

-void do_softirq(void)
+void do_softirq_arch(void)
{
unsigned long flags;
struct thread_info *curtp, *irqtp;
@@ -1035,7 +1035,7 @@

local_irq_restore(flags);
}
-EXPORT_SYMBOL(do_softirq);
+EXPORT_SYMBOL(do_softirq_arch);

#endif /* CONFIG_IRQSTACKS */

diff -ura base/include/linux/interrupt.h new/include/linux/interrupt.h
--- base/include/linux/interrupt.h 2004-06-16 01:19:29.000000000 -0400
+++ new/include/linux/interrupt.h 2004-07-13 12:14:14.000000000 -0400
@@ -93,6 +93,7 @@
};

asmlinkage void do_softirq(void);
+asmlinkage void do_softirq_arch(void);
extern void open_softirq(int nr, void (*action)(struct softirq_action*), void *data);
extern void softirq_init(void);
#define __raise_softirq_irqoff(nr) do { local_softirq_pending() |= 1UL << (nr); } while (0)
diff -ura base/init/Kconfig new/init/Kconfig
--- base/init/Kconfig 2004-06-16 01:19:52.000000000 -0400
+++ new/init/Kconfig 2004-07-13 12:56:02.000000000 -0400
@@ -218,6 +218,43 @@
This option enables access to kernel configuration file and build
information through /proc/config.gz.

+config SOFTIRQ_PREEMPT_BLOCK
+ bool "SoftIRQs to be affected by preemption blocks"
+ default y
+ help
+ If N then SoftIRQ interrupt handlers are to be blocked by
+ exactly the same conditions as hard IRQ handlers. If Y then
+ SoftIRQ handlers are also to be blocked by preemption blocks.
+
+ The reasons why Y is useful are too long to elaborate here.
+ Suffice it to say that SoftIRQ handlers unlike hardIRQ handlers
+ have no upper bounds on their execution time and allowing
+ them to interrupt spinlocked critical regions protected only
+ by a preemption block means that no such region can have an
+ upper bound on the lock hold time.
+
+ Realtime systems should say Y here, all others may say N or
+ Y. If unsure, say N.
+
+config SOFTIRQ_PRI
+ int "SoftIRQ daemon priority"
+ default 0
+ help
+ Select the scheduling policy and priority for the per-cpu
+ SoftIRQ daemons.
+
+ If >0 then the daemons will have a policy of SCHED_FIFO and
+ their priorities will each be set to the given value.
+
+ If ==0 then the daemons will run with a SCHED_OTHER policy
+ unless SOFTIRQ_PREEMPT_BLOCK is set to Y. In that case the
+ daemons will run under SCHED_FIFO with a priority one less
+ than the system maximum.
+
+ Most users should say 0 unless there is a specific priority
+ the softIRQ daemons must run at. If unsure, say 0.
+
+

menuconfig EMBEDDED
bool "Configure standard kernel features (for small systems)"
diff -ura base/kernel/softirq.c new/kernel/softirq.c
--- base/kernel/softirq.c 2004-06-16 01:19:02.000000000 -0400
+++ new/kernel/softirq.c 2004-07-13 13:05:13.000000000 -0400
@@ -15,7 +15,9 @@
#include <linux/percpu.h>
#include <linux/cpu.h>
#include <linux/kthread.h>
+#include <linux/syscalls.h>

+#include <asm/uaccess.h>
#include <asm/irq.h>
/*
- No shared variables, all the data are CPU local.
@@ -108,7 +110,7 @@

#ifndef __ARCH_HAS_DO_SOFTIRQ

-asmlinkage void do_softirq(void)
+asmlinkage void do_softirq_arch(void)
{
__u32 pending;
unsigned long flags;
@@ -126,10 +128,34 @@
local_irq_restore(flags);
}

-EXPORT_SYMBOL(do_softirq);
+EXPORT_SYMBOL(do_softirq_arch);
+
+#endif
+
+#ifdef CONFIG_SOFTIRQ_PREEMPT_BLOCK
+
+asmlinkage void do_softirq(void)
+{
+ int cpu;
+
+ cpu = get_cpu();
+ if (system_state == SYSTEM_RUNNING && per_cpu(ksoftirqd, cpu)) {
+ wakeup_softirqd();
+ } else
+ do_softirq_arch();
+ put_cpu();
+}
+
+#else /* !CONFIG_SOFTIRQ_PREEMPT_BLOCK */

+asmlinkage void do_softirq()
+{
+ do_softirq_arch();
+}
#endif

+EXPORT_SYMBOL(do_softirq);
+
void local_bh_enable(void)
{
__local_bh_enable();
@@ -325,6 +351,20 @@
set_user_nice(current, 19);
current->flags |= PF_NOFREEZE;

+#if CONFIG_SOFTIRQ_PRI > 0
+ {
+ struct sched_param priconfig = { .sched_priority = CONFIG_SOFTIRQ_PRI };
+ set_fs(KERNEL_DS);
+ sys_sched_setscheduler(0, SCHED_FIFO, &priconfig);
+ }
+#elif defined(CONFIG_SOFTIRQ_PREEMPT_BLOCK)
+ {
+ struct sched_param pridefault = { .sched_priority = MAX_RT_PRIO - 2 };
+ set_fs(KERNEL_DS);
+ sys_sched_setscheduler(0, SCHED_FIFO, &pridefault);
+ }
+#endif
+
set_current_state(TASK_INTERRUPTIBLE);

while (!kthread_should_stop()) {
@@ -340,7 +380,7 @@
preempt_disable();
if (cpu_is_offline((long)__bind_cpu))
goto wait_to_die;
- do_softirq();
+ do_softirq_arch();
preempt_enable();
cond_resched();
}