ppc64: Add mem=X boot command line option
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
@@ -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
@@ -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
@@ -51,6 +51,7 @@ extern unsigned long __init lmb_alloc_baextern 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
@@ -285,7 +285,7 @@ static unsigned long iSeries_process_maireturn 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_cmdlinestatic 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
@@ -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_choif (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 *parlmb_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
@@ -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