Skip to contentSkip to conversation

ppc64: Add mem=X boot command line option

PATCH New Michael Ellerman 1 participant 1 message Mar 29, 2005

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ppc64: Add mem=X boot command line option

Hi Andrew,

This patch adds the mem=X boot command line option for PPC64.

On iSeries the user's mem=X value is aligned to PAGE_SIZE, on pSeries we align to 16 MB which is the size of a large page.

The iSeries implementation is fairly straight forward, we declare mem=X as an early_param() and then in iSeries_init_early() we modify the systemcfg->physicalMemorySize based on that value.

On pSeries the mem=X option is parsed in prom_init.c before the kernel proper starts, and is used to modify prom_init_mem()'s idea of memory. The mem=X value and computed tce_alloc_start/end values are saved by prom_init() into the device tree for later use by the kernel.

The device tree properties are read by the kernel in early_dt_scan_chosen(), and used to modify the lmb structure in early_init_devtree(). That's the guts of it.

On non-LPAR machines the tce_alloc_start/end values are read from the device tree and used in htab_initialize() to make sure the TCE table is mapped at the real top of RAM.

If NUMA is enabled we also have to make changes in parse_numa_properties() and do_init_bootmem() to exclude memory regions above the memory limit, and truncate any region which stradles the limit.

NB. This patch does not facilitate using mem=X to give drivers access to large regions of contiguous memory.

---

This is almost identical to the patch I posted last week, with one minor change to make boot messages look the same on iSeries & pSeries, and a few little white space and comment tweaks.

It has been tested on iSeries, pSeries, pSeries LPAR, G5 and Maple.

Thanks to BenH, Anton, Olof, Stephen, Mike & Maneesh for their help.

cheers!

 arch/ppc64/kernel/iSeries_setup.c |   40 +++++++----
 arch/ppc64/kernel/lmb.c           |   26 +++++++
 arch/ppc64/kernel/prom.c          |   15 ++++
 arch/ppc64/kernel/prom_init.c     |  137 +++++++++++++++++++++++++++++++++++---
 arch/ppc64/kernel/setup.c         |   20 ++++-
 arch/ppc64/mm/hash_utils.c        |   23 +++++-
 arch/ppc64/mm/numa.c              |   46 +++++++++++-
 include/asm-ppc64/lmb.h           |    1 
 8 files changed, 276 insertions(+), 32 deletions(-)
arch/ppc64/kernel/setup.c +15 -5
Index: bk-current/arch/ppc64/kernel/setup.c
===================================================================
@@ -636,12 +636,11 @@ void __init setup_system(void)
 	early_console_initialized = 1;
 	register_console(&udbg_console);
 
-#endif /* !CONFIG_PPC_ISERIES */
-
 	/* Save unparsed command line copy for /proc/cmdline */
 	strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE);
 
 	parse_early_param();
+#endif /* !CONFIG_PPC_ISERIES */
 
 #if defined(CONFIG_SMP) && !defined(CONFIG_PPC_ISERIES)
 	/*
@@ -800,20 +799,31 @@ struct seq_operations cpuinfo_op = {
 	.show =	show_cpuinfo,
 };
 
-#if 0 /* XXX not currently used */
+/*
+ * These three variables are used to save values passed to us by prom_init()
+ * via the device tree. The TCE variables are needed because with a memory_limit
+ * in force we may need to explicitly map the TCE are at the top of RAM.
+ */
 unsigned long memory_limit;
+unsigned long tce_alloc_start;
+unsigned long tce_alloc_end;
 
+#ifdef CONFIG_PPC_ISERIES
+/* 
+ * On iSeries we just parse the mem=X option from the command line.
+ * On pSeries it's a bit more complicated, see prom_init_mem()
+ */
 static int __init early_parsemem(char *p)
 {
 	if (!p)
 		return 0;
 
-	memory_limit = memparse(p, &p);
+	memory_limit = ALIGN(memparse(p, &p), PAGE_SIZE);
 
 	return 0;
 }
 early_param("mem", early_parsemem);
-#endif
+#endif /* CONFIG_PPC_ISERIES */
 
 #ifdef CONFIG_PPC_MULTIPLATFORM
 static int __init set_preferred_console(void)
