From: jbeulich@novell.com
Subject: fix issues with the assignment of huge amounts of memory
Patch-mainline: obsolete
References: bnc#482614, bnc#537435

--- head-2010-04-15.orig/arch/x86/kernel/e820-xen.c	2010-04-15 10:48:32.000000000 +0200
+++ head-2010-04-15/arch/x86/kernel/e820-xen.c	2010-04-15 11:48:01.000000000 +0200
@@ -1093,6 +1093,26 @@ static int __init parse_memopt(char *p)
 
 	userdef = 1;
 	mem_size = memparse(p, &p);
+#ifdef CONFIG_XEN
+	/*
+	 * A little less than 2% of available memory are needed for page
+	 * tables, p2m map, and mem_map. Hence the maximum amount of memory
+	 * we can potentially balloon up to can in no case exceed about 50
+	 * times of what we've been given initially. Since even with that we
+	 * won't be able to boot (due to various calculations done based on
+	 * the total number of pages) we further restrict this to factor 32.
+	 */
+	if ((mem_size >> (PAGE_SHIFT + 5)) > xen_start_info->nr_pages) {
+		u64 size = (u64)xen_start_info->nr_pages << 5;
+
+		printk(KERN_WARNING "mem=%Luk is invalid for an initial"
+				    " allocation of %luk, using %Luk\n",
+		       (unsigned long long)mem_size >> 10,
+		       xen_start_info->nr_pages << (PAGE_SHIFT - 10),
+		       (unsigned long long)size << (PAGE_SHIFT - 10));
+		mem_size = size << PAGE_SHIFT;
+	}
+#endif
 	e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1);
 
 	i = e820.nr_map - 1;
@@ -1291,6 +1311,7 @@ void __init e820_reserve_resources_late(
 char *__init default_machine_specific_memory_setup(void)
 {
 	int rc, nr_map;
+	unsigned long long maxmem;
 	struct xen_memory_map memmap;
 	static struct e820entry __initdata map[E820MAX];
 
@@ -1316,6 +1337,22 @@ char *__init default_machine_specific_me
 		BUG();
 
 #ifdef CONFIG_XEN
+	/* See the comment in parse_memopt(). */
+	for (maxmem = rc = 0; rc < e820.nr_map; ++rc)
+		if (e820.map[rc].type == E820_RAM)
+			maxmem += e820.map[rc].size;
+	if ((maxmem >> (PAGE_SHIFT + 5)) > xen_start_info->nr_pages) {
+		unsigned long long size = (u64)xen_start_info->nr_pages << 5;
+
+		printk(KERN_WARNING "maxmem of %LuM is invalid for an initial"
+				    " allocation of %luM, using %LuM\n",
+		       maxmem >> 20,
+		       xen_start_info->nr_pages >> (20 - PAGE_SHIFT),
+		       size >> (20 - PAGE_SHIFT));
+		size <<= PAGE_SHIFT;
+		e820_remove_range(size, ULLONG_MAX - size, E820_RAM, 1);
+	}
+
 	if (is_initial_xendomain()) {
 		memmap.nr_entries = E820MAX;
 		set_xen_guest_handle(memmap.buffer, machine_e820.map);
--- head-2010-04-15.orig/arch/x86/kernel/setup-xen.c	2010-04-15 11:46:02.000000000 +0200
+++ head-2010-04-15/arch/x86/kernel/setup-xen.c	2010-04-15 11:48:03.000000000 +0200
@@ -130,12 +130,7 @@ static struct notifier_block xen_panic_b
 unsigned long *phys_to_machine_mapping;
 EXPORT_SYMBOL(phys_to_machine_mapping);
 
-unsigned long *pfn_to_mfn_frame_list_list,
-#ifdef CONFIG_X86_64
-	*pfn_to_mfn_frame_list[512];
-#else
-	*pfn_to_mfn_frame_list[128];
-#endif
+unsigned long *pfn_to_mfn_frame_list_list, **pfn_to_mfn_frame_list;
 
 /* Raw start-of-day parameters from the hypervisor. */
 start_info_t *xen_start_info;
@@ -1167,17 +1162,17 @@ void __init setup_arch(char **cmdline_p)
 		p2m_pages = xen_start_info->nr_pages;
 
 	if (!xen_feature(XENFEAT_auto_translated_physmap)) {
-		unsigned long i, j;
+		unsigned long i, j, size;
 		unsigned int k, fpp;
 
 		/* Make sure we have a large enough P->M table. */
 		phys_to_machine_mapping = alloc_bootmem_pages(
 			max_pfn * sizeof(unsigned long));
-		memset(phys_to_machine_mapping, ~0,
-		       max_pfn * sizeof(unsigned long));
 		memcpy(phys_to_machine_mapping,
 		       (unsigned long *)xen_start_info->mfn_list,
 		       p2m_pages * sizeof(unsigned long));
+		memset(phys_to_machine_mapping + p2m_pages, ~0,
+		       (max_pfn - p2m_pages) * sizeof(unsigned long));
 		free_bootmem(
 			__pa(xen_start_info->mfn_list),
 			PFN_PHYS(PFN_UP(xen_start_info->nr_pages *
@@ -1187,15 +1182,26 @@ void __init setup_arch(char **cmdline_p)
 		 * Initialise the list of the frames that specify the list of
 		 * frames that make up the p2m table. Used by save/restore.
 		 */
-		pfn_to_mfn_frame_list_list = alloc_bootmem_pages(PAGE_SIZE);
-
 		fpp = PAGE_SIZE/sizeof(unsigned long);
+		size = (max_pfn + fpp - 1) / fpp;
+		size = (size + fpp - 1) / fpp;
+		++size; /* include a zero terminator for crash tools */
+		size *= sizeof(unsigned long);
+		pfn_to_mfn_frame_list_list = alloc_bootmem_pages(size);
+		if (size > PAGE_SIZE
+		    && xen_create_contiguous_region((unsigned long)
+						    pfn_to_mfn_frame_list_list,
+						    get_order(size), 0))
+			BUG();
+		size -= sizeof(unsigned long);
+		pfn_to_mfn_frame_list = alloc_bootmem(size);
+
 		for (i = j = 0, k = -1; i < max_pfn; i += fpp, j++) {
 			if (j == fpp)
 				j = 0;
 			if (j == 0) {
 				k++;
-				BUG_ON(k>=ARRAY_SIZE(pfn_to_mfn_frame_list));
+				BUG_ON(k * sizeof(unsigned long) >= size);
 				pfn_to_mfn_frame_list[k] =
 					alloc_bootmem_pages(PAGE_SIZE);
 				pfn_to_mfn_frame_list_list[k] =
--- head-2010-04-15.orig/drivers/xen/core/machine_reboot.c	2010-03-25 14:39:15.000000000 +0100
+++ head-2010-04-15/drivers/xen/core/machine_reboot.c	2010-03-25 14:41:06.000000000 +0100
@@ -80,7 +80,7 @@ static void post_suspend(int suspend_can
 	unsigned long shinfo_mfn;
 	extern unsigned long max_pfn;
 	extern unsigned long *pfn_to_mfn_frame_list_list;
-	extern unsigned long *pfn_to_mfn_frame_list[];
+	extern unsigned long **pfn_to_mfn_frame_list;
 
 	if (suspend_cancelled) {
 		xen_start_info->store_mfn =
