
From: Ingo Molnar <mingo@elte.hu>

This uses Davide's do_general_protection() fault based io-bitmap lazy update
code and combines it with the ioport-owner cache.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/arch/i386/kernel/ioport.c    |   10 +++++-----
 25-akpm/arch/i386/kernel/process.c   |   17 ++++++++---------
 25-akpm/arch/i386/kernel/traps.c     |   30 ++++++++++++++++++++++++++++++
 25-akpm/include/asm-i386/processor.h |    1 +
 4 files changed, 44 insertions(+), 14 deletions(-)

diff -puN arch/i386/kernel/ioport.c~ioport-cache-gpf-2.6.9-rc1-mm5-A3 arch/i386/kernel/ioport.c
--- 25/arch/i386/kernel/ioport.c~ioport-cache-gpf-2.6.9-rc1-mm5-A3	2004-09-14 01:48:34.199906544 -0700
+++ 25-akpm/arch/i386/kernel/ioport.c	2004-09-14 01:48:34.207905328 -0700
@@ -105,11 +105,11 @@ asmlinkage long sys_ioperm(unsigned long
 
 	t->io_bitmap_max = bytes;
 
-	/* Update the TSS: */
-	memcpy(tss->io_bitmap, t->io_bitmap_ptr, bytes_updated);
-	tss->io_bitmap_max = bytes;
-	tss->io_bitmap_owner = &current->thread;
-	tss->io_bitmap_base = IO_BITMAP_OFFSET;
+	/*
+	 * Sets the lazy trigger so that the next I/O operation will
+	 * reload the correct bitmap.
+	 */
+	tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET_LAZY;
 
 	put_cpu();
 
diff -puN arch/i386/kernel/process.c~ioport-cache-gpf-2.6.9-rc1-mm5-A3 arch/i386/kernel/process.c
--- 25/arch/i386/kernel/process.c~ioport-cache-gpf-2.6.9-rc1-mm5-A3	2004-09-14 01:48:34.201906240 -0700
+++ 25-akpm/arch/i386/kernel/process.c	2004-09-14 01:48:34.207905328 -0700
@@ -496,17 +496,16 @@ handle_io_bitmap(struct thread_struct *n
 		return;
 	}
 	/*
-	 * The IO bitmap in the TSS needs updating: copy the relevant
-	 * range of the new task's IO bitmap. Normally this is 128 bytes
-	 * or less:
+	 * Lazy TSS's I/O bitmap copy. We set an invalid offset here
+	 * and we let the task to get a GPF in case an I/O instruction
+	 * is performed.  The handler of the GPF will verify that the
+	 * faulting task has a valid I/O bitmap and, it true, does the
+	 * real copy and restart the instruction.  This will save us
+	 * redundant copies when the currently switched task does not
+	 * perform any I/O during its timeslice.
 	 */
-	memcpy(tss->io_bitmap, next->io_bitmap_ptr,
-		max(tss->io_bitmap_max, next->io_bitmap_max));
-	tss->io_bitmap_max = next->io_bitmap_max;
-	tss->io_bitmap_owner = next;
-	tss->io_bitmap_base = IO_BITMAP_OFFSET;
+	tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET_LAZY;
 }
-
 /*
  * This special macro can be used to load a debugging register
  */
diff -puN arch/i386/kernel/traps.c~ioport-cache-gpf-2.6.9-rc1-mm5-A3 arch/i386/kernel/traps.c
--- 25/arch/i386/kernel/traps.c~ioport-cache-gpf-2.6.9-rc1-mm5-A3	2004-09-14 01:48:34.202906088 -0700
+++ 25-akpm/arch/i386/kernel/traps.c	2004-09-14 01:48:34.209905024 -0700
@@ -479,6 +479,36 @@ DO_ERROR_INFO(17, SIGBUS, "alignment che
 
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
 {
+	int cpu = get_cpu();
+	struct tss_struct *tss = &per_cpu(init_tss, cpu);
+	struct thread_struct *thread = &current->thread;
+
+	/*
+	 * Perform the lazy TSS's I/O bitmap copy. If the TSS has an
+	 * invalid offset set (the LAZY one) and the faulting thread has
+	 * a valid I/O bitmap pointer, we copy the I/O bitmap in the TSS
+	 * and we set the offset field correctly. Then we let the CPU to
+	 * restart the faulting instruction.
+	 */
+	if (tss->io_bitmap_base == INVALID_IO_BITMAP_OFFSET_LAZY &&
+	    thread->io_bitmap_ptr) {
+		memcpy(tss->io_bitmap, thread->io_bitmap_ptr,
+		       thread->io_bitmap_max);
+		/*
+		 * If the previously set map was extending to higher ports
+		 * than the current one, pad extra space with 0xff (no access).
+		 */
+		if (thread->io_bitmap_max < tss->io_bitmap_max)
+			memset((char *) tss->io_bitmap +
+				thread->io_bitmap_max, 0xff,
+				tss->io_bitmap_max - thread->io_bitmap_max);
+		tss->io_bitmap_max = thread->io_bitmap_max;
+		tss->io_bitmap_base = IO_BITMAP_OFFSET;
+		put_cpu();
+		return;
+	}
+	put_cpu();
+
 	if (regs->eflags & VM_MASK)
 		goto gp_in_vm86;
 
diff -puN include/asm-i386/processor.h~ioport-cache-gpf-2.6.9-rc1-mm5-A3 include/asm-i386/processor.h
--- 25/include/asm-i386/processor.h~ioport-cache-gpf-2.6.9-rc1-mm5-A3	2004-09-14 01:48:34.204905784 -0700
+++ 25-akpm/include/asm-i386/processor.h	2004-09-14 01:48:34.210904872 -0700
@@ -307,6 +307,7 @@ extern unsigned int mca_pentium_flag;
 #define IO_BITMAP_LONGS (IO_BITMAP_BYTES/sizeof(long))
 #define IO_BITMAP_OFFSET offsetof(struct tss_struct,io_bitmap)
 #define INVALID_IO_BITMAP_OFFSET 0x8000
+#define INVALID_IO_BITMAP_OFFSET_LAZY 0x9000
 
 struct i387_fsave_struct {
 	long	cwd;
_