arch/ppc64/kernel/lmb.c +26 -0
Index: bk-current/arch/ppc64/kernel/lmb.c
===================================================================
@@ -344,3 +344,29 @@ lmb_abs_to_phys(unsigned long aa)
 
 	return pa;
 }
+
+/*
+ * Truncate the lmb list to memory_limit if it's set
+ * You must call lmb_analyze() after this.
+ */
+void __init lmb_enforce_memory_limit(void)
+{
+	extern unsigned long memory_limit;
+	unsigned long i, limit;
+	struct lmb_region *mem = &(lmb.memory);
+
+	if (! memory_limit)
+		return;
+
+	limit = memory_limit;
+	for (i = 0; i < mem->cnt; i++) {
+		if (limit > mem->region[i].size) {
+			limit -= mem->region[i].size;
+			continue;
+		}
+		
+		mem->region[i].size = limit;
+		mem->cnt = i + 1;
+		break;
+	}
+}
include/asm-ppc64/lmb.h +1 -0
Index: bk-current/include/asm-ppc64/lmb.h
===================================================================
@@ -51,6 +51,7 @@ extern unsigned long __init lmb_alloc_ba
 extern unsigned long __init lmb_phys_mem_size(void);
 extern unsigned long __init lmb_end_of_DRAM(void);
 extern unsigned long __init lmb_abs_to_phys(unsigned long);
+extern void __init lmb_enforce_memory_limit(void);
 
 extern void lmb_dump_all(void);
 
arch/ppc64/kernel/iSeries_setup.c +28 -12
Index: bk-current/arch/ppc64/kernel/iSeries_setup.c
===================================================================
@@ -285,7 +285,7 @@ static unsigned long iSeries_process_mai
 	return mem_blocks;
 }
 
