Messages in this thread Patch in this message | | | Date | Tue, 21 Feb 2006 14:51:44 +0000 (GMT) | From | Mel Gorman <> | Subject | Re: [PATCH 4/7] ppc64 - Specify amount of kernel memory at boot time |
| |
On Fri, 17 Feb 2006, Dave Hansen wrote:
> On Fri, 2006-02-17 at 14:17 +0000, Mel Gorman wrote: >> This patch adds the kernelcore= parameter for ppc64. >> >> The amount of memory will requested will not be reserved in all nodes. The >> first node that is found that can accomodate the requested amount of memory >> and have remaining more for ZONE_EASYRCLM is used. If a node has memory holes, >> it also will not be used. > > One thing I think we really need to see before these go into mainline is > the ability to shrink the ZONE_EASYRCLM at runtime, and give the memory > back to NORMAL/DMA. > > Otherwise, any system starting off sufficiently small will end up having > lowmem starvation issues. Allowing resizing at least gives the admin a > chance to avoid those issues. > >> + if (core_mem_pfn == 0 || >> + end_pfn - start_pfn < core_mem_pfn || >> + end_pfn - start_pfn != pages_present) { >> + zones_size[ZONE_DMA] = end_pfn - start_pfn; >> + zones_size[ZONE_EASYRCLM] = 0; >> + zholes_size[ZONE_DMA] = >> + zones_size[ZONE_DMA] - pages_present; >> + zholes_size[ZONE_EASYRCLM] = 0; >> + if (core_mem_pfn >= pages_present) >> + core_mem_pfn -= pages_present; >> + } else { >> + zones_size[ZONE_DMA] = core_mem_pfn; >> + zones_size[ZONE_EASYRCLM] = end_pfn - core_mem_pfn; >> + zholes_size[ZONE_DMA] = 0; >> + zholes_size[ZONE_EASYRCLM] = 0; >> + core_mem_pfn = 0; >> + } > > I'm finding this bit of code really hard to parse. > > First of all, please give "core_mem_size" and "core_mem_pfn" some better > names. "core_mem_size" in _what_? Bytes? Pages? g0ats? ;) > > The "pfn" in "core_mem_pfn" is usually used to denote a physical address >>> PAGE_SHIFT. However, yours is actually a _number_ of pages, not an > address, right? Actually, as I look at it closer, it appears to be a > pfn in the else{} and a nr_page in the if{} block. > > core_mem_nr_pages or nr_core_mem_pages might be more appropriate. > > Users will _not_ care about memory holes. They'll just want to specify > a number of pages. I think this: > >> + zones_size[ZONE_DMA] = core_mem_pfn; >> + zones_size[ZONE_EASYRCLM] = end_pfn - core_mem_pfn; > > is probably bogus because it doesn't deal with holes at all. > > Walking those init_node_data() structures in get_region() is probably > pretty darn fast, and we don't need to be careful about how many times > we do it. I think I'd probably separate out the problem a bit. > > 1. make get_region() not care about holes. Have it just return the > range of the node's pages. > 2. make a new function (get_region_holes()??) that, given a pfn range, > walks the init_node_data[] just like get_region() (have them share > code) and return the present_pages in that pfn range. > 3. go back to paging init, and try to properly size ZONE_DMA. Find > holes with your new function, and increase its size proportionately, > set zholes_size[ZONE_DMA] at this time. Make sure the user size is > in nr_page, _NOT_ max_pfns. > 4. give the rest of the space to ZONE_EASYRCLM. Call your new function > to properly size its zone hole(s). > 5. Profit! > > This may all belong broken out in a new function. >
A new release of patches is a long time away but here is an early draft of what the above currently looks like. Is this more or less what you were thinking?
diff -rup -X /usr/src/patchset-0.5/bin//dontdiff linux-2.6.16-rc3-mm1-103_x86coremem/arch/powerpc/mm/numa.c linux-2.6.16-rc3-mm1-104_ppc64coremem/arch/powerpc/mm/numa.c --- linux-2.6.16-rc3-mm1-103_x86coremem/arch/powerpc/mm/numa.c 2006-02-16 09:50:42.000000000 +0000 +++ linux-2.6.16-rc3-mm1-104_ppc64coremem/arch/powerpc/mm/numa.c 2006-02-21 11:45:38.000000000 +0000 @@ -17,10 +17,12 @@ #include <linux/nodemask.h> #include <linux/cpu.h> #include <linux/notifier.h> +#include <linux/sort.h> #include <asm/sparsemem.h> #include <asm/lmb.h> #include <asm/system.h> #include <asm/smp.h> +#include <asm/machdep.h>
static int numa_enabled = 1;
@@ -101,22 +103,41 @@ void __init add_region(unsigned int nid, init_node_data[i].nid = nid; }
+/* Compare two elements in init_node_data. Assume start_pfn is at start */ +static int cmp_init_node_data(const void *a, const void *b) +{ + return *(unsigned long *)a - *(unsigned long *)b; +} + +/* + * init_node_data is not necessarilly in pfn order making it difficult to + * determine where the EasyRclm should begin if it is requested. This sorts + * init_node_data by start_pfn + */ +void __init sort_regions(void) +{ + size_t num = 0; + size_t size_element = sizeof(init_node_data) / MAX_REGIONS; + + while (init_node_data[num].end_pfn) + num++; + + sort(init_node_data, num, size_element, cmp_init_node_data, NULL); +} + /* We assume init_node_data has no overlapping regions */ void __init get_region(unsigned int nid, unsigned long *start_pfn, - unsigned long *end_pfn, unsigned long *pages_present) + unsigned long *end_pfn) { unsigned int i;
*start_pfn = -1UL; - *end_pfn = *pages_present = 0; + *end_pfn = 0;
for (i = 0; init_node_data[i].end_pfn; i++) { if (init_node_data[i].nid != nid) continue;
- *pages_present += init_node_data[i].end_pfn - - init_node_data[i].start_pfn;
if (init_node_data[i].start_pfn < *start_pfn) *start_pfn = init_node_data[i].start_pfn;
@@ -129,6 +150,108 @@ void __init get_region(unsigned int nid, *start_pfn = 0; }
+/* + * Given a pfn range and the required number of pages for the kernel, return + * the zone of the DMA and EASYRCLM zones and any holes that are contained + */ +void __init get_zones_info(unsigned int nid, + unsigned long start_pfn, + unsigned long end_region_pfn, + unsigned long kernelcore_pages, + unsigned long *zones_size, + unsigned long *zholes_size) +{ + unsigned long end_kernelcore_pfn = 0; + unsigned long pages_present_kernel = 0; + unsigned long pages_present = 0; + unsigned int i; + + dbg("get_zones_info: nid: %u kernelcore_pages: %lu range %lu -> %lu\n", + nid, + kernelcore_pages, + start_pfn, + end_region_pfn); + + /* Handle the case where this region is totally empty */ + if (end_region_pfn == start_pfn) { + dbg("get_zones_info: region is empty\n"); + zones_size[ZONE_DMA] = 0; + zones_size[ZONE_EASYRCLM] = 0; + zholes_size[ZONE_DMA] = 0; + zholes_size[ZONE_EASYRCLM] = 0; + return; + } + + /* + * If kernelcore_pages were not required, make sure that this whole + * region will be used for ZONE_DMA + */ + dbg("get_zones_info: kernelcore_pages = %lu\n", kernelcore_pages); + if (kernelcore_pages == 0) { + dbg("get_zones_info: kernel will use whole region\n"); + kernelcore_pages = end_region_pfn - start_pfn; + end_kernelcore_pfn = end_region_pfn; + } + + /* + * Walk through node data to find a pfn where kernelcore_pages will + * be in the range start_pfn -> kernelcore_pfn . + */ + for (i = 0; init_node_data[i].end_pfn; i++) { + unsigned long segment_start_pfn, segment_end_pfn; + unsigned long local_pages_present; + + /* + * If this segment does not contain pages in the required + * pfn range, skip it + */ + if (init_node_data[i].nid != nid) + continue; + if (start_pfn >= init_node_data[i].end_pfn) + continue; + if (end_region_pfn <= init_node_data[i].start_pfn) + continue; + + /* Find what portion of the segment is of interest */ + segment_start_pfn = init_node_data[i].start_pfn; + segment_end_pfn = init_node_data[i].end_pfn; + if (segment_start_pfn < start_pfn) + segment_start_pfn = start_pfn; + if (segment_end_pfn > end_region_pfn) + segment_end_pfn = end_region_pfn; + + /* + * Update pages_present and see if we have reached the end + * of the kernelcore portion of the region + */ + local_pages_present = segment_end_pfn - segment_start_pfn; + if (local_pages_present > kernelcore_pages) { + end_kernelcore_pfn = + segment_start_pfn + kernelcore_pages; + pages_present_kernel = + pages_present + kernelcore_pages; + kernelcore_pages = 0; + } + + pages_present += local_pages_present; + if (kernelcore_pages) { + kernelcore_pages -= pages_present; + pages_present_kernel += pages_present; + } + } + + /* Set the zones value */ + dbg("get_zones_info: nid %d ZONE_DMA: %lu -> %lu\n", nid, + start_pfn, end_kernelcore_pfn); + dbg("get_zones_info: nid %d ZONE_EASYRCLM: %lu -> %lu\n", nid, + end_kernelcore_pfn, end_region_pfn); + zones_size[ZONE_DMA] = end_kernelcore_pfn - start_pfn; + zholes_size[ZONE_DMA] = zones_size[ZONE_DMA] - pages_present_kernel; + zones_size[ZONE_EASYRCLM] = end_region_pfn - end_kernelcore_pfn; + zholes_size[ZONE_EASYRCLM] = zones_size[ZONE_EASYRCLM] - + (pages_present - pages_present_kernel); +} + static inline void map_cpu_to_node(int cpu, int node) { numa_cpu_lookup_table[cpu] = node; @@ -622,11 +745,11 @@ void __init do_init_bootmem(void) register_cpu_notifier(&ppc64_numa_nb);
for_each_online_node(nid) { - unsigned long start_pfn, end_pfn, pages_present; + unsigned long start_pfn, end_pfn; unsigned long bootmem_paddr; unsigned long bootmap_pages;
- get_region(nid, &start_pfn, &end_pfn, &pages_present); + get_region(nid, &start_pfn, &end_pfn);
/* Allocate the node structure node local if possible */ NODE_DATA(nid) = careful_allocation(nid, @@ -721,21 +844,39 @@ void __init paging_init(void) { unsigned long zones_size[MAX_NR_ZONES]; unsigned long zholes_size[MAX_NR_ZONES]; + unsigned long kernelcore_pages = 0; int nid; + char *opt;
memset(zones_size, 0, sizeof(zones_size)); memset(zholes_size, 0, sizeof(zholes_size));
- for_each_online_node(nid) { - unsigned long start_pfn, end_pfn, pages_present; + /* Check if ZONE_EASYRCLM should be populated */ + opt = strstr(cmd_line, "kernelcore="); + if (opt) { + opt += 11; + unsigned long size_bytes = memparse(opt, &opt); + kernelcore_pages = size_bytes >> PAGE_SHIFT; + }
- get_region(nid, &start_pfn, &end_pfn, &pages_present); + sort_regions(); + for_each_online_node(nid) { + unsigned long start_pfn, end_region_pfn;
- zones_size[ZONE_DMA] = end_pfn - start_pfn; - zholes_size[ZONE_DMA] = zones_size[ZONE_DMA] - pages_present; + get_region(nid, &start_pfn, &end_region_pfn);
- dbg("free_area_init node %d %lx %lx (hole: %lx)\n", nid, - zones_size[ZONE_DMA], start_pfn, zholes_size[ZONE_DMA]); + get_zones_info(nid, start_pfn, end_region_pfn, + kernelcore_pages, + &zones_size[0], + &zholes_size[0]); + + dbg("free_area_init DMA node %d %lx %lx (hole: %lx)\n", + nid, zones_size[ZONE_DMA], + start_pfn, zholes_size[ZONE_DMA]); + + dbg("free_area_init EasyRclm node %d %lx %lx (hole: %lx)\n", + nid, zones_size[ZONE_EASYRCLM], + start_pfn, zholes_size[ZONE_EASYRCLM]);
free_area_init_node(nid, NODE_DATA(nid), zones_size, start_pfn, zholes_size); -- Mel Gorman Part-time Phd Student Linux Technology Center University of Limerick IBM Dublin Software Lab - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |