This is V8 of the patchset to size zones and memory holes in an
architecture-independent manner. The notable addition in this release is
accounting for mem_map as a memory hole as it is not reclaimable and the
optional account of the kernel image as a memory hole. This is to match the
existing behavior of x86_64.
Changelog since V7
o Rebase to 2.6.17-mm6
o Account for mem_map as a memory hole
o Adjust mem_map when arch independent zone-sizing is used and PFN 0 is in
a memory hole not accounted for by ARCH_PFN_OFFSET
Changelog since V6
o MAX_ACTIVE_REGIONS is really maximum active regions, not MAX_ACTIVE_REGIONS-1
o MAX_ACTIVE_REGIONS is 256 unless the architecture specifically asks for
a different number or MAX_NUMNODES is >= 32
o nr_nodemap_entries tracks the number of entries rather than terminating with
end_pfn == 0
o Add number of documentation-related comments. Functions exposed by headers
may potentially be picked up by kerneldoc
o Changed misleading zone_present_pages_in_node() name to
zone_spanned_pages_in_node()
o Be a bit more verbose to help debugging when things go wrong.
o On x86_64, end_pfn_map now gets updated properly or ACPI tables get "lost"
o Signoffs added to patches 1 and 5 by Bob Picco related to contributions,
fixes and reviews
Changelog since V5
o Add a missing #include to mm/mem_init.c
o Drop the verbose debugging part of the set
o Report active range registration when loglevel is set for KERN_DEBUG
Changelog since V4
o Rebase to 2.6.17-rc3-mm1
o Calculate holes on x86 with SRAT correctly
Changelog since V3
o Rebase to 2.6.17-rc2
o Allow the active regions to be cleared. Needed by x86_64 when it decides
the SRAT table is bad half way through the registering of active regions
o Fix for flatmem x86_64 machines booting
Changelog since V2
o Fix a bug where holes in lower zones get double counted
o Catch the case where a new range is registered that is within an range
o Catch the case where a zone boundary is within a hole
o Use the EFI map for registering ranges on x86_64+numa
o On IA64+NUMA, add the active ranges before rounding for granules
o On x86_64, remove e820_hole_size and e820_bootmem_free and use
arch-independent equivalents
o On x86_64, remove the map walk in e820_end_of_ram()
o Rename memory_present_with_active_regions, name ambiguous
o Add absent_pages_in_range() for arches to call
Changelog since V1
o Correctly convert virtual and physical addresses to PFNs on ia64
o Correctly convert physical addresses to PFN on older ppc
o When add_active_range() is called with overlapping pfn ranges, merge them
o When a zone boundary occurs within a memory hole, account correctly
o Minor whitespace damage cleanup
o Debugging patch temporarily included
At a basic level, architectures define structures to record where active
ranges of page frames are located. Once located, the code to calculate
zone sizes and holes in each architecture is very similar. Some of this
zone and hole sizing code is difficult to read for no good reason. This
set of patches eliminates the similar-looking architecture-specific code.
The patches introduce a mechanism where architectures register where the
active ranges of page frames are with add_active_range(). When all areas
have been discovered, free_area_init_nodes() is called to initialise
the pgdat and zones. The zone sizes and holes are then calculated in an
architecture independent manner.
Patch 1 introduces the mechanism for registering and initialising PFN ranges
Patch 2 changes ppc to use the mechanism - 134 arch-specific LOC removed
Patch 3 changes x86 to use the mechanism - 142 arch-specific LOC removed
Patch 4 changes x86_64 to use the mechanism - 78 arch-specific LOC removed
Patch 5 changes ia64 to use the mechanism - 57 arch-specific LOC removed
Patch 6 accounts for mem_map as a memory hole as the pages are not reclaimable.
It adjusts the watermarks slightly
The patches have been successfully boot tested by me and verified that the
zones are the correct size on
o x86, flatmem with 1.5GiB of RAM
o x86, NUMAQ
o x86 with SRAT CONFIG_NUMA=n
o PPC64, NUMA
o PPC64, CONFIG_NUMA=n
o PPC64, CONFIG_64BIT=N
o x86_64, NUMA with SRAT
o x86_64, NUMA with broken SRAT that falls back to k8topology discovery
o x86_64, CONFIG_NUMA=n
o x86_64, CONFIG_64=n
o x86_64, CONFIG_64=n, CONFIG_NUMA=n
o x86_64, ACPI_NUMA, ACPI_MEMORY_HOTPLUG && !SPARSEMEM to trigger the
hotadd path without sparsemem fun in srat.c (SRAT broken on test machine and
I'm pretty sure the machine does not support physical memory hotadd anyway
so test may not have been effective other than being a compile test.)
o ia64 (Itanium 2)
o ia64 (Itanium 2), CONFIG_64=N
Tony Luck has successfully tested for ia64 on Itanium with tiger_defconfig,
gensparse_defconfig and defconfig. Bob Picco has also tested and debugged
on IA64. Jack Steiner successfully boot tested on a mammoth SGI IA64-based
machine. These were on patches against 2.6.17-rc1 and release 3 of these
patches but there have been no ia64-changes since release 3.
There are differences in the zone sizes for x86_64 as the arch-specific code
for x86_64 accounts the kernel image and the starting mem_maps as memory
holes but the architecture-independent code accounts the memory as present.
The big benefit of this set of patches is the reduction of 411 lines of
architecture-specific code, some of which is very hairy. There should be
a greater net reduction when other architectures use the same mechanisms
for zone and hole sizing but I lack the hardware to test on.
Additional credit;
Dave Hansen for the initial suggestion and comments on early patches
Andy Whitcroft for reviewing early versions and catching numerous
errors
Tony Luck for testing and debugging on IA64
Bob Picco for fixing bugs related to pfn registration, reviewing a
number of patch revisions, providing a number of suggestions
on future direction and testing heavily
Jack Steiner and Robin Holt for testing on IA64 and clarifying
issues related to memory holes
Yasunori for testing on IA64
Andi Kleen for reviewing and feeding back about x86_64
Christian Kujau for providing valuable information related to ACPI
problems on x86_64 and testing potential fixes
--
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
This patch defines the structure to represent an active range of page
frames within a node in an architecture independent manner. Architectures
are expected to register active ranges of PFNs using add_active_range(nid,
start_pfn, end_pfn) and call free_area_init_nodes() passing the PFNs of
the end of each zone.
include/linux/mm.h | 45 +++
include/linux/mmzone.h | 10
mm/page_alloc.c | 557 ++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 586 insertions(+), 26 deletions(-)
Signed-off-by: Mel Gorman <redacted>
Signed-off-by: Bob Picco <redacted>
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.17-mm6-clean/include/linux/mm.h linux-2.6.17-mm6-101-add_free_area_init_nodes/include/linux/mm.h
@@ -86,6 +88,33 @@ int min_free_kbytes = 1024;unsignedlong__meminitdatanr_kernel_pages;unsignedlong__meminitdatanr_all_pages;+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP+/*+*MAX_ACTIVE_REGIONSdeterminesthemaxmimumnumberofdistinct+*rangesofmemory(RAM)thatmayberegisteredwithadd_active_range().+*Rangespassedtoadd_active_range()willbemergedifpossible+*sothenumberoftimesadd_active_range()canbecalledis+*relatedtothenumberofnodesandthenumberofholes+*/+#ifdef CONFIG_MAX_ACTIVE_REGIONS+/* Allow an architecture to set MAX_ACTIVE_REGIONS to save memory */+#define MAX_ACTIVE_REGIONS CONFIG_MAX_ACTIVE_REGIONS+#else+#if MAX_NUMNODES >= 32+/* If there can be many nodes, allow up to 50 holes per node */+#define MAX_ACTIVE_REGIONS (MAX_NUMNODES*50)+#else+/* By default, allow up to 256 distinct regions */+#define MAX_ACTIVE_REGIONS 256+#endif+#endif++structnode_active_region__initdataearly_node_map[MAX_ACTIVE_REGIONS];+int__initdatanr_nodemap_entries;+unsignedlong__initdataarch_zone_lowest_possible_pfn[MAX_NR_ZONES];+unsignedlong__initdataarch_zone_highest_possible_pfn[MAX_NR_ZONES];+#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */+#ifdef CONFIG_DEBUG_VMstaticintpage_outside_zone_boundaries(structzone*zone,structpage*page){
@@ -2064,6 +2074,272 @@ __meminit int init_currently_empty_zone(return0;}+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP+/*+*Basiciteratorsupport.ReturnthefirstrangeofPFNsforanode+*Note:nid==MAX_NUMNODESreturnsfirstregionregardlessofnode+*/+staticint__initfirst_active_region_index_in_nid(intnid)+{+inti;++for(i=0;i<nr_nodemap_entries;i++)+if(nid==MAX_NUMNODES||early_node_map[i].nid==nid)+returni;++return-1;+}++/*+*Basiciteratorsupport.ReturnthenextactiverangeofPFNsforanode+*Note:nid==MAX_NUMNODESreturnsnextregionregardlesofnode+*/+staticint__initnext_active_region_index_in_nid(intindex,intnid)+{+for(index=index+1;index<nr_nodemap_entries;index++)+if(nid==MAX_NUMNODES||early_node_map[index].nid==nid)+returnindex;++return-1;+}++#ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID+/*+*RequiredbySPARSEMEM.GivenaPFN,returnwhatnodethePFNison.+*Architecturesmayimplementtheirownversionbutifadd_active_range()+*wasusedandtherearenospecialrequirements,thisisaconvenient+*alternative+*/+int__initearly_pfn_to_nid(unsignedlongpfn)+{+inti;++for(i=0;i<nr_nodemap_entries;i++){+unsignedlongstart_pfn=early_node_map[i].start_pfn;+unsignedlongend_pfn=early_node_map[i].end_pfn;++if(start_pfn<=pfn&&pfn<end_pfn)+returnearly_node_map[i].nid;+}++return0;+}+#endif /* CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID */++/* Basic iterator support to walk early_node_map[] */+#define for_each_active_range_index_in_nid(i, nid) \+for(i=first_active_region_index_in_nid(nid);i!=-1;\+i=next_active_region_index_in_nid(i,nid))++/**+*free_bootmem_with_active_regions-Callfree_bootmem_nodeforeachactiverange+*@nid:Thenodetofreememoryon.IfMAX_NUMNODES,allnodesarefreed+*@max_low_pfn:ThehighestPFNthattillbepassedtofree_bootmem_node+*+*Ifanarchitectureguaranteesthatallrangesregisteredwith+*add_active_ranges()containnoholesandmaybefreed,this+*thisfunctionmaybeusedinsteadofcallingfree_bootmem()manually.+*/+void__initfree_bootmem_with_active_regions(intnid,+unsignedlongmax_low_pfn)+{+inti;++for_each_active_range_index_in_nid(i,nid){+unsignedlongsize_pages=0;+unsignedlongend_pfn=early_node_map[i].end_pfn;++if(early_node_map[i].start_pfn>=max_low_pfn)+continue;++if(end_pfn>max_low_pfn)+end_pfn=max_low_pfn;++size_pages=end_pfn-early_node_map[i].start_pfn;+free_bootmem_node(NODE_DATA(early_node_map[i].nid),+PFN_PHYS(early_node_map[i].start_pfn),+size_pages<<PAGE_SHIFT);+}+}++/**+*sparse_memory_present_with_active_regions-Callmemory_presentforeachactiverange+*@nid:Thenodetocallmemory_presentfor.IfMAX_NUMNODES,allnodeswillbeused+*+*Ifanarchitectureguaranteesthatallrangesregisteredwith+*add_active_ranges()containnoholesandmaybefreed,this+*thisfunctionmaybeusedinsteadofcallingmemory_present()manually.+*/+void__initsparse_memory_present_with_active_regions(intnid)+{+inti;++for_each_active_range_index_in_nid(i,nid)+memory_present(early_node_map[i].nid,+early_node_map[i].start_pfn,+early_node_map[i].end_pfn);+}++/**+*get_pfn_range_for_nid-Returnthestartandendpageframesforanode+*@nid:Thenidtoreturntherangefor.IfMAX_NUMNODES,theminandmaxPFNarereturned+*@start_pfn:Passedbyreference.Onreturn,itwillhavethenodestart_pfn+*@end_pfn:Passedbyreference.Onreturn,itwillhavethenodeend_pfn+*+*Itreturnsthestartandendpageframeofanodebasedoninformation+*providedbyanarchcallingadd_active_range().Ifcalledforanode+*withnoavailablememory,awarningisprintedandthestartandend+*PFNswillbe0+*/+void__initget_pfn_range_for_nid(unsignedintnid,+unsignedlong*start_pfn,unsignedlong*end_pfn)+{+inti;+*start_pfn=-1UL;+*end_pfn=0;++for_each_active_range_index_in_nid(i,nid){+*start_pfn=min(*start_pfn,early_node_map[i].start_pfn);+*end_pfn=max(*end_pfn,early_node_map[i].end_pfn);+}++if(*start_pfn==-1UL){+printk(KERN_WARNING"Node %u active with no memory\n",nid);+*start_pfn=0;+}+}++/*+*Returnthenumberofpagesazonespansinanode,includingholes+*present_pages=zone_spanned_pages_in_node()-zone_absent_pages_in_node()+*/+unsignedlong__initzone_spanned_pages_in_node(intnid,+unsignedlongzone_type,+unsignedlong*ignored)+{+unsignedlongnode_start_pfn,node_end_pfn;+unsignedlongzone_start_pfn,zone_end_pfn;++/* Get the start and end of the node and zone */+get_pfn_range_for_nid(nid,&node_start_pfn,&node_end_pfn);+zone_start_pfn=arch_zone_lowest_possible_pfn[zone_type];+zone_end_pfn=arch_zone_highest_possible_pfn[zone_type];++/* Check that this node has pages within the zone's required range */+if(zone_end_pfn<node_start_pfn||zone_start_pfn>node_end_pfn)+return0;++/* Move the zone boundaries inside the node if necessary */+zone_end_pfn=min(zone_end_pfn,node_end_pfn);+zone_start_pfn=max(zone_start_pfn,node_start_pfn);++/* Return the spanned pages */+returnzone_end_pfn-zone_start_pfn;+}++/*+*Returnthenumberofholesinarangeonanode.IfnidisMAX_NUMNODES,+*thenallholesintherequestedrangewillbeaccountedfor+*/+unsignedlong__init__absent_pages_in_range(intnid,+unsignedlongrange_start_pfn,+unsignedlongrange_end_pfn)+{+inti=0;+unsignedlongprev_end_pfn=0,hole_pages=0;+unsignedlongstart_pfn;++/* Find the end_pfn of the first active range of pfns in the node */+i=first_active_region_index_in_nid(nid);+if(i==-1)+return0;++prev_end_pfn=early_node_map[i].start_pfn;++/* Find all holes for the zone within the node */+for(;i!=-1;i=next_active_region_index_in_nid(i,nid)){++/* No need to continue if prev_end_pfn is outside the zone */+if(prev_end_pfn>=range_end_pfn)+break;++/* Make sure the end of the zone is not within the hole */+start_pfn=min(early_node_map[i].start_pfn,range_end_pfn);+prev_end_pfn=max(prev_end_pfn,range_start_pfn);++/* Update the hole size cound and move on */+if(start_pfn>range_start_pfn){+BUG_ON(prev_end_pfn>start_pfn);+hole_pages+=start_pfn-prev_end_pfn;+}+prev_end_pfn=early_node_map[i].end_pfn;+}++returnhole_pages;+}++/**+*absent_pages_in_range-Returnnumberofpageframesinholeswithinarange+*@start_pfn:ThestartPFNtostartsearchingforholes+*@end_pfn:TheendPFNtostopsearchingforholes+*+*Itreturnsthenumberofpagesframesinmemoryholeswithinarange+*/+unsignedlong__initabsent_pages_in_range(unsignedlongstart_pfn,+unsignedlongend_pfn)+{+return__absent_pages_in_range(MAX_NUMNODES,start_pfn,end_pfn);+}++/* Return the number of page frames in holes in a zone on a node */+unsignedlong__initzone_absent_pages_in_node(intnid,+unsignedlongzone_type,+unsignedlong*ignored)+{+return__absent_pages_in_range(nid,+arch_zone_lowest_possible_pfn[zone_type],+arch_zone_highest_possible_pfn[zone_type]);+}+#else+staticinlineunsignedlongzone_spanned_pages_in_node(intnid,+unsignedlongzone_type,+unsignedlong*zones_size)+{+returnzones_size[zone_type];+}++staticinlineunsignedlongzone_absent_pages_in_node(intnid,+unsignedlongzone_type,+unsignedlong*zholes_size)+{+if(!zholes_size)+return0;++returnzholes_size[zone_type];+}+#endif++staticvoid__initcalculate_node_totalpages(structpglist_data*pgdat,+unsignedlong*zones_size,unsignedlong*zholes_size)+{+unsignedlongrealtotalpages,totalpages=0;+inti;++for(i=0;i<MAX_NR_ZONES;i++)+totalpages+=zone_spanned_pages_in_node(pgdat->node_id,i,+zones_size);+pgdat->node_spanned_pages=totalpages;++realtotalpages=totalpages;+for(i=0;i<MAX_NR_ZONES;i++)+realtotalpages-=+zone_absent_pages_in_node(pgdat->node_id,i,+zholes_size);+pgdat->node_present_pages=realtotalpages;+printk(KERN_DEBUG"On node %d totalpages: %lu\n",pgdat->node_id,+realtotalpages);+}+/**Setupthezonedatastructures:*-markallpagesreserved
@@ -2171,13 +2451,240 @@ void __meminit free_area_init_node(int n{pgdat->node_id=nid;pgdat->node_start_pfn=node_start_pfn;-calculate_zone_totalpages(pgdat,zones_size,zholes_size);+calculate_node_totalpages(pgdat,zones_size,zholes_size);alloc_node_mem_map(pgdat);free_area_init_core(pgdat,zones_size,zholes_size);}+#ifdef CONFIG_ARCH_POPULATES_NODE_MAP+/**+*add_active_range-RegisterarangeofPFNsbackedbyphysicalmemory+*@nid:ThenodeIDtherangeresideson+*@start_pfn:ThestartPFNoftheavailablephysicalmemory+*@end_pfn:TheendPFNoftheavailablephysicalmemory+*+*Theserangesarestoredinanearly_node_map[]andlaterusedby+*free_area_init_nodes()tocalculatezonesizesandholes.Ifthe+*rangespansamemoryhole,itisuptothearchitecturetoensure+*thememoryisnotfreedbythebootmemallocator.Ifpossible+*therangebeingregisteredwillbemergedwithexistingranges.+*/+void__initadd_active_range(unsignedintnid,unsignedlongstart_pfn,+unsignedlongend_pfn)+{+inti;++printk(KERN_DEBUG"Entering add_active_range(%d, %lu, %lu) "+"%d entries of %d used\n",+nid,start_pfn,end_pfn,+nr_nodemap_entries,MAX_ACTIVE_REGIONS);++/* Merge with existing active regions if possible */+for(i=0;i<nr_nodemap_entries;i++){+if(early_node_map[i].nid!=nid)+continue;++/* Skip if an existing region covers this new one */+if(start_pfn>=early_node_map[i].start_pfn&&+end_pfn<=early_node_map[i].end_pfn)+return;++/* Merge forward if suitable */+if(start_pfn<=early_node_map[i].end_pfn&&+end_pfn>early_node_map[i].end_pfn){+early_node_map[i].end_pfn=end_pfn;+return;+}++/* Merge backward if suitable */+if(start_pfn<early_node_map[i].end_pfn&&+end_pfn>=early_node_map[i].start_pfn){+early_node_map[i].start_pfn=start_pfn;+return;+}+}++/* Check that early_node_map is large enough */+if(i>=MAX_ACTIVE_REGIONS){+printk(KERN_CRIT"More than %d memory regions, truncating\n",+MAX_ACTIVE_REGIONS);+return;+}++early_node_map[i].nid=nid;+early_node_map[i].start_pfn=start_pfn;+early_node_map[i].end_pfn=end_pfn;+nr_nodemap_entries=i+1;+}++/**+*shrink_active_range-ShrinkanexistingregisteredrangeofPFNs+*@nid:Thenodeidtherangeisonthatshouldbeshrunk+*@old_end_pfn:TheoldendPFNoftherange+*@new_end_pfn:ThenewPFNoftherange+*+*i386withNUMAusealloc_remap()tostoreanode_mem_maponalocalnode.+*Themapiskeptattheendphysicalpagerangethathasalreadybeen+*registeredwithadd_active_range().Thisfunctionallowsanarchtoshrink+*anexistingregisteredrange.+*/+void__initshrink_active_range(unsignedintnid,unsignedlongold_end_pfn,+unsignedlongnew_end_pfn)+{+inti;++/* Find the old active region end and shrink */+for_each_active_range_index_in_nid(i,nid)+if(early_node_map[i].end_pfn==old_end_pfn){+early_node_map[i].end_pfn=new_end_pfn;+break;+}+}++/**+*remove_all_active_ranges-Removeallcurrentlyregisteredregions+*Duringdiscovery,itmaybefoundthatatablelikeSRATisinvalid+*andanalternativediscoverymethodmustbeused.Thisfunctionremoves+*allcurrentlyregisteredregions.+*/+void__initremove_all_active_ranges()+{+memset(early_node_map,0,sizeof(early_node_map));+nr_nodemap_entries=0;+}++/* Compare two active node_active_regions */+staticint__initcmp_node_active_region(constvoid*a,constvoid*b)+{+structnode_active_region*arange=(structnode_active_region*)a;+structnode_active_region*brange=(structnode_active_region*)b;++/* Done this way to avoid overflows */+if(arange->start_pfn>brange->start_pfn)+return1;+if(arange->start_pfn<brange->start_pfn)+return-1;++return0;+}++/* sort the node_map by start_pfn */+staticvoid__initsort_node_map(void)+{+sort(early_node_map,(size_t)nr_nodemap_entries,+sizeof(structnode_active_region),+cmp_node_active_region,NULL);+}++/* Find the lowest pfn for a node. This depends on a sorted early_node_map */+unsignedlong__initfind_min_pfn_for_node(unsignedlongnid)+{+inti;++/* Assuming a sorted map, the first range found has the starting pfn */+for_each_active_range_index_in_nid(i,nid)+returnearly_node_map[i].start_pfn;++printk(KERN_WARNING"Could not find start_pfn for node %lu\n",nid);+return0;+}++/**+*find_min_pfn_with_active_regions-FindtheminimumPFNregistered+*+*ItreturnstheminimumPFNbasedoninformationprovidedvia+*add_active_range()+*/+unsignedlong__initfind_min_pfn_with_active_regions(void)+{+returnfind_min_pfn_for_node(MAX_NUMNODES);+}++/**+*find_max_pfn_with_active_regions-FindthemaximumPFNregistered+*+*ItreturnsthemaximumPFNbasedoninformationprovidedvia+*add_active_range()+*/+unsignedlong__initfind_max_pfn_with_active_regions(void)+{+inti;+unsignedlongmax_pfn=0;++for(i=0;i<nr_nodemap_entries;i++)+max_pfn=max(max_pfn,early_node_map[i].end_pfn);++returnmax_pfn;+}++/**+*free_area_init_nodes-Initialiseallpg_data_tandzonedata+*@arch_max_dma_pfn:ThemaximumPFNusableforZONE_DMA+*@arch_max_dma32_pfn:ThemaximumPFNusableforZONE_DMA32+*@arch_max_low_pfn:ThemaximumPFNusableforZONE_NORMAL+*@arch_max_high_pfn:ThemaximumPFNusableforZONE_HIGHMEM+*+*Thiswillcallfree_area_init_node()foreachactivenodeinthesystem.+*Usingthepagerangesprovidedbyadd_active_range(),thesizeofeach+*zoneineachnodeandtheirholesiscalculated.IfthemaximumPFN+*betweentwoadjacentzonesmatch,itisassumedthatthezoneisempty.+*Forexample,ifarch_max_dma_pfn==arch_max_dma32_pfn,itisassumed+*thatarch_max_dma32_pfnhasnopages.Itisalsoassumedthatazone+*startswherethepreviousoneended.Forexample,ZONE_DMA32starts+*atarch_max_dma_pfn.+*/+void__initfree_area_init_nodes(unsignedlongarch_max_dma_pfn,+unsignedlongarch_max_dma32_pfn,+unsignedlongarch_max_low_pfn,+unsignedlongarch_max_high_pfn)+{+unsignedlongnid;+inti;++/* Record where the zone boundaries are */+memset(arch_zone_lowest_possible_pfn,0,+sizeof(arch_zone_lowest_possible_pfn));+memset(arch_zone_highest_possible_pfn,0,+sizeof(arch_zone_highest_possible_pfn));+arch_zone_lowest_possible_pfn[ZONE_DMA]=+find_min_pfn_with_active_regions();+arch_zone_highest_possible_pfn[ZONE_DMA]=arch_max_dma_pfn;+arch_zone_highest_possible_pfn[ZONE_DMA32]=arch_max_dma32_pfn;+arch_zone_highest_possible_pfn[ZONE_NORMAL]=arch_max_low_pfn;+arch_zone_highest_possible_pfn[ZONE_HIGHMEM]=arch_max_high_pfn;+for(i=1;i<MAX_NR_ZONES;i++)+arch_zone_lowest_possible_pfn[i]=+arch_zone_highest_possible_pfn[i-1];++/* Regions in the early_node_map can be in any order */+sort_node_map();++/* Print out the zone ranges */+printk("Zone PFN ranges:\n");+for(i=0;i<MAX_NR_ZONES;i++)+printk(" %-8s %8lu -> %8lu\n",+zone_names[i],+arch_zone_lowest_possible_pfn[i],+arch_zone_highest_possible_pfn[i]);++/* Print out the early_node_map[] */+printk("early_node_map[%d] active PFN ranges\n",nr_nodemap_entries);+for(i=0;i<nr_nodemap_entries;i++)+printk(" %3d: %8lu -> %8lu\n",early_node_map[i].nid,+early_node_map[i].start_pfn,+early_node_map[i].end_pfn);++/* Initialise every node */+for_each_online_node(nid){+pg_data_t*pgdat=NODE_DATA(nid);+free_area_init_node(nid,pgdat,NULL,+find_min_pfn_for_node(nid),NULL);+}+}+#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */+#ifndef CONFIG_NEED_MULTIPLE_NODESstaticbootmem_data_tcontig_bootmem_data;structpglist_datacontig_page_data={.bdata=&contig_bootmem_data};
@@ -256,20 +256,22 @@ void __init do_init_bootmem(void)boot_mapsize=init_bootmem(start>>PAGE_SHIFT,total_pages);+/* Add active regions with valid PFNs */+for(i=0;i<lmb.memory.cnt;i++){+unsignedlongstart_pfn,end_pfn;+start_pfn=lmb.memory.region[i].base>>PAGE_SHIFT;+end_pfn=start_pfn+lmb_size_pages(&lmb.memory,i);+add_active_range(0,start_pfn,end_pfn);+}+/* Add all physical memory to the bootmem map, mark each area*present.*/-for(i=0;i<lmb.memory.cnt;i++){-unsignedlongbase=lmb.memory.region[i].base;-unsignedlongsize=lmb_size_bytes(&lmb.memory,i);#ifdef CONFIG_HIGHMEM-if(base>=total_lowmem)-continue;-if(base+size>total_lowmem)-size=total_lowmem-base;+free_bootmem_with_active_regions(0,total_lowmem>>PAGE_SHIFT);+#else+free_bootmem_with_active_regions(0,max_pfn);#endif-free_bootmem(base,size);-}/* reserve the sections we're already using */for(i=0;i<lmb.reserved.cnt;i++)
@@ -277,9 +279,8 @@ void __init do_init_bootmem(void)lmb_size_bytes(&lmb.reserved,i));/* XXX need to clip this if using highmem? */-for(i=0;i<lmb.memory.cnt;i++)-memory_present(0,lmb_start_pfn(&lmb.memory,i),-lmb_end_pfn(&lmb.memory,i));+sparse_memory_present_with_active_regions(0);+init_bootmem_done=1;}
@@ -39,96 +39,6 @@ static bootmem_data_t __initdata plat_nostaticintmin_common_depth;staticintn_mem_addr_cells,n_mem_size_cells;-/*-*Weneedsomewheretostorestart/end/nodeforeachregionuntilwehave-*allocatedtherealnode_datastructures.-*/-#define MAX_REGIONS (MAX_LMB_REGIONS*2)-staticstruct{-unsignedlongstart_pfn;-unsignedlongend_pfn;-intnid;-}init_node_data[MAX_REGIONS]__initdata;--int__initearly_pfn_to_nid(unsignedlongpfn)-{-unsignedinti;--for(i=0;init_node_data[i].end_pfn;i++){-unsignedlongstart_pfn=init_node_data[i].start_pfn;-unsignedlongend_pfn=init_node_data[i].end_pfn;--if((start_pfn<=pfn)&&(pfn<end_pfn))-returninit_node_data[i].nid;-}--return-1;-}--void__initadd_region(unsignedintnid,unsignedlongstart_pfn,-unsignedlongpages)-{-unsignedinti;--dbg("add_region nid %d start_pfn 0x%lx pages 0x%lx\n",-nid,start_pfn,pages);--for(i=0;init_node_data[i].end_pfn;i++){-if(init_node_data[i].nid!=nid)-continue;-if(init_node_data[i].end_pfn==start_pfn){-init_node_data[i].end_pfn+=pages;-return;-}-if(init_node_data[i].start_pfn==(start_pfn+pages)){-init_node_data[i].start_pfn-=pages;-return;-}-}--/*-*LeavelastentryNULLsowedontiterateofftheend(weuse-*entry.end_pfntoterminatethewalk).-*/-if(i>=(MAX_REGIONS-1)){-printk(KERN_ERR"WARNING: too many memory regions in "-"numa code, truncating\n");-return;-}--init_node_data[i].start_pfn=start_pfn;-init_node_data[i].end_pfn=start_pfn+pages;-init_node_data[i].nid=nid;-}--/* We assume init_node_data has no overlapping regions */-void__initget_region(unsignedintnid,unsignedlong*start_pfn,-unsignedlong*end_pfn,unsignedlong*pages_present)-{-unsignedinti;--*start_pfn=-1UL;-*end_pfn=*pages_present=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;--if(init_node_data[i].end_pfn>*end_pfn)-*end_pfn=init_node_data[i].end_pfn;-}--/* We didnt find a matching region, return start/end as 0 */-if(*start_pfn==-1UL)-*start_pfn=0;-}-staticvoid__cpuinitmap_cpu_to_node(intcpu,intnode){numa_cpu_lookup_table[cpu]=node;
@@ -633,11 +546,11 @@ void __init do_init_bootmem(void)(void*)(unsignedlong)boot_cpuid);for_each_online_node(nid){-unsignedlongstart_pfn,end_pfn,pages_present;+unsignedlongstart_pfn,end_pfn;unsignedlongbootmem_paddr;unsignedlongbootmap_pages;-get_region(nid,&start_pfn,&end_pfn,&pages_present);+get_pfn_range_for_nid(nid,&start_pfn,&end_pfn);/* Allocate the node structure node local if possible */NODE_DATA(nid)=careful_allocation(nid,
@@ -670,19 +583,7 @@ void __init do_init_bootmem(void)init_bootmem_node(NODE_DATA(nid),bootmem_paddr>>PAGE_SHIFT,start_pfn,end_pfn);-/* Add free regions on this node */-for(i=0;init_node_data[i].end_pfn;i++){-unsignedlongstart,end;--if(init_node_data[i].nid!=nid)-continue;--start=init_node_data[i].start_pfn<<PAGE_SHIFT;-end=init_node_data[i].end_pfn<<PAGE_SHIFT;--dbg("free_bootmem %lx %lx\n",start,end-start);-free_bootmem_node(NODE_DATA(nid),start,end-start);-}+free_bootmem_with_active_regions(nid,end_pfn);/* Mark reserved regions on this node */for(i=0;i<lmb.reserved.cnt;i++){
@@ -713,44 +614,14 @@ void __init do_init_bootmem(void)}}-/* Add regions into sparsemem */-for(i=0;init_node_data[i].end_pfn;i++){-unsignedlongstart,end;--if(init_node_data[i].nid!=nid)-continue;--start=init_node_data[i].start_pfn;-end=init_node_data[i].end_pfn;--memory_present(nid,start,end);-}+sparse_memory_present_with_active_regions(nid);}}void__initpaging_init(void){-unsignedlongzones_size[MAX_NR_ZONES];-unsignedlongzholes_size[MAX_NR_ZONES];-intnid;--memset(zones_size,0,sizeof(zones_size));-memset(zholes_size,0,sizeof(zholes_size));--for_each_online_node(nid){-unsignedlongstart_pfn,end_pfn,pages_present;--get_region(nid,&start_pfn,&end_pfn,&pages_present);--zones_size[ZONE_DMA]=end_pfn-start_pfn;-zholes_size[ZONE_DMA]=zones_size[ZONE_DMA]-pages_present;--dbg("free_area_init node %d %lx %lx (hole: %lx)\n",nid,-zones_size[ZONE_DMA],start_pfn,zholes_size[ZONE_DMA]);--free_area_init_node(nid,NODE_DATA(nid),zones_size,start_pfn,-zholes_size);-}+unsignedlongend_pfn=lmb_end_of_DRAM()>>PAGE_SHIFT;+free_area_init_nodes(end_pfn,end_pfn,end_pfn,end_pfn);}staticint__initearly_numa(char*p)
@@ -369,19 +368,22 @@ void __init paging_init(void)(KMAP_FIX_BEGIN),KMAP_FIX_BEGIN),KMAP_FIX_BEGIN);kmap_prot=PAGE_KERNEL;#endif /* CONFIG_HIGHMEM */--/*-*AllpagesareDMA-ablesoweputthemallintheDMAzone.-*/-zones_size[ZONE_DMA]=total_lowmem>>PAGE_SHIFT;-for(i=1;i<MAX_NR_ZONES;i++)-zones_size[i]=0;+/* All pages are DMA-able so we put them all in the DMA zone. */+start_pfn=__pa(PAGE_OFFSET)>>PAGE_SHIFT;+end_pfn=start_pfn+(total_memory>>PAGE_SHIFT);+add_active_range(0,start_pfn,end_pfn);#ifdef CONFIG_HIGHMEM-zones_size[ZONE_HIGHMEM]=(total_memory-total_lowmem)>>PAGE_SHIFT;+free_area_init_nodes(total_lowmem>>PAGE_SHIFT,+total_lowmem>>PAGE_SHIFT,+total_lowmem>>PAGE_SHIFT,+total_memory>>PAGE_SHIFT);+#else+free_area_init_nodes(total_memory>>PAGE_SHIFT,+total_memory>>PAGE_SHIFT,+total_memory>>PAGE_SHIFT,+total_memory>>PAGE_SHIFT);#endif /* CONFIG_HIGHMEM */--free_area_init(zones_size);}void__initmem_init(void)
@@ -54,8 +54,6 @@ struct node_memory_chunk_s {staticstructnode_memory_chunk_snode_memory_chunk[MAXCHUNKS];staticintnum_memory_chunks;/* total number of memory chunks */-staticintzholes_size_init;-staticunsignedlongzholes_size[MAX_NUMNODES*MAX_NR_ZONES];externvoid*boot_ioremap(unsignedlong,unsignedlong);
@@ -135,50 +133,6 @@ static void __init parse_memory_affinity"enabled and removable":"enabled"));}-#if MAX_NR_ZONES != 4-#error "MAX_NR_ZONES != 4, chunk_to_zone requires review"-#endif-/* Take a chunk of pages from page frame cstart to cend and count the number-*ofpagesineachzone,returnedviazones[].-*/-static__initvoidchunk_to_zones(unsignedlongcstart,unsignedlongcend,-unsignedlong*zones)-{-unsignedlongmax_dma;-externunsignedlongmax_low_pfn;--intz;-unsignedlongrend;--/* FIXME: MAX_DMA_ADDRESS and max_low_pfn are trying to provide-*similarlyscopedinformationandshouldbehandledinaconsistant-*manner.-*/-max_dma=virt_to_phys((char*)MAX_DMA_ADDRESS)>>PAGE_SHIFT;--/* Split the hole into the zones in which it falls. Repeatedly-*takethesegmentinwhichtheremainingholestarts,roundit-*totheendofthatzone.-*/-memset(zones,0,MAX_NR_ZONES*sizeof(long));-while(cstart<cend){-if(cstart<max_dma){-z=ZONE_DMA;-rend=(cend<max_dma)?cend:max_dma;--}elseif(cstart<max_low_pfn){-z=ZONE_NORMAL;-rend=(cend<max_low_pfn)?cend:max_low_pfn;--}else{-z=ZONE_HIGHMEM;-rend=cend;-}-zones[z]+=rend-cstart;-cstart=rend;-}-}-/**TheSRATtablealwayslistsascendingaddresses,socanalways*assumethatthefirst"start"addressthatyouseeisthereal
@@ -287,6 +240,7 @@ static int __init acpi20_parse_srat(struprintk("chunk %d nid %d start_pfn %08lx end_pfn %08lx\n",j,chunk->nid,chunk->start_pfn,chunk->end_pfn);node_read_chunk(chunk->nid,chunk);+add_active_range(chunk->nid,chunk->start_pfn,chunk->end_pfn);}for_each_online_node(nid){
@@ -403,57 +357,7 @@ int __init get_memcfg_from_srat(void)returnacpi20_parse_srat((structacpi_table_srat*)header);}out_err:+remove_all_active_ranges();printk("failed to get NUMA memory information from SRAT table\n");return0;}--/* For each node run the memory list to determine whether there are-*anymemoryholes.ForeachholedeterminewhichZONEtheyfall-*into.-*-*NOTE#1:thisrequiresknowledgeofthezoneboundriesandso-*_cannot_beperformedbeforethosearecalculatedinsetup_memory.-*-*NOTE#2:werelyonthefactthatthememorychunksareorderedby-*startpfnnumberduringsetup.-*/-staticvoid__initget_zholes_init(void)-{-intnid;-intc;-intfirst;-unsignedlongend=0;--for_each_online_node(nid){-first=1;-for(c=0;c<num_memory_chunks;c++){-if(node_memory_chunk[c].nid==nid){-if(first){-end=node_memory_chunk[c].end_pfn;-first=0;--}else{-/* Record any gap between this chunk-*andthepreviouschunkonthisnode-*againstthezonesitspans.-*/-chunk_to_zones(end,-node_memory_chunk[c].start_pfn,-&zholes_size[nid*MAX_NR_ZONES]);-}-}-}-}-}--unsignedlong*__initget_zholes_size(intnid)-{-if(!zholes_size_init){-zholes_size_init++;-get_zholes_init();-}-if(nid>=MAX_NUMNODES||!node_online(nid))-printk("%s: nid = %d is invalid/offline. num_online_nodes = %d",-__FUNCTION__,nid,num_online_nodes());-return&zholes_size[nid*MAX_NR_ZONES];-}
@@ -156,21 +156,6 @@ static void __init find_max_pfn_node(intBUG();}-/* Find the owning node for a pfn. */-intearly_pfn_to_nid(unsignedlongpfn)-{-intnid;--for_each_node(nid){-if(node_end_pfn[nid]==0)-break;-if(node_start_pfn[nid]<=pfn&&node_end_pfn[nid]>=pfn)-returnnid;-}--return0;-}-/* *Allocatememoryforthepg_data_tforthisnodeviaacrudepre-bootmem*method.Fornodezerotakethisfromthebottomofmemory,for
@@ -226,6 +211,8 @@ static unsigned long calculate_numa_remaunsignedlongpfn;for_each_online_node(nid){+unsignedold_end_pfn=node_end_pfn[nid];+/**Theacpi/sratnodeinfocanshowhot-addmemroyzones*wherememorycouldbeaddedbutnotcurrentlypresent.
@@ -275,6 +262,7 @@ static unsigned long calculate_numa_remanode_end_pfn[nid]-=size;node_remap_start_pfn[nid]=node_end_pfn[nid];+shrink_active_range(nid,old_end_pfn,node_end_pfn[nid]);}printk("Reserving total of %ld pages for numa KVA remap\n",reserve_pages);
@@ -351,45 +339,20 @@ unsigned long __init setup_memory(void)void__initzone_sizes_init(void){intnid;+unsignedlongmax_dma_pfn;--for_each_online_node(nid){-unsignedlongzones_size[MAX_NR_ZONES]={0,0,0};-unsignedlong*zholes_size;-unsignedintmax_dma;--unsignedlonglow=max_low_pfn;-unsignedlongstart=node_start_pfn[nid];-unsignedlonghigh=node_end_pfn[nid];--max_dma=virt_to_phys((char*)MAX_DMA_ADDRESS)>>PAGE_SHIFT;--if(node_has_online_mem(nid)){-if(start>low){-#ifdef CONFIG_HIGHMEM-BUG_ON(start>high);-zones_size[ZONE_HIGHMEM]=high-start;-#endif-}else{-if(low<max_dma)-zones_size[ZONE_DMA]=low;-else{-BUG_ON(max_dma>low);-BUG_ON(low>high);-zones_size[ZONE_DMA]=max_dma;-zones_size[ZONE_NORMAL]=low-max_dma;-#ifdef CONFIG_HIGHMEM-zones_size[ZONE_HIGHMEM]=high-low;-#endif-}-}+/* If SRAT has not registered memory, register it now */+if(find_max_pfn_with_active_regions()==0){+for_each_online_node(nid){+if(node_has_online_mem(nid))+add_active_range(nid,node_start_pfn[nid],+node_end_pfn[nid]);}--zholes_size=get_zholes_size(nid);--free_area_init_node(nid,NODE_DATA(nid),zones_size,start,-zholes_size);}++max_dma_pfn=virt_to_phys((char*)MAX_DMA_ADDRESS)>>PAGE_SHIFT;+free_area_init_nodes(max_dma_pfn,max_dma_pfn,+max_low_pfn,highend_pfn);return;}
@@ -292,6 +216,49 @@ void __init e820_reserve_resources(void)}}+/* Walk the e820 map and register active regions within a node */+void__init+e820_register_active_regions(intnid,unsignedlongstart_pfn,+unsignedlongend_pfn)+{+inti;+unsignedlongei_startpfn,ei_endpfn;+for(i=0;i<e820.nr_map;i++){+structe820entry*ei=&e820.map[i];+ei_startpfn=round_up(ei->addr,PAGE_SIZE)>>PAGE_SHIFT;+ei_endpfn=round_down(ei->addr+ei->size,PAGE_SIZE)+>>PAGE_SHIFT;++/* Skip map entries smaller than a page */+if(ei_startpfn>ei_endpfn)+continue;++/* Check if end_pfn_map should be updated */+if(ei->type!=E820_RAM&&ei_endpfn>end_pfn_map)+end_pfn_map=ei_endpfn;++/* Skip if map is outside the node */+if(ei->type!=E820_RAM||+ei_endpfn<=start_pfn||+ei_startpfn>=end_pfn)+continue;++/* Check for overlaps */+if(ei_startpfn<start_pfn)+ei_startpfn=start_pfn;+if(ei_endpfn>end_pfn)+ei_endpfn=end_pfn;++/* Obey end_user_pfn to save on memmap */+if(ei_startpfn>=end_user_pfn)+continue;+if(ei_endpfn>end_user_pfn)+ei_endpfn=end_user_pfn;++add_active_range(nid,ei_startpfn,ei_endpfn);+}+}+/* *Addamemoryregiontothekernele820map.*/
@@ -403,69 +403,12 @@ void __cpuinit zap_low_mappings(int cpu)__flush_tlb_all();}-/* Compute zone sizes for the DMA and DMA32 zones in a node. */-__initvoid-size_zones(unsignedlong*z,unsignedlong*h,-unsignedlongstart_pfn,unsignedlongend_pfn)-{-inti;-unsignedlongw;--for(i=0;i<MAX_NR_ZONES;i++)-z[i]=0;--if(start_pfn<MAX_DMA_PFN)-z[ZONE_DMA]=MAX_DMA_PFN-start_pfn;-if(start_pfn<MAX_DMA32_PFN){-unsignedlongdma32_pfn=MAX_DMA32_PFN;-if(dma32_pfn>end_pfn)-dma32_pfn=end_pfn;-z[ZONE_DMA32]=dma32_pfn-start_pfn;-}-z[ZONE_NORMAL]=end_pfn-start_pfn;--/* Remove lower zones from higher ones. */-w=0;-for(i=0;i<MAX_NR_ZONES;i++){-if(z[i])-z[i]-=w;-w+=z[i];-}--/* Compute holes */-w=start_pfn;-for(i=0;i<MAX_NR_ZONES;i++){-unsignedlongs=w;-w+=z[i];-h[i]=e820_hole_size(s,w);-}--/* Add the space pace needed for mem_map to the holes too. */-for(i=0;i<MAX_NR_ZONES;i++)-h[i]+=(z[i]*sizeof(structpage))/PAGE_SIZE;--/* The 16MB DMA zone has the kernel and other misc mappings.-Accountthemtoo*/-if(h[ZONE_DMA]){-h[ZONE_DMA]+=dma_reserve;-if(h[ZONE_DMA]>=z[ZONE_DMA]){-printk(KERN_WARNING-"Kernel too large and filling up ZONE_DMA?\n");-h[ZONE_DMA]=z[ZONE_DMA];-}-}-}-#ifndef CONFIG_NUMAvoid__initpaging_init(void){-unsignedlongzones[MAX_NR_ZONES],holes[MAX_NR_ZONES];-memory_present(0,0,end_pfn);sparse_init();-size_zones(zones,holes,0,end_pfn);-free_area_init_node(0,NODE_DATA(0),zones,-__pa(PAGE_OFFSET)>>PAGE_SHIFT,holes);+free_area_init_nodes(MAX_DMA_PFN,MAX_DMA32_PFN,end_pfn,end_pfn);}#endif
@@ -146,6 +146,9 @@ int __init k8_scan_nodes(unsigned long snodes[nodeid].start=base;nodes[nodeid].end=limit;+e820_register_active_regions(nodeid,+nodes[nodeid].start>>PAGE_SHIFT,+nodes[nodeid].end>>PAGE_SHIFT);prevbase=base;
@@ -175,13 +175,11 @@ void __init setup_node_bootmem(int nodeivoid__initsetup_node_zones(intnodeid){unsignedlongstart_pfn,end_pfn,memmapsize,limit;-unsignedlongzones[MAX_NR_ZONES];-unsignedlongholes[MAX_NR_ZONES];start_pfn=node_start_pfn(nodeid);end_pfn=node_end_pfn(nodeid);-Dprintk(KERN_INFO"Setting up node %d %lx-%lx\n",+Dprintk(KERN_INFO"Setting up memmap for node %d %lx-%lx\n",nodeid,start_pfn,end_pfn);/* Try to allocate mem_map at end to not fill up precious <4GB
@@ -259,8 +253,11 @@ static int numa_emulation(unsigned long printk(KERN_ERR"No NUMA hash function found. Emulation disabled.\n");return-1;}-for_each_online_node(i)+for_each_online_node(i){+e820_register_active_regions(i,nodes[i].start>>PAGE_SHIFT,+nodes[i].end>>PAGE_SHIFT);setup_node_bootmem(i,nodes[i].start,nodes[i].end);+}numa_init_array();return0;}
@@ -173,7 +174,7 @@ static int hotadd_enough_memory(struct bif(mem<0)return0;-allowed=(end_pfn-e820_hole_size(0,end_pfn))*PAGE_SIZE;+allowed=(end_pfn-absent_pages_in_range(0,end_pfn))*PAGE_SIZE;allowed=(allowed/100)*hotadd_percent;if(allocated+mem>allowed){unsignedlongrange;
@@ -223,7 +224,7 @@ static int reserve_hotadd(int node, unsi}/* This check might be a bit too strict, but I'm keeping it for now. */-if(e820_hole_size(s_pfn,e_pfn)!=e_pfn-s_pfn){+if(absent_pages_in_range(s_pfn,e_pfn)!=e_pfn-s_pfn){printk(KERN_ERR"SRAT: Hotplug area has existing memory\n");return-1;}
@@ -341,13 +344,13 @@ static int nodes_cover_memory(void)unsignedlongs=nodes[i].start>>PAGE_SHIFT;unsignedlonge=nodes[i].end>>PAGE_SHIFT;pxmram+=e-s;-pxmram-=e820_hole_size(s,e);+pxmram-=absent_pages_in_range(s,e);pxmram-=nodes_add[i].end-nodes_add[i].start;if((long)pxmram<0)pxmram=0;}-e820ram=end_pfn-e820_hole_size(0,end_pfn);+e820ram=end_pfn-absent_pages_in_range(0,end_pfn);/* We seem to lose 3 pages somewhere. Allow a bit of slack. */if((long)(e820ram-pxmram)>=1*1024*1024){printk(KERN_ERR
@@ -361,6 +361,9 @@ config NODES_SHIFTMAX_NUMNODESwillbe2^(Thisvalue).Ifindoubt,usethedefault.+configARCH_POPULATES_NODE_MAP+def_booly+# VIRTUAL_MEM_MAP and FLAT_NODE_MEM_MAP are functionally equivalent.# VIRTUAL_MEM_MAP has been retained for historical reasons.configVIRTUAL_MEM_MAP
@@ -703,6 +703,7 @@ static __init int count_node_pages(unsig{unsignedlongend=start+len;+add_active_range(node,start>>PAGE_SHIFT,end>>PAGE_SHIFT);mem_data[node].num_physpages+=len>>PAGE_SHIFT;if(start<=__pa(MAX_DMA_ADDRESS))mem_data[node].num_dma_physpages+=
@@ -727,9 +728,8 @@ static __init int count_node_pages(unsigvoid__initpaging_init(void){unsignedlongmax_dma;-unsignedlongzones_size[MAX_NR_ZONES];-unsignedlongzholes_size[MAX_NR_ZONES];unsignedlongpfn_offset=0;+unsignedlongmax_pfn=0;intnode;max_dma=virt_to_phys((void*)MAX_DMA_ADDRESS)>>PAGE_SHIFT;
@@ -746,47 +746,18 @@ void __init paging_init(void)#endiffor_each_online_node(node){-memset(zones_size,0,sizeof(zones_size));-memset(zholes_size,0,sizeof(zholes_size));-num_physpages+=mem_data[node].num_physpages;--if(mem_data[node].min_pfn>=max_dma){-/* All of this node's memory is above ZONE_DMA */-zones_size[ZONE_NORMAL]=mem_data[node].max_pfn--mem_data[node].min_pfn;-zholes_size[ZONE_NORMAL]=mem_data[node].max_pfn--mem_data[node].min_pfn--mem_data[node].num_physpages;-}elseif(mem_data[node].max_pfn<max_dma){-/* All of this node's memory is in ZONE_DMA */-zones_size[ZONE_DMA]=mem_data[node].max_pfn--mem_data[node].min_pfn;-zholes_size[ZONE_DMA]=mem_data[node].max_pfn--mem_data[node].min_pfn--mem_data[node].num_dma_physpages;-}else{-/* This node has memory in both zones */-zones_size[ZONE_DMA]=max_dma--mem_data[node].min_pfn;-zholes_size[ZONE_DMA]=zones_size[ZONE_DMA]--mem_data[node].num_dma_physpages;-zones_size[ZONE_NORMAL]=mem_data[node].max_pfn--max_dma;-zholes_size[ZONE_NORMAL]=zones_size[ZONE_NORMAL]--(mem_data[node].num_physpages--mem_data[node].num_dma_physpages);-}-pfn_offset=mem_data[node].min_pfn;#ifdef CONFIG_VIRTUAL_MEM_MAPNODE_DATA(node)->node_mem_map=vmem_map+pfn_offset;#endif-free_area_init_node(node,NODE_DATA(node),zones_size,-pfn_offset,zholes_size);+if(mem_data[node].max_pfn>max_pfn)+max_pfn=mem_data[node].max_pfn;}+free_area_init_nodes(max_dma,max_dma,max_pfn,max_pfn);+zero_page_memmap_ptr=virt_to_page(ia64_imva(empty_zero_page));}
The x86_64 code accounted for memmap and some portions of the the DMA zone
as holes. This was because those areas would never be reclaimed and accounting
for them as memory affects min watermarks. This patch will account for the
memmap as a memory hole. Architectures may optionally use set_dma_reserve() if
they wish to account for a portion of memory in ZONE_DMA as a hole.
arch/x86_64/mm/init.c | 4 +
include/linux/mm.h | 1
mm/page_alloc.c | 96 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 1 deletion(-)
Signed-off-by: Mel Gorman <redacted>
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.17-mm6-105-ia64_use_init_nodes/arch/x86_64/mm/init.c linux-2.6.17-mm6-106-account_kernel_mmap/arch/x86_64/mm/init.c
@@ -87,6 +87,7 @@ int min_free_kbytes = 1024;unsignedlong__meminitdatanr_kernel_pages;unsignedlong__meminitdatanr_all_pages;+unsignedlong__initdatadma_reserve;#ifdef CONFIG_ARCH_POPULATES_NODE_MAP/*
@@ -2300,6 +2301,20 @@ unsigned long __init zone_absent_pages_iarch_zone_lowest_possible_pfn[zone_type],arch_zone_highest_possible_pfn[zone_type]);}++/* Return the zone index a PFN is in */+intmemmap_zone_idx(structpage*lmem_map)+{+inti;+unsignedlongphys_addr=virt_to_phys(lmem_map);+unsignedlongpfn=phys_addr>>PAGE_SHIFT;++for(i=0;i<MAX_NR_ZONES;i++)+if(pfn<arch_zone_highest_possible_pfn[i])+break;++returni;+}#elsestaticinlineunsignedlongzone_spanned_pages_in_node(intnid,unsignedlongzone_type,
@@ -2317,6 +2332,11 @@ static inline unsigned long zone_absent_returnzholes_size[zone_type];}++staticinlineintmemmap_zone_idx(structpage*lmem_map)+{+returnMAX_NR_ZONES;+}#endifstaticvoid__initcalculate_node_totalpages(structpglist_data*pgdat,
@@ -2340,6 +2360,58 @@ static void __init calculate_node_totalprealtotalpages);}+#ifdef CONFIG_FLAT_NODE_MEM_MAP+/* Account for mem_map for CONFIG_FLAT_NODE_MEM_MAP */+unsignedlong__meminitaccount_memmap(structpglist_data*pgdat,+intzone_index)+{+unsignedlongpages=0;+if(zone_index==memmap_zone_idx(pgdat->node_mem_map)){+pages=pgdat->node_spanned_pages;+pages=(pages*sizeof(structpage))>>PAGE_SHIFT;+printk(KERN_DEBUG"%lu pages used for memmap\n",pages);+}+returnpages;+}+#else+/* Account for mem_map for CONFIG_SPARSEMEM */+unsignedlongaccount_memmap(structpglist_data*pgdat,intzone_index)+{+unsignedlongpages=0;+unsignedlongmemmap_pfn;+structpage*memmap_addr;+intpnum;+unsignedlongpgdat_startpfn,pgdat_endpfn;+structmem_section*section;++pgdat_startpfn=pgdat->node_start_pfn;+pgdat_endpfn=pgdat_startpfn+pgdat->node_spanned_pages;++/* Go through valid sections looking for memmap */+for(pnum=0;pnum<NR_MEM_SECTIONS;pnum++){+if(!valid_section_nr(pnum))+continue;++section=__nr_to_section(pnum);+if(!section_has_mem_map(section))+continue;++memmap_addr=__section_mem_map_addr(section);+memmap_pfn=(unsignedlong)memmap_addr>>PAGE_SHIFT;++if(memmap_pfn<pgdat_startpfn||memmap_pfn>=pgdat_endpfn)+continue;++if(zone_index==memmap_zone_idx(memmap_addr))+pages+=(PAGES_PER_SECTION*sizeof(structpage));+}++pages>>=PAGE_SHIFT;+printk(KERN_DEBUG"%lu pages used for SPARSE memmap\n",pages);+returnpages;+}+#endif+/**Setupthezonedatastructures:*-markallpagesreserved
On (10/07/06 12:30), David Howells didst pronounce:
Mel Gorman [off-list ref] wrote:
quoted
+unsigned long __initdata dma_reserve;
Should this be static? Or should it be predeclared in a header file
somewhere?
It should be static as it's set by set_dma_reserve(). Thanks.
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.17-mm6-106-account_kernel_mmap/mm/page_alloc.c linux-2.6.17-mm6-107-fixstatic/mm/page_alloc.c
On Sat, Jul 08, 2006 at 12:10:42PM +0100, Mel Gorman wrote:
There are differences in the zone sizes for x86_64 as the arch-specific code
for x86_64 accounts the kernel image and the starting mem_maps as memory
holes but the architecture-independent code accounts the memory as present.
Shouldn't this be the same for all architectures? Or to put it in other words:
why does only x86_64 account the kernel image as memory hole?
On Sat, Jul 08, 2006 at 12:10:42PM +0100, Mel Gorman wrote:
quoted
There are differences in the zone sizes for x86_64 as the arch-specific code
for x86_64 accounts the kernel image and the starting mem_maps as memory
holes but the architecture-independent code accounts the memory as present.
Shouldn't this be the same for all architectures?
The comment in the mail is inaccurate because patch 6/6 will account for
the kernel image and mem_map as holes for all architectures if it is
merged. The patch could be submitted independent of arch-independent
zone-sizing.
Or to put it in other words:
why does only x86_64 account the kernel image as memory hole?
From Andi Kleen's mails in the thread "[PATCH 0/5] Sizing zones and holes
in an architecture independent manner V7"
quoted
quoted
Begin extract <<<
Why is it a performance regression if the image and memmap is accounted
for as holes? How are those regions different from any other kernel
allocation or bootmem allocations for example which are not accounted as
holes?
They are comparatively big and cannot be freed.
If you are sure that it makes a measurable difference to performance,
There was at least one benchmark/use case where it made a significant
difference, can't remember the exact numbers though.
It affects the low/high water marks in the VM zone balancer.
Especially for the 16MB DMA zone it can make a difference if you
account 4MB kernel in there or not.
quoted
quoted
End extract <<<
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab