xen: make phys_to_machine structure dynamic
authorJeremy Fitzhardinge <jeremy@goop.org>
Mon, 26 May 2008 22:31:18 +0000 (23:31 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 27 May 2008 08:11:37 +0000 (10:11 +0200)
We now support the use of memory hotplug, so the physical to machine
page mapping structure must be dynamic.  This is implemented as a
two-level radix tree structure, which allows us to efficiently
incrementally allocate memory for the p2m table as new pages are
added.

Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/xen/enlighten.c
arch/x86/xen/mmu.c
arch/x86/xen/setup.c
arch/x86/xen/xen-ops.h
include/asm-x86/xen/page.h

index 5c0635a..73d3c84 100644 (file)
@@ -1221,7 +1221,7 @@ asmlinkage void __init xen_start_kernel(void)
 
        /* Get mfn list */
        if (!xen_feature(XENFEAT_auto_translated_physmap))
-               phys_to_machine_mapping = (unsigned long *)xen_start_info->mfn_list;
+               xen_build_dynamic_phys_to_machine();
 
        pgd = (pgd_t *)xen_start_info->pt_base;
 
index 07c2653..c3b27de 100644 (file)
 #include "multicalls.h"
 #include "mmu.h"
 
+/*
+ * This should probably be a config option.  On 32-bit, it costs 1
+ * page/gig of memory; on 64-bit its 2 pages/gig.  If we want it to be
+ * completely unbounded we can add another level to the p2m structure.
+ */
+#define MAX_GUEST_PAGES                (16ull * 1024*1024*1024 / PAGE_SIZE)
+#define P2M_ENTRIES_PER_PAGE   (PAGE_SIZE / sizeof(unsigned long))
+
+static unsigned long *p2m_top[MAX_GUEST_PAGES / P2M_ENTRIES_PER_PAGE];
+
+static inline unsigned p2m_top_index(unsigned long pfn)
+{
+       BUG_ON(pfn >= MAX_GUEST_PAGES);
+       return pfn / P2M_ENTRIES_PER_PAGE;
+}
+
+static inline unsigned p2m_index(unsigned long pfn)
+{
+       return pfn % P2M_ENTRIES_PER_PAGE;
+}
+
+void __init xen_build_dynamic_phys_to_machine(void)
+{
+       unsigned pfn;
+       unsigned long *mfn_list = (unsigned long *)xen_start_info->mfn_list;
+
+       BUG_ON(xen_start_info->nr_pages >= MAX_GUEST_PAGES);
+
+       for(pfn = 0;
+           pfn < xen_start_info->nr_pages;
+           pfn += P2M_ENTRIES_PER_PAGE) {
+               unsigned topidx = p2m_top_index(pfn);
+
+               p2m_top[topidx] = &mfn_list[pfn];
+       }
+}
+
+unsigned long get_phys_to_machine(unsigned long pfn)
+{
+       unsigned topidx, idx;
+
+       topidx = p2m_top_index(pfn);
+       if (p2m_top[topidx] == NULL)
+               return INVALID_P2M_ENTRY;
+
+       idx = p2m_index(pfn);
+       return p2m_top[topidx][idx];
+}
+
+static void alloc_p2m(unsigned long **pp)
+{
+       unsigned long *p;
+       unsigned i;
+
+       p = (void *)__get_free_page(GFP_KERNEL | __GFP_NOFAIL);
+       BUG_ON(p == NULL);
+
+       for(i = 0; i < P2M_ENTRIES_PER_PAGE; i++)
+               p[i] = INVALID_P2M_ENTRY;
+
+       if (cmpxchg(pp, NULL, p) != NULL)
+               free_page((unsigned long)p);
+}
+
+void set_phys_to_machine(unsigned long pfn, unsigned long mfn)
+{
+       unsigned topidx, idx;
+
+       if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) {
+               BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY);
+               return;
+       }
+
+       topidx = p2m_top_index(pfn);
+       if (p2m_top[topidx] == NULL) {
+               /* no need to allocate a page to store an invalid entry */
+               if (mfn == INVALID_P2M_ENTRY)
+                       return;
+               alloc_p2m(&p2m_top[topidx]);
+       }
+
+       idx = p2m_index(pfn);
+       p2m_top[topidx][idx] = mfn;
+}
+
 xmaddr_t arbitrary_virt_to_machine(unsigned long address)
 {
        unsigned int level;
index 82517e4..37f8f0b 100644 (file)
@@ -27,8 +27,6 @@
 extern const char xen_hypervisor_callback[];
 extern const char xen_failsafe_callback[];
 
-unsigned long *phys_to_machine_mapping;
-EXPORT_SYMBOL(phys_to_machine_mapping);
 
 /**
  * machine_specific_memory_setup - Hook for machine specific memory setup.
index f1063ae..7bdc8c5 100644 (file)
@@ -22,6 +22,8 @@ void __init xen_arch_setup(void);
 void __init xen_init_IRQ(void);
 void xen_enable_sysenter(void);
 
+void __init xen_build_dynamic_phys_to_machine(void);
+
 void xen_setup_timer(int cpu);
 void xen_setup_cpu_clockevents(void);
 unsigned long xen_cpu_khz(void);
index e11f240..293344f 100644 (file)
@@ -26,15 +26,15 @@ typedef struct xpaddr {
 #define FOREIGN_FRAME_BIT      (1UL<<31)
 #define FOREIGN_FRAME(m)       ((m) | FOREIGN_FRAME_BIT)
 
-extern unsigned long *phys_to_machine_mapping;
+extern unsigned long get_phys_to_machine(unsigned long pfn);
+extern void set_phys_to_machine(unsigned long pfn, unsigned long mfn);
 
 static inline unsigned long pfn_to_mfn(unsigned long pfn)
 {
        if (xen_feature(XENFEAT_auto_translated_physmap))
                return pfn;
 
-       return phys_to_machine_mapping[(unsigned int)(pfn)] &
-               ~FOREIGN_FRAME_BIT;
+       return get_phys_to_machine(pfn) & ~FOREIGN_FRAME_BIT;
 }
 
 static inline int phys_to_machine_mapping_valid(unsigned long pfn)
@@ -42,7 +42,7 @@ static inline int phys_to_machine_mapping_valid(unsigned long pfn)
        if (xen_feature(XENFEAT_auto_translated_physmap))
                return 1;
 
-       return (phys_to_machine_mapping[pfn] != INVALID_P2M_ENTRY);
+       return get_phys_to_machine(pfn) != INVALID_P2M_ENTRY;
 }
 
 static inline unsigned long mfn_to_pfn(unsigned long mfn)
@@ -106,20 +106,12 @@ static inline unsigned long mfn_to_local_pfn(unsigned long mfn)
        unsigned long pfn = mfn_to_pfn(mfn);
        if ((pfn < max_mapnr)
            && !xen_feature(XENFEAT_auto_translated_physmap)
-           && (phys_to_machine_mapping[pfn] != mfn))
+           && (get_phys_to_machine(pfn) != mfn))
                return max_mapnr; /* force !pfn_valid() */
+       /* XXX fixme; not true with sparsemem */
        return pfn;
 }
 
-static inline void set_phys_to_machine(unsigned long pfn, unsigned long mfn)
-{
-       if (xen_feature(XENFEAT_auto_translated_physmap)) {
-               BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY);
-               return;
-       }
-       phys_to_machine_mapping[pfn] = mfn;
-}
-
 /* VIRT <-> MACHINE conversion */
 #define virt_to_machine(v)     (phys_to_machine(XPADDR(__pa(v))))
 #define virt_to_mfn(v)         (pfn_to_mfn(PFN_DOWN(__pa(v))))