1. 23 Aug, 2004 1 commit
    • Ingo Molnar's avatar
      [PATCH] context-switching overhead in X, ioport() · a55702bb
      Ingo Molnar authored
      while debugging/improving scheduling latencies i got the following
      strange latency report from Lee Revell:
      
        http://krustophenia.net/testresults.php?dataset=2.6.8.1-P6#/var/www/2.6.8.1-P6
      
      this trace shows a 120 usec latency caused by XFree86, on a 600 MHz x86
      system. Looking closer reveals:
      
        00000002 0.006ms (+0.003ms): __switch_to (schedule)
        00000002 0.088ms (+0.082ms): finish_task_switch (schedule)
      
      it took more than 80 usecs for XFree86 to do a context-switch!
      
      it turns out that the reason for this (massive) context-switching
      overhead is the following change in 2.6.8:
      
            [PATCH] larger IO bitmaps
      
      To demonstrate the effect of this change i've written ioperm-latency.c
      (attached), which gives the following on vanilla 2.6.8.1:
      
        # ./ioperm-latency
        default no ioperm:             scheduling latency: 2528 cycles
        turning on port 80 ioperm:     scheduling latency: 10563 cycles
        turning on port 65535 ioperm:  scheduling latency: 10517 cycles
      
      the ChangeSet says:
      
              Now, with the lazy bitmap allocation and per-CPU TSS, this
              will really not drain any resources I think.
      
      this is plain wrong. An increase in the IO bitmap size introduces
      per-context-switch overhead as well: we now have to copy an 8K bitmap
      every time XFree86 context-switches - even though XFree86 never uses
      ports higher than 1024! I've straced XFree86 on a number of x86 systems
      and in every instance ioperm() was used - so i'd say the majority of x86
      Linux systems running 2.6.8.1 are affected by this problem.
      
      This not only causes lots of overhead, it also trashes ~16K out of the
      L1 and L2 caches, on every context-switch. It's as if XFree86 did a L1
      cache flush on every context-switch ...
      
      the simple solution would be to revert IO_BITMAP_BITS back to 1024 and
      release 2.6.8.2?
      
      I've implemented another solution as well, which tracks the
      highest-enabled port # for every task and does the copying of the bitmap
      intelligently. (patch attached) The patched kernel gives:
      
        # ./ioperm-latency
        default no ioperm:             scheduling latency: 2423 cycles
        turning on port 80 ioperm:     scheduling latency: 2503 cycles
        turning on port 65535 ioperm:  scheduling latency: 10607 cycles
      
      this is much more acceptable - the full overhead only occurs in the very
      unlikely event of a task using the high ioport range. X doesnt suffer
      any significant overhead.
      
      (tracking the maximum allowed port # also allows a simplification of
      io_bitmap handling: e.g. we dont do the invalid-offset trick anymore -
      the IO bitmap in the TSS is always valid and secure.)
      
      I tested the patch on x86 SMP and UP, it works fine for me. I tested
      boundary conditions as well, it all seems secure.
      
      	Ingo
      
      #include <errno.h>
      #include <stdio.h>
      #include <sched.h>
      #include <signal.h>
      #include <sys/io.h>
      #include <stdlib.h>
      #include <unistd.h>
      #include <linux/unistd.h>
      
      #define CYCLES(x) asm volatile ("rdtsc" :"=a" (x)::"edx")
      
      #define __NR_sched_set_affinity 241
      _syscall3 (int, sched_set_affinity, pid_t, pid, unsigned int, mask_len, unsigned long *, mask)
      
      /*
       * Use a pair of RT processes bound to the same CPU to measure
       * context-switch overhead:
       */
      static void measure(void)
      {
      	unsigned long i, min = ~0UL, pid, mask = 1, t1, t2;
      
      	sched_set_affinity(0, sizeof(mask), &mask);
      
      	pid = fork();
      	if (!pid)
      		for (;;) {
      			asm volatile ("sti; nop; cli");
      			sched_yield();
      		}
      
      	sched_yield();
      	for (i = 0; i < 100; i++) {
      		asm volatile ("sti; nop; cli");
      		CYCLES(t1);
      		sched_yield();
      		CYCLES(t2);
      		if (i > 10) {
      			if (t2 - t1 < min)
      				min = t2 - t1;
      		}
      	}
      	asm volatile ("sti");
      
      	kill(pid, 9);
      	printf("scheduling latency: %ld cycles\n", min);
      	sched_yield();
      }
      
      int main(void)
      {
      	struct sched_param p = { sched_priority: 2 };
      	unsigned long mask = 1;
      
      	if (iopl(3)) {
      		printf("need to run as root!\n");
      		exit(-1);
      	}
      	sched_setscheduler(0, SCHED_FIFO, &p);
      	sched_set_affinity(0, sizeof(mask), &mask);
      
      	printf("default no ioperm:             ");
      	measure();
      
      	printf("turning on port 80 ioperm:     ");
      	ioperm(0x80,1,1);
      	measure();
      
      	printf("turning on port 65535 ioperm:  ");
      	if (ioperm(0xffff,1,1))
      		printf("FAILED - older kernel.\n");
      	else
      		measure();
      
      	return 0;
      }
      Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
      Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
      Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
      a55702bb
  2. 22 Aug, 2004 20 commits
  3. 20 Aug, 2004 8 commits
  4. 19 Aug, 2004 11 commits