xen: switch extra memory accounting to use pfns
[cascardo/linux.git] / arch / x86 / xen / setup.c
index 0d7f881..f5ef674 100644 (file)
 #include <xen/hvc-console.h>
 #include "xen-ops.h"
 #include "vdso.h"
-#include "p2m.h"
 #include "mmu.h"
 
+#define GB(x) ((uint64_t)(x) * 1024 * 1024 * 1024)
+
 /* Amount of extra memory space we add to the e820 ranges */
 struct xen_memory_region xen_extra_mem[XEN_EXTRA_MEM_MAX_REGIONS] __initdata;
 
@@ -69,62 +70,89 @@ static unsigned long xen_remap_mfn __initdata = INVALID_P2M_ENTRY;
  */
 #define EXTRA_MEM_RATIO                (10)
 
-static void __init xen_add_extra_mem(phys_addr_t start, phys_addr_t size)
+static bool xen_512gb_limit __initdata = IS_ENABLED(CONFIG_XEN_512GB);
+
+static void __init xen_parse_512gb(void)
+{
+       bool val = false;
+       char *arg;
+
+       arg = strstr(xen_start_info->cmd_line, "xen_512gb_limit");
+       if (!arg)
+               return;
+
+       arg = strstr(xen_start_info->cmd_line, "xen_512gb_limit=");
+       if (!arg)
+               val = true;
+       else if (strtobool(arg + strlen("xen_512gb_limit="), &val))
+               return;
+
+       xen_512gb_limit = val;
+}
+
+static void __init xen_add_extra_mem(unsigned long start_pfn,
+                                    unsigned long n_pfns)
 {
        int i;
 
+       /*
+        * No need to check for zero size, should happen rarely and will only
+        * write a new entry regarded to be unused due to zero size.
+        */
        for (i = 0; i < XEN_EXTRA_MEM_MAX_REGIONS; i++) {
                /* Add new region. */
-               if (xen_extra_mem[i].size == 0) {
-                       xen_extra_mem[i].start = start;
-                       xen_extra_mem[i].size  = size;
+               if (xen_extra_mem[i].n_pfns == 0) {
+                       xen_extra_mem[i].start_pfn = start_pfn;
+                       xen_extra_mem[i].n_pfns = n_pfns;
                        break;
                }
                /* Append to existing region. */
-               if (xen_extra_mem[i].start + xen_extra_mem[i].size == start) {
-                       xen_extra_mem[i].size += size;
+               if (xen_extra_mem[i].start_pfn + xen_extra_mem[i].n_pfns ==
+                   start_pfn) {
+                       xen_extra_mem[i].n_pfns += n_pfns;
                        break;
                }
        }
        if (i == XEN_EXTRA_MEM_MAX_REGIONS)
                printk(KERN_WARNING "Warning: not enough extra memory regions\n");
 
-       memblock_reserve(start, size);
+       memblock_reserve(PFN_PHYS(start_pfn), PFN_PHYS(n_pfns));
 }
 
-static void __init xen_del_extra_mem(phys_addr_t start, phys_addr_t size)
+static void __init xen_del_extra_mem(unsigned long start_pfn,
+                                    unsigned long n_pfns)
 {
        int i;
-       phys_addr_t start_r, size_r;
+       unsigned long start_r, size_r;
 
        for (i = 0; i < XEN_EXTRA_MEM_MAX_REGIONS; i++) {
-               start_r = xen_extra_mem[i].start;
-               size_r = xen_extra_mem[i].size;
+               start_r = xen_extra_mem[i].start_pfn;
+               size_r = xen_extra_mem[i].n_pfns;
 
                /* Start of region. */
-               if (start_r == start) {
-                       BUG_ON(size > size_r);
-                       xen_extra_mem[i].start += size;
-                       xen_extra_mem[i].size -= size;
+               if (start_r == start_pfn) {
+                       BUG_ON(n_pfns > size_r);
+                       xen_extra_mem[i].start_pfn += n_pfns;
+                       xen_extra_mem[i].n_pfns -= n_pfns;
                        break;
                }
                /* End of region. */
-               if (start_r + size_r == start + size) {
-                       BUG_ON(size > size_r);
-                       xen_extra_mem[i].size -= size;
+               if (start_r + size_r == start_pfn + n_pfns) {
+                       BUG_ON(n_pfns > size_r);
+                       xen_extra_mem[i].n_pfns -= n_pfns;
                        break;
                }
                /* Mid of region. */
-               if (start > start_r && start < start_r + size_r) {
-                       BUG_ON(start + size > start_r + size_r);
-                       xen_extra_mem[i].size = start - start_r;
+               if (start_pfn > start_r && start_pfn < start_r + size_r) {
+                       BUG_ON(start_pfn + n_pfns > start_r + size_r);
+                       xen_extra_mem[i].n_pfns = start_pfn - start_r;
                        /* Calling memblock_reserve() again is okay. */
-                       xen_add_extra_mem(start + size, start_r + size_r -
-                                         (start + size));
+                       xen_add_extra_mem(start_pfn + n_pfns, start_r + size_r -
+                                         (start_pfn + n_pfns));
                        break;
                }
        }
-       memblock_free(start, size);
+       memblock_free(PFN_PHYS(start_pfn), PFN_PHYS(n_pfns));
 }
 
 /*
@@ -135,11 +163,10 @@ static void __init xen_del_extra_mem(phys_addr_t start, phys_addr_t size)
 unsigned long __ref xen_chk_extra_mem(unsigned long pfn)
 {
        int i;
-       phys_addr_t addr = PFN_PHYS(pfn);
 
        for (i = 0; i < XEN_EXTRA_MEM_MAX_REGIONS; i++) {
-               if (addr >= xen_extra_mem[i].start &&
-                   addr < xen_extra_mem[i].start + xen_extra_mem[i].size)
+               if (pfn >= xen_extra_mem[i].start_pfn &&
+                   pfn < xen_extra_mem[i].start_pfn + xen_extra_mem[i].n_pfns)
                        return INVALID_P2M_ENTRY;
        }
 
@@ -155,10 +182,10 @@ void __init xen_inv_extra_mem(void)
        int i;
 
        for (i = 0; i < XEN_EXTRA_MEM_MAX_REGIONS; i++) {
-               if (!xen_extra_mem[i].size)
+               if (!xen_extra_mem[i].n_pfns)
                        continue;
-               pfn_s = PFN_DOWN(xen_extra_mem[i].start);
-               pfn_e = PFN_UP(xen_extra_mem[i].start + xen_extra_mem[i].size);
+               pfn_s = xen_extra_mem[i].start_pfn;
+               pfn_e = pfn_s + xen_extra_mem[i].n_pfns;
                for (pfn = pfn_s; pfn < pfn_e; pfn++)
                        set_phys_to_machine(pfn, INVALID_P2M_ENTRY);
        }
@@ -486,7 +513,7 @@ void __init xen_remap_memory(void)
                } else if (pfn_s + len == xen_remap_buf.target_pfn) {
                        len += xen_remap_buf.size;
                } else {
-                       xen_del_extra_mem(PFN_PHYS(pfn_s), PFN_PHYS(len));
+                       xen_del_extra_mem(pfn_s, len);
                        pfn_s = xen_remap_buf.target_pfn;
                        len = xen_remap_buf.size;
                }
@@ -496,19 +523,36 @@ void __init xen_remap_memory(void)
        }
 
        if (pfn_s != ~0UL && len)
-               xen_del_extra_mem(PFN_PHYS(pfn_s), PFN_PHYS(len));
+               xen_del_extra_mem(pfn_s, len);
 
        set_pte_mfn(buf, mfn_save, PAGE_KERNEL);
 
        pr_info("Remapped %ld page(s)\n", remapped);
 }
 
+static unsigned long __init xen_get_pages_limit(void)
+{
+       unsigned long limit;
+
+#ifdef CONFIG_X86_32
+       limit = GB(64) / PAGE_SIZE;
+#else
+       limit = MAXMEM / PAGE_SIZE;
+       if (!xen_initial_domain() && xen_512gb_limit)
+               limit = GB(512) / PAGE_SIZE;
+#endif
+       return limit;
+}
+
 static unsigned long __init xen_get_max_pages(void)
 {
-       unsigned long max_pages = MAX_DOMAIN_PAGES;
+       unsigned long max_pages, limit;
        domid_t domid = DOMID_SELF;
        int ret;
 
+       limit = xen_get_pages_limit();
+       max_pages = limit;
+
        /*
         * For the initial domain we use the maximum reservation as
         * the maximum page.
@@ -524,7 +568,7 @@ static unsigned long __init xen_get_max_pages(void)
                        max_pages = ret;
        }
 
-       return min(max_pages, MAX_DOMAIN_PAGES);
+       return min(max_pages, limit);
 }
 
 static void __init xen_align_and_add_e820_region(phys_addr_t start,
@@ -555,20 +599,27 @@ static void __init xen_ignore_unusable(void)
 static unsigned long __init xen_count_remap_pages(unsigned long max_pfn)
 {
        unsigned long extra = 0;
+       unsigned long start_pfn, end_pfn;
        const struct e820entry *entry = xen_e820_map;
        int i;
 
+       end_pfn = 0;
        for (i = 0; i < xen_e820_map_entries; i++, entry++) {
-               unsigned long start_pfn = PFN_DOWN(entry->addr);
-               unsigned long end_pfn = PFN_UP(entry->addr + entry->size);
+               start_pfn = PFN_DOWN(entry->addr);
+               /* Adjacent regions on non-page boundaries handling! */
+               end_pfn = min(end_pfn, start_pfn);
 
                if (start_pfn >= max_pfn)
-                       break;
-               if (entry->type == E820_RAM)
-                       continue;
-               if (end_pfn >= max_pfn)
-                       end_pfn = max_pfn;
-               extra += end_pfn - start_pfn;
+                       return extra + max_pfn - end_pfn;
+
+               /* Add any holes in map to result. */
+               extra += start_pfn - end_pfn;
+
+               end_pfn = PFN_UP(entry->addr + entry->size);
+               end_pfn = min(end_pfn, max_pfn);
+
+               if (entry->type != E820_RAM)
+                       extra += end_pfn - start_pfn;
        }
 
        return extra;
@@ -663,37 +714,35 @@ static void __init xen_phys_memcpy(phys_addr_t dest, phys_addr_t src,
 
 /*
  * Reserve Xen mfn_list.
- * See comment above "struct start_info" in <xen/interface/xen.h>
- * We tried to make the the memblock_reserve more selective so
- * that it would be clear what region is reserved. Sadly we ran
- * in the problem wherein on a 64-bit hypervisor with a 32-bit
- * initial domain, the pt_base has the cr3 value which is not
- * neccessarily where the pagetable starts! As Jan put it: "
- * Actually, the adjustment turns out to be correct: The page
- * tables for a 32-on-64 dom0 get allocated in the order "first L1",
- * "first L2", "first L3", so the offset to the page table base is
- * indeed 2. When reading xen/include/public/xen.h's comment
- * very strictly, this is not a violation (since there nothing is said
- * that the first thing in the page table space is pointed to by
- * pt_base; I admit that this seems to be implied though, namely
- * do I think that it is implied that the page table space is the
- * range [pt_base, pt_base + nt_pt_frames), whereas that
- * range here indeed is [pt_base - 2, pt_base - 2 + nt_pt_frames),
- * which - without a priori knowledge - the kernel would have
- * difficulty to figure out)." - so lets just fall back to the
- * easy way and reserve the whole region.
  */
 static void __init xen_reserve_xen_mfnlist(void)
 {
+       phys_addr_t start, size;
+
        if (xen_start_info->mfn_list >= __START_KERNEL_map) {
-               memblock_reserve(__pa(xen_start_info->mfn_list),
-                                xen_start_info->pt_base -
-                                xen_start_info->mfn_list);
+               start = __pa(xen_start_info->mfn_list);
+               size = PFN_ALIGN(xen_start_info->nr_pages *
+                                sizeof(unsigned long));
+       } else {
+               start = PFN_PHYS(xen_start_info->first_p2m_pfn);
+               size = PFN_PHYS(xen_start_info->nr_p2m_frames);
+       }
+
+       if (!xen_is_e820_reserved(start, size)) {
+               memblock_reserve(start, size);
                return;
        }
 
-       memblock_reserve(PFN_PHYS(xen_start_info->first_p2m_pfn),
-                        PFN_PHYS(xen_start_info->nr_p2m_frames));
+#ifdef CONFIG_X86_32
+       /*
+        * Relocating the p2m on 32 bit system to an arbitrary virtual address
+        * is not supported, so just give up.
+        */
+       xen_raw_console_write("Xen hypervisor allocated p2m list conflicts with E820 map\n");
+       BUG();
+#else
+       xen_relocate_p2m();
+#endif
 }
 
 /**
@@ -701,7 +750,7 @@ static void __init xen_reserve_xen_mfnlist(void)
  **/
 char * __init xen_memory_setup(void)
 {
-       unsigned long max_pfn = xen_start_info->nr_pages;
+       unsigned long max_pfn, pfn_s, n_pfns;
        phys_addr_t mem_end, addr, size, chunk_size;
        u32 type;
        int rc;
@@ -711,7 +760,9 @@ char * __init xen_memory_setup(void)
        int i;
        int op;
 
-       max_pfn = min(MAX_DOMAIN_PAGES, max_pfn);
+       xen_parse_512gb();
+       max_pfn = xen_get_pages_limit();
+       max_pfn = min(max_pfn, xen_start_info->nr_pages);
        mem_end = PFN_PHYS(max_pfn);
 
        memmap.nr_entries = E820MAX;
@@ -751,11 +802,12 @@ char * __init xen_memory_setup(void)
                          &xen_e820_map_entries);
 
        max_pages = xen_get_max_pages();
-       if (max_pages > max_pfn)
-               extra_pages += max_pages - max_pfn;
 
        /* How many extra pages do we need due to remapping? */
-       extra_pages += xen_count_remap_pages(max_pfn);
+       max_pages += xen_count_remap_pages(max_pfn);
+
+       if (max_pages > max_pfn)
+               extra_pages += max_pages - max_pfn;
 
        /*
         * Clamp the amount of extra memory to a EXTRA_MEM_RATIO
@@ -764,12 +816,15 @@ char * __init xen_memory_setup(void)
         * is limited to the max size of lowmem, so that it doesn't
         * get completely filled.
         *
+        * Make sure we have no memory above max_pages, as this area
+        * isn't handled by the p2m management.
+        *
         * In principle there could be a problem in lowmem systems if
         * the initial memory is also very large with respect to
         * lowmem, but we won't try to deal with that here.
         */
-       extra_pages = min(EXTRA_MEM_RATIO * min(max_pfn, PFN_DOWN(MAXMEM)),
-                         extra_pages);
+       extra_pages = min3(EXTRA_MEM_RATIO * min(max_pfn, PFN_DOWN(MAXMEM)),
+                          extra_pages, max_pages - max_pfn);
        i = 0;
        addr = xen_e820_map[0].addr;
        size = xen_e820_map[0].size;
@@ -782,9 +837,11 @@ char * __init xen_memory_setup(void)
                                chunk_size = min(size, mem_end - addr);
                        } else if (extra_pages) {
                                chunk_size = min(size, PFN_PHYS(extra_pages));
-                               extra_pages -= PFN_DOWN(chunk_size);
-                               xen_add_extra_mem(addr, chunk_size);
-                               xen_max_p2m_pfn = PFN_DOWN(addr + chunk_size);
+                               pfn_s = PFN_UP(addr);
+                               n_pfns = PFN_DOWN(addr + chunk_size) - pfn_s;
+                               extra_pages -= n_pfns;
+                               xen_add_extra_mem(pfn_s, n_pfns);
+                               xen_max_p2m_pfn = pfn_s + n_pfns;
                        } else
                                type = E820_UNUSABLE;
                }
@@ -805,9 +862,6 @@ char * __init xen_memory_setup(void)
        /*
         * Set the rest as identity mapped, in case PCI BARs are
         * located here.
-        *
-        * PFNs above MAX_P2M_PFN are considered identity mapped as
-        * well.
         */
        set_phys_range_identity(addr / PAGE_SIZE, ~0ul);
 
@@ -895,7 +949,10 @@ char * __init xen_auto_xlated_memory_setup(void)
                e820_add_region(xen_e820_map[i].addr, xen_e820_map[i].size,
                                xen_e820_map[i].type);
 
-       xen_reserve_xen_mfnlist();
+       /* Remove p2m info, it is not needed. */
+       xen_start_info->mfn_list = 0;
+       xen_start_info->first_p2m_pfn = 0;
+       xen_start_info->nr_p2m_frames = 0;
 
        return "Xen";
 }