Task switch timing tests?

Linus Torvalds (Linus.Torvalds@cs.Helsinki.FI)
Tue, 18 Jul 1995 18:26:07 +0300


Hi,
I just made 1.3.11 available on the normal ftp-sites (ftp.funet.fi and
ftp.cs.helsinki.fi). I also have an experimental patch to go on top of
that, and I'd like to know if it (a) works and (b) speeds up
task-switching. Does anybody want to test this out (the byte pipe
benchmarks should show the effects if any, and there are other purely
context-switch sensitive benchmarks as well)?

(I am actually using this patch on my own system, so it _should_ work ok
for others too. I just don't have anything good to test context
switches with).

A plain 1.3.11 kernel will only make a rather small difference (the
scheduling is cleaned up, but not much optimized), so the real
difference should show up if you apply the accompanying patch on top of
the 1.3.11 kernel (you get a kernel that says "1.3.12", but it isn't,
really).

Linus
----------
diff -u --recursive --new-file v1.3.11/linux/Makefile linux/Makefile
--- v1.3.11/linux/Makefile Tue Jul 18 16:28:55 1995
+++ linux/Makefile Tue Jul 18 16:29:38 1995
@@ -1,6 +1,6 @@
VERSION = 1
PATCHLEVEL = 3
-SUBLEVEL = 11
+SUBLEVEL = 12

ARCH = i386

Binary files v1.3.11/linux/drivers/scsi/aic7770 and linux/drivers/scsi/aic7770 differ
diff -u --recursive --new-file v1.3.11/linux/kernel/fork.c linux/kernel/fork.c
--- v1.3.11/linux/kernel/fork.c Tue Jul 18 16:28:59 1995
+++ linux/kernel/fork.c Tue Jul 18 17:15:38 1995
@@ -192,6 +192,8 @@
p->state = TASK_UNINTERRUPTIBLE;
p->flags &= ~(PF_PTRACED|PF_TRACESYS);
p->pid = last_pid;
+ p->next_run = NULL;
+ p->prev_run = NULL;
p->p_pptr = p->p_opptr = current;
p->p_cptr = NULL;
p->signal = 0;
diff -u --recursive --new-file v1.3.11/linux/kernel/sched.c linux/kernel/sched.c
--- v1.3.11/linux/kernel/sched.c Tue Jul 18 16:28:59 1995
+++ linux/kernel/sched.c Tue Jul 18 17:56:31 1995
@@ -92,32 +92,77 @@

struct kernel_stat kstat = { 0 };

+static inline void add_to_runqueue(struct task_struct * p)
+{
+#if 1 /* sanity tests */
+ if (p->next_run || p->prev_run) {
+ printk("task already on run-queue\n");
+ return;
+ }
+#endif
+ if (p->counter > current->counter + 3)
+ need_resched = 1;
+ nr_running++;
+ (p->next_run = init_task.next_run)->prev_run = p;
+ p->prev_run = &init_task;
+ init_task.next_run = p;
+}
+
+static inline void del_from_runqueue(struct task_struct * p)
+{
+ struct task_struct *next = p->next_run;
+ struct task_struct *prev = p->prev_run;
+
+#if 1 /* sanity tests */
+ if (!next || !prev) {
+ printk("task not on run-queue\n");
+ return;
+ }
+#endif
+ if (p == &init_task) {
+ printk("idle task may not sleep\n");
+ return;
+ }
+ nr_running--;
+ next->prev_run = prev;
+ prev->next_run = next;
+ p->next_run = NULL;
+ p->prev_run = NULL;
+}
+
/*
* Wake up a process. Put it on the run-queue if it's not
* already there. The "current" process is always on the
- * run-queue, and as such you're allowed to do the simpler
+ * run-queue (except when the actual re-schedule is in
+ * progress), and as such you're allowed to do the simpler
* "current->state = TASK_RUNNING" to mark yourself runnable
* without the overhead of this.
- *
- * (actually, the run-queue isn't implemented yet, so this
- * function is mostly a dummy one)
*/
inline void wake_up_process(struct task_struct * p)
{
- long oldstate;
+ unsigned long flags;

- oldstate = xchg(&p->state, TASK_RUNNING);
- /* already on run-queue? */
- if (oldstate == TASK_RUNNING || p == current)
- return;
- if (p->counter > current->counter + 3)
- need_resched = 1;
+ save_flags(flags);
+ cli();
+ p->state = TASK_RUNNING;
+ if (!p->next_run)
+ add_to_runqueue(p);
+ restore_flags(flags);
+}
+
+static void process_timeout(unsigned long __data)
+{
+ struct task_struct * p = (struct task_struct *) __data;
+
+ p->timeout = 0;
+ wake_up_process(p);
}

/*
* 'schedule()' is the scheduler function. It's a very simple and nice
* scheduler: it's not perfect, but certainly works for most things.
- * The one thing you might take a look at is the signal-handler code here.
+ *
+ * The goto is "interesting".
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
@@ -128,6 +173,7 @@
int c;
struct task_struct * p;
struct task_struct * next;
+ unsigned long timeout = 0;

/* check alarm, wake up any interruptible tasks that have got a signal */

@@ -137,31 +183,39 @@
}
run_task_queue(&tq_scheduler);

- if (current->state == TASK_INTERRUPTIBLE) {
- if (current->signal & ~current->blocked)
- current->state = TASK_RUNNING;
- }
need_resched = 0;
- nr_running = 0;
+ cli();
+ switch (current->state) {
+ case TASK_INTERRUPTIBLE:
+ if (current->signal & ~current->blocked)
+ goto makerunnable;
+ timeout = current->timeout;
+ if (timeout && (timeout <= jiffies)) {
+ current->timeout = 0;
+ timeout = 0;
+ makerunnable:
+ current->state = TASK_RUNNING;
+ break;
+ }
+ default:
+ del_from_runqueue(current);
+ case TASK_RUNNING:
+ }
+ p = init_task.next_run;
+ sti();

+/*
+ * Note! there may appear new tasks on the run-queue during this, as
+ * interrupts are enabled. However, they will be put on front of the
+ * list, so our list starting at "p" is essentially fixed.
+ */
/* this is the scheduler proper: */
c = -1000;
next = &init_task;
- for_each_task(p) {
- switch (p->state) {
- case TASK_INTERRUPTIBLE:
- if (!p->timeout)
- continue;
- if (p->timeout > jiffies)
- continue;
- p->timeout = 0;
- p->state = TASK_RUNNING;
- /* fall through */
- case TASK_RUNNING:
- nr_running++;
- if (p->counter > c)
- c = p->counter, next = p;
- }
+ while (p != &init_task) {
+ if (p->counter > c)
+ c = p->counter, next = p;
+ p = p->next_run;
}

/* if all runnable processes have "counter == 0", re-calculate counters */
@@ -169,10 +223,21 @@
for_each_task(p)
p->counter = (p->counter >> 1) + p->priority;
}
- if (current == next)
- return;
- kstat.context_swtch++;
- switch_to(next);
+ if (current != next) {
+ struct timer_list timer;
+
+ kstat.context_swtch++;
+ if (timeout) {
+ init_timer(&timer);
+ timer.expires = timeout - jiffies;
+ timer.data = (unsigned long) current;
+ timer.function = process_timeout;
+ add_timer(&timer);
+ }
+ switch_to(next);
+ if (timeout)
+ del_timer(&timer);
+ }
}

asmlinkage int sys_pause(void)
----------