changeset:   45641:a80a5617ce8a6ea5f9ee88a4ca5477e3b956df1f
tag:         tip
user:        James Morris <jmorris@namei.org>
date:        Tue Jan 30 04:17:34 2007 -0500
files:       arch/i386/Kconfig arch/i386/kernel/lguest.c arch/i386/kernel/smp.c drivers/lguest/core.c drivers/lguest/page_tables.c
description:
Add host SMP support, making the hv pte page per-cpu.

Signed-off-by: James Morris <jmorris@namei.org>

---
 arch/i386/Kconfig            |    2 -
 arch/i386/kernel/lguest.c    |   10 +++++
 arch/i386/kernel/smp.c       |    1 
 drivers/lguest/core.c        |    7 ++-
 drivers/lguest/page_tables.c |   84 +++++++++++++++++++++++++++++++------------
 5 files changed, 77 insertions(+), 27 deletions(-)

diff -r e6f2d789ef5c8dfb7e3cd2b3d37583f368d69ccf -r a80a5617ce8a6ea5f9ee88a4ca5477e3b956df1f arch/i386/Kconfig
--- a/arch/i386/Kconfig	Tue Jan 30 04:15:44 2007 -0500
+++ b/arch/i386/Kconfig	Tue Jan 30 04:17:34 2007 -0500
@@ -252,7 +252,7 @@ source "arch/i386/Kconfig.cpu"
 
 config LGUEST
 	tristate "Linux hypervisor example code"
-	depends on X86 && PARAVIRT && EXPERIMENTAL && !SMP && !X86_PAE
+	depends on X86 && PARAVIRT && EXPERIMENTAL && !X86_PAE
 	select LGUEST_GUEST
 	select HVC_DRIVER
 	---help---
diff -r e6f2d789ef5c8dfb7e3cd2b3d37583f368d69ccf -r a80a5617ce8a6ea5f9ee88a4ca5477e3b956df1f arch/i386/kernel/lguest.c
--- a/arch/i386/kernel/lguest.c	Tue Jan 30 04:15:44 2007 -0500
+++ b/arch/i386/kernel/lguest.c	Tue Jan 30 04:17:34 2007 -0500
@@ -247,6 +247,14 @@ static fastcall unsigned long lguest_api
 {
 	return 0;
 }
+
+static void lguest_setup_boot_clock(void)
+{
+}
+
+static void lguest_setup_secondary_clock(void)
+{
+}
 #endif
 
 /* We move eflags word to lguest_page.irq_enabled to restore interrupt
@@ -494,6 +502,8 @@ static __attribute_used__ __init void lg
 	paravirt_ops.apic_write = lguest_apic_write;
 	paravirt_ops.apic_write_atomic = lguest_apic_write_atomic;
 	paravirt_ops.apic_read = lguest_apic_read;
+	paravirt_ops.setup_boot_clock = lguest_setup_boot_clock;
+	paravirt_ops.setup_secondary_clock = lguest_setup_secondary_clock;
 #endif
 	paravirt_ops.load_idt = lguest_load_idt;
 	paravirt_ops.iret = lguest_iret;
diff -r e6f2d789ef5c8dfb7e3cd2b3d37583f368d69ccf -r a80a5617ce8a6ea5f9ee88a4ca5477e3b956df1f arch/i386/kernel/smp.c
--- a/arch/i386/kernel/smp.c	Tue Jan 30 04:15:44 2007 -0500
+++ b/arch/i386/kernel/smp.c	Tue Jan 30 04:17:34 2007 -0500
@@ -479,6 +479,7 @@ void flush_tlb_all(void)
 {
 	on_each_cpu(do_flush_tlb_all, NULL, 1, 1);
 }
+EXPORT_SYMBOL_GPL(flush_tlb_all);
 
 /*
  * this function sends a 'reschedule' IPI to another CPU.
diff -r e6f2d789ef5c8dfb7e3cd2b3d37583f368d69ccf -r a80a5617ce8a6ea5f9ee88a4ca5477e3b956df1f drivers/lguest/core.c
--- a/drivers/lguest/core.c	Tue Jan 30 04:15:44 2007 -0500
+++ b/drivers/lguest/core.c	Tue Jan 30 04:17:34 2007 -0500
@@ -647,11 +647,12 @@ static void __init setup_hypervisor(stru
 static void __init setup_hypervisor(struct hypervisor *lguest)
 {
 	struct desc_struct lguest_cs, lguest_ds;
-	unsigned int i;
+	unsigned int i, cpu = get_cpu();
 
 	/* Insert lguest entries in GDT (copy kernel segments). */
-	lguest_cs = get_cpu_gdt_table(smp_processor_id())[GDT_ENTRY_KERNEL_CS];
-	lguest_ds = get_cpu_gdt_table(smp_processor_id())[GDT_ENTRY_KERNEL_DS];
+	lguest_cs = get_cpu_gdt_table(cpu)[GDT_ENTRY_KERNEL_CS];
+	lguest_ds = get_cpu_gdt_table(cpu)[GDT_ENTRY_KERNEL_DS];
+	put_cpu();
 	for_each_possible_cpu(i) {
 		get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_CS] = lguest_cs;
 		get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_DS] = lguest_ds;
