xen: find unused contiguous memory area
[cascardo/linux.git] / arch / x86 / xen / setup.c
index adad417..973d294 100644 (file)
@@ -38,6 +38,10 @@ struct xen_memory_region xen_extra_mem[XEN_EXTRA_MEM_MAX_REGIONS] __initdata;
 /* Number of pages released from the initial allocation. */
 unsigned long xen_released_pages;
 
+/* E820 map used during setting up memory. */
+static struct e820entry xen_e820_map[E820MAX] __initdata;
+static u32 xen_e820_map_entries __initdata;
+
 /*
  * Buffer used to remap identity mapped pages. We only need the virtual space.
  * The physical page behind this address is remapped as needed to different
@@ -164,15 +168,13 @@ void __init xen_inv_extra_mem(void)
  * This function updates min_pfn with the pfn found and returns
  * the size of that range or zero if not found.
  */
-static unsigned long __init xen_find_pfn_range(
-       const struct e820entry *list, size_t map_size,
-       unsigned long *min_pfn)
+static unsigned long __init xen_find_pfn_range(unsigned long *min_pfn)
 {
-       const struct e820entry *entry;
+       const struct e820entry *entry = xen_e820_map;
        unsigned int i;
        unsigned long done = 0;
 
-       for (i = 0, entry = list; i < map_size; i++, entry++) {
+       for (i = 0; i < xen_e820_map_entries; i++, entry++) {
                unsigned long s_pfn;
                unsigned long e_pfn;
 
@@ -221,7 +223,7 @@ static int __init xen_free_mfn(unsigned long mfn)
  * as a fallback if the remapping fails.
  */
 static void __init xen_set_identity_and_release_chunk(unsigned long start_pfn,
-       unsigned long end_pfn, unsigned long nr_pages, unsigned long *released)
+                       unsigned long end_pfn, unsigned long nr_pages)
 {
        unsigned long pfn, end;
        int ret;
@@ -241,7 +243,7 @@ static void __init xen_set_identity_and_release_chunk(unsigned long start_pfn,
                WARN(ret != 1, "Failed to release pfn %lx err=%d\n", pfn, ret);
 
                if (ret == 1) {
-                       (*released)++;
+                       xen_released_pages++;
                        if (!__set_phys_to_machine(pfn, INVALID_P2M_ENTRY))
                                break;
                } else
@@ -356,9 +358,8 @@ static void __init xen_do_set_identity_and_remap_chunk(
  * to Xen and not remapped.
  */
 static unsigned long __init xen_set_identity_and_remap_chunk(
-        const struct e820entry *list, size_t map_size, unsigned long start_pfn,
-       unsigned long end_pfn, unsigned long nr_pages, unsigned long remap_pfn,
-       unsigned long *released, unsigned long *remapped)
+       unsigned long start_pfn, unsigned long end_pfn, unsigned long nr_pages,
+       unsigned long remap_pfn)
 {
        unsigned long pfn;
        unsigned long i = 0;
@@ -379,12 +380,11 @@ static unsigned long __init xen_set_identity_and_remap_chunk(
                if (cur_pfn + size > nr_pages)
                        size = nr_pages - cur_pfn;
 
-               remap_range_size = xen_find_pfn_range(list, map_size,
-                                                     &remap_pfn);
+               remap_range_size = xen_find_pfn_range(&remap_pfn);
                if (!remap_range_size) {
                        pr_warning("Unable to find available pfn range, not remapping identity pages\n");
                        xen_set_identity_and_release_chunk(cur_pfn,
-                               cur_pfn + left, nr_pages, released);
+                                               cur_pfn + left, nr_pages);
                        break;
                }
                /* Adjust size to fit in current e820 RAM region */
@@ -396,7 +396,6 @@ static unsigned long __init xen_set_identity_and_remap_chunk(
                /* Update variables to reflect new mappings. */
                i += size;
                remap_pfn += size;
-               *remapped += size;
        }
 
        /*
@@ -411,15 +410,11 @@ static unsigned long __init xen_set_identity_and_remap_chunk(
        return remap_pfn;
 }
 
-static void __init xen_set_identity_and_remap(
-       const struct e820entry *list, size_t map_size, unsigned long nr_pages,
-       unsigned long *released, unsigned long *remapped)
+static void __init xen_set_identity_and_remap(unsigned long nr_pages)
 {
        phys_addr_t start = 0;
        unsigned long last_pfn = nr_pages;
-       const struct e820entry *entry;
-       unsigned long num_released = 0;
-       unsigned long num_remapped = 0;
+       const struct e820entry *entry = xen_e820_map;
        int i;
 
        /*
@@ -433,9 +428,9 @@ static void __init xen_set_identity_and_remap(
         * example) the DMI tables in a reserved region that begins on
         * a non-page boundary.
         */
-       for (i = 0, entry = list; i < map_size; i++, entry++) {
+       for (i = 0; i < xen_e820_map_entries; i++, entry++) {
                phys_addr_t end = entry->addr + entry->size;
-               if (entry->type == E820_RAM || i == map_size - 1) {
+               if (entry->type == E820_RAM || i == xen_e820_map_entries - 1) {
                        unsigned long start_pfn = PFN_DOWN(start);
                        unsigned long end_pfn = PFN_UP(end);
 
@@ -444,17 +439,13 @@ static void __init xen_set_identity_and_remap(
 
                        if (start_pfn < end_pfn)
                                last_pfn = xen_set_identity_and_remap_chunk(
-                                               list, map_size, start_pfn,
-                                               end_pfn, nr_pages, last_pfn,
-                                               &num_released, &num_remapped);
+                                               start_pfn, end_pfn, nr_pages,
+                                               last_pfn);
                        start = end;
                }
        }
 
-       *released = num_released;
-       *remapped = num_remapped;
-
-       pr_info("Released %ld page(s)\n", num_released);
+       pr_info("Released %ld page(s)\n", xen_released_pages);
 }
 
 /*
@@ -549,17 +540,96 @@ static void __init xen_align_and_add_e820_region(phys_addr_t start,
        e820_add_region(start, end - start, type);
 }
 
-static void __init xen_ignore_unusable(struct e820entry *list, size_t map_size)
+static void __init xen_ignore_unusable(void)
 {
-       struct e820entry *entry;
+       struct e820entry *entry = xen_e820_map;
        unsigned int i;
 
-       for (i = 0, entry = list; i < map_size; i++, entry++) {
+       for (i = 0; i < xen_e820_map_entries; i++, entry++) {
                if (entry->type == E820_UNUSABLE)
                        entry->type = E820_RAM;
        }
 }
 
+static unsigned long __init xen_count_remap_pages(unsigned long max_pfn)
+{
+       unsigned long extra = 0;
+       const struct e820entry *entry = xen_e820_map;
+       int i;
+
+       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);
+
+               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;
+}
+
+bool __init xen_is_e820_reserved(phys_addr_t start, phys_addr_t size)
+{
+       struct e820entry *entry;
+       unsigned mapcnt;
+       phys_addr_t end;
+
+       if (!size)
+               return false;
+
+       end = start + size;
+       entry = xen_e820_map;
+
+       for (mapcnt = 0; mapcnt < xen_e820_map_entries; mapcnt++) {
+               if (entry->type == E820_RAM && entry->addr <= start &&
+                   (entry->addr + entry->size) >= end)
+                       return false;
+
+               entry++;
+       }
+
+       return true;
+}
+
+/*
+ * Find a free area in physical memory not yet reserved and compliant with
+ * E820 map.
+ * Used to relocate pre-allocated areas like initrd or p2m list which are in
+ * conflict with the to be used E820 map.
+ * In case no area is found, return 0. Otherwise return the physical address
+ * of the area which is already reserved for convenience.
+ */
+phys_addr_t __init xen_find_free_area(phys_addr_t size)
+{
+       unsigned mapcnt;
+       phys_addr_t addr, start;
+       struct e820entry *entry = xen_e820_map;
+
+       for (mapcnt = 0; mapcnt < xen_e820_map_entries; mapcnt++, entry++) {
+               if (entry->type != E820_RAM || entry->size < size)
+                       continue;
+               start = entry->addr;
+               for (addr = start; addr < start + size; addr += PAGE_SIZE) {
+                       if (!memblock_is_reserved(addr))
+                               continue;
+                       start = addr + PAGE_SIZE;
+                       if (start + size > entry->addr + entry->size)
+                               break;
+               }
+               if (addr >= start + size) {
+                       memblock_reserve(start, size);
+                       return start;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Reserve Xen mfn_list.
  * See comment above "struct start_info" in <xen/interface/xen.h>
@@ -600,15 +670,13 @@ static void __init xen_reserve_xen_mfnlist(void)
  **/
 char * __init xen_memory_setup(void)
 {
-       static struct e820entry map[E820MAX] __initdata;
-
        unsigned long max_pfn = xen_start_info->nr_pages;
-       phys_addr_t mem_end;
+       phys_addr_t mem_end, addr, size, chunk_size;
+       u32 type;
        int rc;
        struct xen_memory_map memmap;
        unsigned long max_pages;
        unsigned long extra_pages = 0;
-       unsigned long remapped_pages;
        int i;
        int op;
 
@@ -616,7 +684,7 @@ char * __init xen_memory_setup(void)
        mem_end = PFN_PHYS(max_pfn);
 
        memmap.nr_entries = E820MAX;
-       set_xen_guest_handle(memmap.buffer, map);
+       set_xen_guest_handle(memmap.buffer, xen_e820_map);
 
        op = xen_initial_domain() ?
                XENMEM_machine_memory_map :
@@ -625,15 +693,16 @@ char * __init xen_memory_setup(void)
        if (rc == -ENOSYS) {
                BUG_ON(xen_initial_domain());
                memmap.nr_entries = 1;
-               map[0].addr = 0ULL;
-               map[0].size = mem_end;
+               xen_e820_map[0].addr = 0ULL;
+               xen_e820_map[0].size = mem_end;
                /* 8MB slack (to balance backend allocations). */
-               map[0].size += 8ULL << 20;
-               map[0].type = E820_RAM;
+               xen_e820_map[0].size += 8ULL << 20;
+               xen_e820_map[0].type = E820_RAM;
                rc = 0;
        }
        BUG_ON(rc);
        BUG_ON(memmap.nr_entries == 0);
+       xen_e820_map_entries = memmap.nr_entries;
 
        /*
         * Xen won't allow a 1:1 mapping to be created to UNUSABLE
@@ -644,24 +713,18 @@ char * __init xen_memory_setup(void)
         * a patch in the future.
         */
        if (xen_initial_domain())
-               xen_ignore_unusable(map, memmap.nr_entries);
+               xen_ignore_unusable();
 
        /* Make sure the Xen-supplied memory map is well-ordered. */
-       sanitize_e820_map(map, memmap.nr_entries, &memmap.nr_entries);
+       sanitize_e820_map(xen_e820_map, xen_e820_map_entries,
+                         &xen_e820_map_entries);
 
        max_pages = xen_get_max_pages();
        if (max_pages > max_pfn)
                extra_pages += max_pages - max_pfn;
 
-       /*
-        * Set identity map on non-RAM pages and prepare remapping the
-        * underlying RAM.
-        */
-       xen_set_identity_and_remap(map, memmap.nr_entries, max_pfn,
-                                  &xen_released_pages, &remapped_pages);
-
-       extra_pages += xen_released_pages;
-       extra_pages += remapped_pages;
+       /* How many extra pages do we need due to remapping? */
+       extra_pages += xen_count_remap_pages(max_pfn);
 
        /*
         * Clamp the amount of extra memory to a EXTRA_MEM_RATIO
@@ -677,29 +740,35 @@ char * __init xen_memory_setup(void)
        extra_pages = min(EXTRA_MEM_RATIO * min(max_pfn, PFN_DOWN(MAXMEM)),
                          extra_pages);
        i = 0;
-       while (i < memmap.nr_entries) {
-               phys_addr_t addr = map[i].addr;
-               phys_addr_t size = map[i].size;
-               u32 type = map[i].type;
+       addr = xen_e820_map[0].addr;
+       size = xen_e820_map[0].size;
+       while (i < xen_e820_map_entries) {
+               chunk_size = size;
+               type = xen_e820_map[i].type;
 
                if (type == E820_RAM) {
                        if (addr < mem_end) {
-                               size = min(size, mem_end - addr);
+                               chunk_size = min(size, mem_end - addr);
                        } else if (extra_pages) {
-                               size = min(size, PFN_PHYS(extra_pages));
-                               extra_pages -= PFN_DOWN(size);
-                               xen_add_extra_mem(addr, size);
-                               xen_max_p2m_pfn = PFN_DOWN(addr + size);
+                               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);
                        } else
                                type = E820_UNUSABLE;
                }
 
-               xen_align_and_add_e820_region(addr, size, type);
+               xen_align_and_add_e820_region(addr, chunk_size, type);
 
-               map[i].addr += size;
-               map[i].size -= size;
-               if (map[i].size == 0)
+               addr += chunk_size;
+               size -= chunk_size;
+               if (size == 0) {
                        i++;
+                       if (i < xen_e820_map_entries) {
+                               addr = xen_e820_map[i].addr;
+                               size = xen_e820_map[i].size;
+                       }
+               }
        }
 
        /*
@@ -709,7 +778,7 @@ char * __init xen_memory_setup(void)
         * PFNs above MAX_P2M_PFN are considered identity mapped as
         * well.
         */
-       set_phys_range_identity(map[i-1].addr / PAGE_SIZE, ~0ul);
+       set_phys_range_identity(addr / PAGE_SIZE, ~0ul);
 
        /*
         * In domU, the ISA region is normal, usable memory, but we
@@ -723,6 +792,12 @@ char * __init xen_memory_setup(void)
 
        xen_reserve_xen_mfnlist();
 
+       /*
+        * Set identity map on non-RAM pages and prepare remapping the
+        * underlying RAM.
+        */
+       xen_set_identity_and_remap(max_pfn);
+
        return "Xen";
 }
 
@@ -731,23 +806,25 @@ char * __init xen_memory_setup(void)
  */
 char * __init xen_auto_xlated_memory_setup(void)
 {
-       static struct e820entry map[E820MAX] __initdata;
-
        struct xen_memory_map memmap;
        int i;
        int rc;
 
        memmap.nr_entries = E820MAX;
-       set_xen_guest_handle(memmap.buffer, map);
+       set_xen_guest_handle(memmap.buffer, xen_e820_map);
 
        rc = HYPERVISOR_memory_op(XENMEM_memory_map, &memmap);
        if (rc < 0)
                panic("No memory map (%d)\n", rc);
 
-       sanitize_e820_map(map, ARRAY_SIZE(map), &memmap.nr_entries);
+       xen_e820_map_entries = memmap.nr_entries;
+
+       sanitize_e820_map(xen_e820_map, ARRAY_SIZE(xen_e820_map),
+                         &xen_e820_map_entries);
 
-       for (i = 0; i < memmap.nr_entries; i++)
-               e820_add_region(map[i].addr, map[i].size, map[i].type);
+       for (i = 0; i < xen_e820_map_entries; i++)
+               e820_add_region(xen_e820_map[i].addr, xen_e820_map[i].size,
+                               xen_e820_map[i].type);
 
        xen_reserve_xen_mfnlist();