-static void __init iSeries_parse_cmdline(void)
+static void __init iSeries_get_cmdline(void)
 {
 	char *p, *q;
 
@@ -305,6 +305,8 @@ static void __init iSeries_parse_cmdline
 
 static void __init iSeries_init_early(void)
 {
+	extern unsigned long memory_limit;
+
 	DBG(" -> iSeries_init_early()\n");
 
 	ppcdbg_initialize();
@@ -352,6 +354,31 @@ static void __init iSeries_init_early(vo
 	 */
 	build_iSeries_Memory_Map();
 
+	iSeries_get_cmdline();
+	
+	/* Save unparsed command line copy for /proc/cmdline */
+	strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE);
+	
+	/* Parse early parameters, in particular mem=x */
+	parse_early_param();
+	
+	if (memory_limit) {
+		if (memory_limit < systemcfg->physicalMemorySize)
+			systemcfg->physicalMemorySize = memory_limit;
+		else {
+			printk("Ignoring mem=%lu >= ram_top.\n", memory_limit);
+			memory_limit = 0;
+		}
+	}
+
+	/* Bolt kernel mappings for all of memory (or just a bit if we've got a limit) */
+	iSeries_bolt_kernel(0, systemcfg->physicalMemorySize);
+	
+	lmb_init();
+	lmb_add(0, systemcfg->physicalMemorySize);
+	lmb_analyze();
+	lmb_reserve(0, __pa(klimit));
+	
 	/* Initialize machine-dependency vectors */
 #ifdef CONFIG_SMP
 	smp_init_iSeries();
@@ -377,9 +404,6 @@ static void __init iSeries_init_early(vo
 		initrd_start = initrd_end = 0;
 #endif /* CONFIG_BLK_DEV_INITRD */
 
-
-	iSeries_parse_cmdline();
-
 	DBG(" <- iSeries_init_early()\n");
 }
 
@@ -540,14 +564,6 @@ static void __init build_iSeries_Memory_
 	 *   nextPhysChunk
 	 */
 	systemcfg->physicalMemorySize = chunk_to_addr(nextPhysChunk);
-
-	/* Bolt kernel mappings for all of memory */
-	iSeries_bolt_kernel(0, systemcfg->physicalMemorySize);
-
-	lmb_init();
-	lmb_add(0, systemcfg->physicalMemorySize);
-	lmb_analyze();	/* ?? */
-	lmb_reserve(0, __pa(klimit));
 }
 
 /*
arch/ppc64/kernel/prom.c +15 -0
Index: bk-current/arch/ppc64/kernel/prom.c
===================================================================
@@ -912,6 +912,8 @@ static int __init early_init_dt_scan_cho
 					    const char *full_path, void *data)
 {
 	u32 *prop;
+	u64 *prop64;
+	extern unsigned long memory_limit, tce_alloc_start, tce_alloc_end;
 
 	if (strcmp(full_path, "/chosen") != 0)
 		return 0;
@@ -928,6 +930,18 @@ static int __init early_init_dt_scan_cho
 	if (get_flat_dt_prop(node, "linux,iommu-force-on", NULL) != NULL)
 		iommu_force_on = 1;
 
+ 	prop64 = (u64*)get_flat_dt_prop(node, "linux,memory-limit", NULL);
+ 	if (prop64)
+ 		memory_limit = *prop64;
+ 		
+ 	prop64 = (u64*)get_flat_dt_prop(node, "linux,tce-alloc-start", NULL);
+ 	if (prop64)
+ 		tce_alloc_start = *prop64;
+ 		
+ 	prop64 = (u64*)get_flat_dt_prop(node, "linux,tce-alloc-end", NULL);
+ 	if (prop64)
+ 		tce_alloc_end = *prop64;
+ 
 #ifdef CONFIG_PPC_RTAS
 	/* To help early debugging via the front panel, we retreive a minimal
 	 * set of RTAS infos now if available
@@ -1067,6 +1081,7 @@ void __init early_init_devtree(void *par
 	lmb_init();
 	scan_flat_dt(early_init_dt_scan_root, NULL);
 	scan_flat_dt(early_init_dt_scan_memory, NULL);
+	lmb_enforce_memory_limit();
 	lmb_analyze();
 	systemcfg->physicalMemorySize = lmb_phys_mem_size();
 	lmb_reserve(0, __pa(klimit));
arch/ppc64/mm/hash_utils.c +21 -2
Index: bk-current/arch/ppc64/mm/hash_utils.c
===================================================================
@@ -149,6 +149,8 @@ void __init htab_initialize(void)
 	unsigned long pteg_count;
 	unsigned long mode_rw;
 	int i, use_largepages = 0;
+	unsigned long base = 0, size = 0;
+	extern unsigned long tce_alloc_start, tce_alloc_end;
 
 	DBG(" -> htab_initialize()\n");
 
@@ -204,8 +206,6 @@ void __init htab_initialize(void)
 
 	/* create bolted the linear mapping in the hash table */
 	for (i=0; i < lmb.memory.cnt; i++) {
-		unsigned long base, size;
-
 		base = lmb.memory.region[i].physbase + KERNELBASE;
 		size = lmb.memory.region[i].size;
 
@@ -234,6 +234,25 @@ void __init htab_initialize(void)
 #endif /* CONFIG_U3_DART */
 		create_pte_mapping(base, base + size, mode_rw, use_largepages);
 	}
+
+	/*
+	 * If we have a memory_limit and we've allocated TCEs then we need to
+	 * explicitly map the TCE area at the top of RAM. We also cope with the
+	 * case that the TCEs start below memory_limit.
+	 * tce_alloc_start/end are 16MB aligned so the mapping should work
+	 * for either 4K or 16MB pages.
+	 */
+	if (tce_alloc_start) {
+		tce_alloc_start += KERNELBASE;
+		tce_alloc_end += KERNELBASE;
+		
+		if (base + size >= tce_alloc_start)
+			tce_alloc_start = base + size + 1;
+		
+		create_pte_mapping(tce_alloc_start, tce_alloc_end,
+			mode_rw, use_largepages);
+	}
+	
 	DBG(" <- htab_initialize()\n");
 }
 #undef KB
arch/ppc64/mm/numa.c +42 -4

70 lines not on this page · raw message

arch/ppc64/kernel/prom_init.c +128 -9

188 lines not on this page · raw message