diff -r e6f2d789ef5c8dfb7e3cd2b3d37583f368d69ccf -r a80a5617ce8a6ea5f9ee88a4ca5477e3b956df1f drivers/lguest/page_tables.c
--- a/drivers/lguest/page_tables.c	Tue Jan 30 04:15:44 2007 -0500
+++ b/drivers/lguest/page_tables.c	Tue Jan 30 04:17:34 2007 -0500
@@ -5,6 +5,7 @@
 #include <linux/types.h>
 #include <linux/spinlock.h>
 #include <linux/random.h>
+#include <linux/percpu.h>
 #include <asm/tlbflush.h>
 #include "lguest.h"
 
@@ -19,7 +20,9 @@ struct pgtable_page_info {
 };
 
 static struct pgtable_page_info pg_info[MAX_PAGETABLE_PAGES];
-static u32 *hypervisor_pte_page;
+static DEFINE_PER_CPU(u32 *, hypervisor_pte_pages) = { NULL };
+
+#define hypervisor_pte_page(cpu) per_cpu(hypervisor_pte_pages, cpu)
 
 /* These access the real versions. */
 static u32 *toplev(struct lguest_guest_info *linfo, unsigned long vaddr)
@@ -311,10 +314,6 @@ int init_guest_pagetable(struct lguest_g
 	linfo->pgdir = (u32 *)get_zeroed_page(GFP_KERNEL);
 	if (!linfo->pgdir)
 		return -ENOMEM;
-
-	/* Since hypervisor less that 4MB, we simply mug top pte page. */
-	linfo->pgdir[PAGE_SIZE/sizeof(u32)-1] 
-		= (__pa(hypervisor_pte_page) | _PAGE_KERNEL);
 	return 0;
 }
 
@@ -361,21 +360,62 @@ void guest_set_pud(struct lguest_guest_i
 	pin_stack_pages(linfo);
 }
 
+/* Caller must be preempt-safe */
 void map_trap_page(struct lguest_guest_info *linfo)
 {
-	/* FIXME: For SMP, we need one pte page per CPU: altering it
-	 * for current guest only works on UP. */
-	hypervisor_pte_page[0] = (__pa(linfo->trap_page)|_PAGE_PRESENT);
+	int cpu = smp_processor_id();
+	
+	hypervisor_pte_page(cpu)[0] = (__pa(linfo->trap_page)|_PAGE_PRESENT);
+
+	/* Since hypervisor less that 4MB, we simply mug top pte page. */
+	linfo->pgdir[PAGE_SIZE/sizeof(u32)-1] =
+				(__pa(hypervisor_pte_page(cpu))| _PAGE_KERNEL);
+}
+
+static __exit void free_hypervisor_pte_pages(void)
+{
+	int i;
+	
+	for_each_possible_cpu(i)
+		free_page((long)hypervisor_pte_page(i));
+}
+
+static __init int alloc_hypervisor_pte_pages(void)
+{
+	int i;
+	
+	for_each_possible_cpu(i) {
+		u32 *p = (u32 *)get_zeroed_page(GFP_KERNEL);
+
+		hypervisor_pte_page(i) = p;
+		if (!hypervisor_pte_page(i)) {
+			free_hypervisor_pte_pages();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+static __init void populate_hypervisor_pte_page(int cpu)
+{
+	int i;
+	u32 *pte;
+
+	pte = hypervisor_pte_page(cpu) + (lguest_address() / PAGE_SIZE) % 1024;
+	for (i = 0; i < HYPERVISOR_PAGES; i++) {
+		pte[i] = ((page_to_pfn(&hype_pages[i]) << PAGE_SHIFT) 
+			  | _PAGE_KERNEL_EXEC);
+	}
 }
 
 __init int init_pagetables(struct page hype_pages[])
 {
-	unsigned int i;
-	u32 *pte;
-
-	hypervisor_pte_page = (u32 *)get_zeroed_page(GFP_KERNEL);
-	if (!hypervisor_pte_page)
-		return -ENOMEM;
+	int ret;
+	unsigned int i;
+
+	ret = alloc_hypervisor_pte_pages();
+	if (ret)
+		return ret;
 
 	for (i = 0; i < ARRAY_SIZE(pg_info); i++) {
 		pg_info[i].guestid = NO_GUEST;
@@ -384,17 +424,15 @@ __init int init_pagetables(struct page h
 			unsigned int j;
 			for (j = 0; j < i; j++)
 				free_page((long)pg_info[j].page);
-			free_page((long)hypervisor_pte_page);
+			free_hypervisor_pte_pages();
 			return -ENOMEM;
 		}
 	}
 
-	/* Now populate hypervisor pagetable page. */
-	pte = hypervisor_pte_page + (lguest_address() / PAGE_SIZE) % 1024;
-	for (i = 0; i < HYPERVISOR_PAGES; i++) {
-		pte[i] = ((page_to_pfn(&hype_pages[i]) << PAGE_SHIFT) 
-			  | _PAGE_KERNEL_EXEC);
-	}
+	/* Now populate per-cpu hypervisor pagetable page. */
+	for_each_possible_cpu(i)
+		populate_hypervisor_pte_page(i);
+
 	return 0;
 }
 
@@ -403,5 +441,5 @@ __exit void free_pagetables(void)
 	unsigned int i;
 	for (i = 0; i < ARRAY_SIZE(pg_info); i++)
 		free_page((long)pg_info[i].page);
-	free_page((long)hypervisor_pte_page);
-}
+	free_hypervisor_pte_pages();
+}


