Name: Don't Check for Dead Tasks on Every Schedule Status: Booted on 2.6.7-rc2-bk3 Signed-off-by: Rusty Russell Currently the scheduler checks if we just unscheduled a dead task, and if so, it does the (probably final) put_task_struct. We can remove this check by using a "one-behind" cache in exit.c: upon exit, we free put_task_struct the task from the last exit. In addition, we prime the cache with the init_task, so we never have to check if it's empty. diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .18428-linux-2.6.7-rc2-bk3/init/main.c .18428-linux-2.6.7-rc2-bk3.updated/init/main.c --- .18428-linux-2.6.7-rc2-bk3/init/main.c 2004-05-31 09:57:40.000000000 +1000 +++ .18428-linux-2.6.7-rc2-bk3.updated/init/main.c 2004-06-03 10:41:18.000000000 +1000 @@ -91,6 +91,7 @@ extern void free_initmem(void); extern void populate_rootfs(void); extern void driver_init(void); extern void prepare_namespace(void); +extern void exit_init(void); #ifdef CONFIG_TC extern void tc_init(void); @@ -414,6 +415,9 @@ asmlinkage void __init start_kernel(void */ smp_prepare_boot_cpu(); + /* Set up exit task cache. */ + exit_init(); + /* * Set up the scheduler prior starting any interrupts (such as the * timer interrupt). Full topology setup happens at smp_init() diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .18428-linux-2.6.7-rc2-bk3/kernel/exit.c .18428-linux-2.6.7-rc2-bk3.updated/kernel/exit.c --- .18428-linux-2.6.7-rc2-bk3/kernel/exit.c 2004-05-31 09:57:40.000000000 +1000 +++ .18428-linux-2.6.7-rc2-bk3.updated/kernel/exit.c 2004-06-03 10:41:18.000000000 +1000 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ extern void sem_exit (void); extern struct task_struct *child_reaper; int getrusage(struct task_struct *, int, struct rusage __user *); +static DEFINE_PER_CPU(struct task_struct *, last_exit); static void __unhash_process(struct task_struct *p) { @@ -751,6 +753,12 @@ static void exit_notify(struct task_stru _raw_write_unlock(&tasklist_lock); local_irq_enable(); + /* We free up the previous one at this point, to ensure we + * never get freed while we're exiting. Preemption stays + * disabled until after we've scheduled away. */ + put_task_struct(__get_cpu_var(last_exit)); + __get_cpu_var(last_exit) = tsk; + /* If the process is dead, release it - nobody will wait for it */ if (state == TASK_DEAD) release_task(tsk); @@ -1163,6 +1171,17 @@ end_wait4: return retval; } +void __init exit_init(void) +{ + unsigned int i; + + /* All this to save a branch in the exit path... */ + for (i = 0; i < NR_CPUS; i++) { + get_task_struct(current); + per_cpu(last_exit, i) = current; + } +} + #ifdef __ARCH_WANT_SYS_WAITPID /* diff -urpN --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal .18428-linux-2.6.7-rc2-bk3/kernel/sched.c .18428-linux-2.6.7-rc2-bk3.updated/kernel/sched.c --- .18428-linux-2.6.7-rc2-bk3/kernel/sched.c 2004-06-03 08:14:09.000000000 +1000 +++ .18428-linux-2.6.7-rc2-bk3.updated/kernel/sched.c 2004-06-03 10:41:47.000000000 +1000 @@ -1003,27 +1003,12 @@ static void finish_task_switch(task_t *p { runqueue_t *rq = this_rq(); struct mm_struct *mm = rq->prev_mm; - unsigned long prev_task_flags; rq->prev_mm = NULL; - /* - * A task struct has one reference for the use as "current". - * If a task dies, then it sets TASK_ZOMBIE in tsk->state and calls - * schedule one last time. The schedule call will never return, - * and the scheduled task must drop that reference. - * The test for TASK_ZOMBIE must occur while the runqueue locks are - * still held, otherwise prev could be scheduled on another cpu, die - * there before we look at prev->state, and then the reference would - * be dropped twice. - * Manfred Spraul - */ - prev_task_flags = prev->flags; finish_arch_switch(rq, prev); if (mm) mmdrop(mm); - if (unlikely(prev_task_flags & PF_DEAD)) - put_task_struct(prev); } /**