This is V9 of the patchset to size zones and memory holes in an
architecture-independent manner. It booted successfully on 5 different
machines (arches were x86, x86_64, ppc64 and ia64) in a number of different
configurations and successfully built a kernel. If it fails on any machine,
booting with loglevel=8 and the console log should tell me what went wrong.
Changelog since V8
o Rebase to 2.6.17-rc4-mm1
o Make dma_reserve static
o Use enumerated zone numbers
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 - 139 arch-specific LOC removed
Patch 3 changes x86 to use the mechanism - 136 arch-specific LOC removed
Patch 4 changes x86_64 to use the mechanism - 74 arch-specific LOC removed
Patch 5 changes ia64 to use the mechanism - 52 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
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 a sizable reduction of
architecture-specific code, some of which is very hairy. There should be a
greater 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 | 47 +++
include/linux/mmzone.h | 10
mm/page_alloc.c | 552 ++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 584 insertions(+), 25 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.18-rc4-mm2-clean/include/linux/mm.h linux-2.6.18-rc4-mm2-101-add_free_area_init_nodes/include/linux/mm.h
@@ -103,6 +105,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){
@@ -2067,6 +2077,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;+enumzone_typei;++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
@@ -2174,13 +2455,236 @@ 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(unsignedlong*max_zone_pfn)+{+unsignedlongnid;+enumzone_typei;++/* 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[0]=find_min_pfn_with_active_regions();+arch_zone_highest_possible_pfn[0]=max_zone_pfn[0];+for(i=1;i<MAX_NR_ZONES;i++){+arch_zone_lowest_possible_pfn[i]=+arch_zone_highest_possible_pfn[i-1];+arch_zone_highest_possible_pfn[i]=+max(max_zone_pfn[i],arch_zone_lowest_possible_pfn[i]);+}++/* 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;
@@ -630,11 +543,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,
@@ -667,19 +580,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++){
@@ -710,44 +611,16 @@ 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);-}+unsignedlongmax_zone_pfns[MAX_NR_ZONES]={+lmb_end_of_DRAM()>>PAGE_SHIFT+};+free_area_init_nodes(max_zone_pfns);}staticint__initearly_numa(char*p)
@@ -369,19 +369,18 @@ 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;+max_zone_pfns[0]=total_lowmem>>PAGE_SHIFT;+max_zone_pfns[1]=total_memory>>PAGE_SHIFT;+#else+max_zone_pfns[0]=total_memory>>PAGE_SHIFT;#endif /* CONFIG_HIGHMEM */--free_area_init(zones_size);+free_area_init_nodes(max_zone_pfns);}void__initmem_init(void)
@@ -55,8 +55,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];staticu8__initdataapicid_to_pxm[MAX_APICID];externvoid*boot_ioremap(unsignedlong,unsignedlong);
@@ -139,47 +137,6 @@ static void __init parse_memory_affinity"enabled and removable":"enabled"));}-/* 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
@@ -291,6 +247,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){
@@ -399,57 +356,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];-}
@@ -157,21 +157,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
@@ -227,6 +212,8 @@ static unsigned long calculate_numa_remaunsignedlongpfn;for_each_online_node(nid){+unsignedold_end_pfn=node_end_pfn[nid];+/**Theacpi/sratnodeinfocanshowhot-addmemroyzones*wherememorycouldbeaddedbutnotcurrentlypresent.
@@ -276,6 +263,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);
@@ -369,45 +357,22 @@ void __init numa_kva_reserve(void)void__initzone_sizes_init(void){intnid;---for_each_online_node(nid){-unsignedlongzones_size[MAX_NR_ZONES]={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-}-}+unsignedlongmax_zone_pfns[MAX_NR_ZONES]={+virt_to_phys((char*)MAX_DMA_ADDRESS)>>PAGE_SHIFT,+max_low_pfn,+highend_pfn+};++/* 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);}++free_area_init_nodes(max_zone_pfns);return;}
@@ -342,6 +264,49 @@ void __init e820_mark_nosave_regions(voi}}+/* 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.*/
@@ -404,69 +404,15 @@ 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];-+unsignedlongmax_zone_pfns[MAX_NR_ZONES]={MAX_DMA_PFN,+MAX_DMA32_PFN,+end_pfn};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_zone_pfns);}#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 __init numa_emulation(unsigneprintk(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;}
@@ -166,7 +167,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;
@@ -238,7 +239,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;}
@@ -351,12 +354,12 @@ 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);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
@@ -352,6 +352,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
@@ -654,6 +654,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+=
@@ -678,10 +679,10 @@ 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;+unsignedlongmax_zone_pfns[MAX_NR_ZONES];max_dma=virt_to_phys((void*)MAX_DMA_ADDRESS)>>PAGE_SHIFT;
@@ -698,47 +699,20 @@ 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;}+max_zone_pfns[ZONE_DMA]=max_dma;+max_zone_pfns[ZONE_NORMAL]=max_pfn;+free_area_init_nodes(max_zone_pfns);+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 | 95 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 99 insertions(+), 1 deletion(-)
Signed-off-by: Mel Gorman <redacted>
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm2-105-ia64_use_init_nodes/arch/x86_64/mm/init.c linux-2.6.18-rc4-mm2-106-account_kernel_mmap/arch/x86_64/mm/init.c
@@ -104,6 +104,7 @@ int min_free_kbytes = 1024;unsignedlong__meminitdatanr_kernel_pages;unsignedlong__meminitdatanr_all_pages;+staticunsignedlong__initdatadma_reserve;#ifdef CONFIG_ARCH_POPULATES_NODE_MAP/*
@@ -2303,6 +2304,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,
@@ -2320,6 +2335,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,
@@ -2343,6 +2363,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
From: Keith Mannthey <hidden> Date: 2006-08-21 18:53:15
On 8/21/06, Mel Gorman [off-list ref] wrote:
This is V9 of the patchset to size zones and memory holes in an
architecture-independent manner. It booted successfully on 5 different
machines (arches were x86, x86_64, ppc64 and ia64) in a number of different
configurations and successfully built a kernel. If it fails on any machine,
booting with loglevel=8 and the console log should tell me what went wrong.
I am wondering why this new api didn't cleanup the pfn_to_nid code
path as well. Arches are left to still keep another set of
nid-start-end info around. We are sending info like
add_active_range(unsigned int nid, unsigned long start_pfn, unsigned
long end_pfn)
With this info making a common pnf_to_nid seems to be of intrest so we
don't have to keep redundant information in both generic and arch
specific data structures.
Are you intending the hot-add memory code path to call add_active_range or ???
Thanks,
Keith
This is V9 of the patchset to size zones and memory holes in an
architecture-independent manner. It booted successfully on 5 different
machines (arches were x86, x86_64, ppc64 and ia64) in a number of different
configurations and successfully built a kernel. If it fails on any machine,
booting with loglevel=8 and the console log should tell me what went wrong.
I am wondering why this new api didn't cleanup the pfn_to_nid code
path as well. Arches are left to still keep another set of
nid-start-end info around. We are sending info like
pfn_to_nid() is used at runtime and the early_node_map[] is deleted by
then. As this step, I only want to get the initialisation correct. What
can be replaced is the architecture-specific early_pfn_to_nid() function
which I did for power and x86.
add_active_range(unsigned int nid, unsigned long start_pfn, unsigned
long end_pfn)
With this info making a common pnf_to_nid seems to be of intrest so we
don't have to keep redundant information in both generic and arch
specific data structures.
To implement a common one of interest, the array would have to be
converted to a linked list at the end of boot so it could be modified by
memory hot-add, then pfn_to_nid() would walk the linked list rather than
the existing array. pfn_valid() would probably be replaced as well.
However, this is going to be slower (if more accurate in some cases) than
the existing pfn_valid() and so I would treat it as a separate issue.
Are you intending the hot-add memory code path to call add_active_range or
???
Not at this time. I want to make sure the memory initialisation is right
before dealing with additional complications.
Thanks,
Keith
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
From: Keith Mannthey <hidden> Date: 2006-08-30 20:57:28
On 8/21/06, Mel Gorman [off-list ref] wrote:
Size zones and holes in an architecture independent manner for x86_64.
Hey Mel,
I am having some trouble with the srat.c changes. I keep running into
"SRAT: Hotplug area has existing memory" so am am taking more throught
look at this patch.
I am working on 2.6.18-rc4-mm3 x86_64.
srat.c is doing some sanity checking against the e820 and hot-add
memory ranges. BIOS folk aren't to be trusted with the SRAT. Calling
remove_all_active_ranges before acpi_numa_init leaves nothing to fall
back onto if the SRAT is bad. (see bad_srat()). What should happen
when we discard the srat info?
i386 code may have similar fallback logic (haven't been there in a while)
also
@@ -166,7 +167,7 @@ static int hotadd_enough_memory(struct b if (mem < 0) return 0;- 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) { unsigned long range;
@@ -238,7 +239,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; }
We really do want to to compare against the e820 map at it contains
the memory that is really present (this info was blown away before
acpi_numa) Anyway I fixed up to have the current chunk added
(e820_register_active_regions) after calling this code so it logicaly
makes sense but it still trip over the check. I am not sure what you
are printing out in you debug code but dosen't look like pfns or
phys_addresses but maybe it can tell us why the check fails.
On (30/08/06 13:57), Keith Mannthey didst pronounce:
On 8/21/06, Mel Gorman [off-list ref] wrote:
quoted
Size zones and holes in an architecture independent manner for x86_64.
Hey Mel,
Hi Keith.
I am having some trouble with the srat.c changes. I keep running into
"SRAT: Hotplug area has existing memory" so am am taking more throught
look at this patch.
I am working on 2.6.18-rc4-mm3 x86_64.
ok, great. How much physical memory is installed on the machine? I want to
determine if the "usable" entries in the e820 map contain physical memory
or not.
srat.c is doing some sanity checking against the e820 and hot-add
memory ranges. BIOS folk aren't to be trusted with the SRAT. Calling
remove_all_active_ranges before acpi_numa_init leaves nothing to fall
back onto if the SRAT is bad. (see bad_srat()). What should happen
when we discard the srat info?
When the SRAT is bad, the information is discarded and discovered by an
alternative method later in the boot process.
In this case, numa_initmem_init() is called after acpi_numa_init(). It
calls acpi_scan_nodes() which returns -1 because the SRAT is bad. Once
that happens, either k8_scan_nodes() will be called and the regions
discovered there or if that is not possible, it'll fall through and
e820_register_active_regions will be called without any node awareness.
i386 code may have similar fallback logic (haven't been there in a while)
@@ -84,6 +84,7 @@ static __init void bad_srat(void) apicid_to_node[i] = NUMA_NO_NODE; for (i = 0; i < MAX_NUMNODES; i++) nodes_add[i].start = nodes[i].end = 0;+ remove_all_active_ranges();
}
We go back to setup_arch with no active areas?
Yes, and it'll be discovered using an alternative method later. There is
no point returning to setup_arch with known bad information about active
areas.
quoted
static __init inline int srat_disabled(void)
@@ -166,7 +167,7 @@ static int hotadd_enough_memory(struct b if (mem < 0) return 0;- 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) {
unsigned long range;
@@ -238,7 +239,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; }
We really do want to to compare against the e820 map at it contains
the memory that is really present (this info was blown away before
acpi_numa)
The information used by absent_pages_in_range() should match what was
available to e820_hole_size().
Anyway I fixed up to have the current chunk added
(e820_register_active_regions) after calling this code so it logicaly
makes sense but it still trip over the check.
I am not sure what you
are printing out in you debug code but dosen't look like pfns or
phys_addresses but maybe it can tell us why the check fails.
My debug code for add_active_range() printing out pfns but I spotted one
case where absent_pages_in_range(I) does not do what one would expect.
Lets say the ranges with physical memory was 0->1000 and 2000-3000 (in
pfns). absent_pages_in_range(0, 3000) would return 1000 as you'd expect but
absent_pages_in_range(5000-6000) would return 0! I have a patch that might
fix this at the end of the mail but I'm not sure it's the problem you are
hitting. In the bootlog, I see;
SRAT: Node 0 PXM 0 0-80000000
Entering add_active_range(0, 0, 152) 0 entries of 3200 used
Entering add_active_range(0, 256, 524165) 1 entries of 3200 used
SRAT: Node 0 PXM 0 0-470000000
Entering add_active_range(0, 0, 152) 2 entries of 3200 used
Entering add_active_range(0, 256, 524165) 2 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 3200 used
SRAT: Node 0 PXM 0 0-1070000000
SRAT: Hotplug area has existing memory
The last part (0-1070000000) is checked as a hotplug area but it's clear
that memory exists in that range. As reserve_hotadd() requires that the
whole range be a hole, I'm having trouble seeing how it ever successfully
reserved unless the ranges going into reserve_hotadd() are something other
than the pfn range for 0-1070000000). The patch later will print out the
range used by reserve_hotadd() so we can see.
A node chunk in this section of code may be a hot-pluggable zone. With
MEMORY_HOTPLUG_SPARSE we don't want to register these regions.
The ranges should not get registered as active memory by
e820_register_active_regions() unless they are marked E820_RAM. My
understanding is that the regions for hotadd would be marked "reserved"
in the e820 map. Is that wrong?
I have but the e820_register_active_regions as a else to this
statment the absent pages check fails.
The patch below omits this change because I think
e820_register_active_regions() will still have got called by the time
you encounter a hotplug area.
Also nodes_cover_memory and alot of these check were based against
comparing the srat data against the e820. Now all this code is
comparing SRAT against SRAT....
I don't see why. The SRAT table passes a range to
e820_register_active_regions() so should be comparing SRAT to e820
I am willing to help here but we should compare the SRAT against to
e820. Table v. Table.
What to you think should be done?
Can you read through this patch and see does it address the problem in any
way? If it doesn't, can you send a complete bootlog so I can see what is
being sent to reserve_hotadd()? Thanks
Signed-off-by: Mel Gorman <redacted>
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm3-clean/arch/x86_64/mm/srat.c linux-2.6.18-rc4-mm3-fix_x8664_hotadd/arch/x86_64/mm/srat.c
@@ -240,7 +240,8 @@ static int reserve_hotadd(int node, unsi/* This check might be a bit too strict, but I'm keeping it for now. */if(absent_pages_in_range(s_pfn,e_pfn)!=e_pfn-s_pfn){-printk(KERN_ERR"SRAT: Hotplug area has existing memory\n");+printk(KERN_ERR"SRAT: Hotplug area %lu -> %lu has existing memory\n",+s_pfn,e_pfn);return-1;}
@@ -2280,6 +2280,10 @@ unsigned long __init __absent_pages_in_rprev_end_pfn=early_node_map[i].end_pfn;}+/* If the range is outside of physical memory, return the range */+if(range_start_pfn>prev_end_pfn)+hole_pages=range_end_pfn-range_start_pfn;+returnhole_pages;}
From: Keith Mannthey <hidden> Date: 2006-08-31 17:52:30
On 8/31/06, Mel Gorman [off-list ref] wrote:
On (30/08/06 13:57), Keith Mannthey didst pronounce:
quoted
On 8/21/06, Mel Gorman [off-list ref] wrote:
quoted
ok, great. How much physical memory is installed on the machine? I want to
determine if the "usable" entries in the e820 map contain physical memory
or not.
Usable entries in the e820 contian memory. I have about 20-24gb
depending on config.
When the SRAT is bad, the information is discarded and discovered by an
alternative method later in the boot process.
In this case, numa_initmem_init() is called after acpi_numa_init(). It
calls acpi_scan_nodes() which returns -1 because the SRAT is bad. Once
that happens, either k8_scan_nodes() will be called and the regions
discovered there or if that is not possible, it'll fall through and
e820_register_active_regions will be called without any node awareness.
sorry I have missed some of the logic in this patch.
I see now in numa_initmem_init that if no numa setup is found it calls
e820_register_active_regions(0, start_pfn, end_pfn) again.
So if the srat is discard it runs the e820 code again.
@@ -84,6 +84,7 @@ static __init void bad_srat(void) apicid_to_node[i] = NUMA_NO_NODE; for (i = 0; i < MAX_NUMNODES; i++) nodes_add[i].start = nodes[i].end = 0;+ remove_all_active_ranges();
}
We go back to setup_arch with no active areas?
Yes, and it'll be discovered using an alternative method later. There is
no point returning to setup_arch with known bad information about active
areas.
Totally agreeded! I just didn't the the fallback path.
quoted
quoted
static __init inline int srat_disabled(void)
@@ -166,7 +167,7 @@ static int hotadd_enough_memory(struct b if (mem < 0) return 0;- 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) {
unsigned long range;
@@ -238,7 +239,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; }
We really do want to to compare against the e820 map at it contains
the memory that is really present (this info was blown away before
acpi_numa)
The information used by absent_pages_in_range() should match what was
available to e820_hole_size().
Is absent_pages_in_range a check against the e820 or the
add_pages_to_range calls?
quoted
Anyway I fixed up to have the current chunk added
(e820_register_active_regions) after calling this code so it logicaly
makes sense but it still trip over the check.
I am not sure what you
are printing out in you debug code but dosen't look like pfns or
phys_addresses but maybe it can tell us why the check fails.
My debug code for add_active_range() printing out pfns but I spotted one
case where absent_pages_in_range(I) does not do what one would expect.
Lets say the ranges with physical memory was 0->1000 and 2000-3000 (in
pfns). absent_pages_in_range(0, 3000) would return 1000 as you'd expect but
absent_pages_in_range(5000-6000) would return 0! I have a patch that might
fix this at the end of the mail but I'm not sure it's the problem you are
hitting. In the bootlog, I see;
SRAT: Node 0 PXM 0 0-80000000
Entering add_active_range(0, 0, 152) 0 entries of 3200 used
Entering add_active_range(0, 256, 524165) 1 entries of 3200 used
SRAT: Node 0 PXM 0 0-470000000
Entering add_active_range(0, 0, 152) 2 entries of 3200 used
Entering add_active_range(0, 256, 524165) 2 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 3200 used
SRAT: Node 0 PXM 0 0-1070000000
SRAT: Hotplug area has existing memory
The last part (0-1070000000) is checked as a hotplug area but it's clear
that memory exists in that range. As reserve_hotadd() requires that the
whole range be a hole, I'm having trouble seeing how it ever successfully
reserved unless the ranges going into reserve_hotadd() are something other
than the pfn range for 0-1070000000). The patch later will print out the
range used by reserve_hotadd() so we can see.
No the whole node is 0-1070000000 the hot add range is 470000000-1070000000
reserve_hotadd is called with start and end not nd->start nd->end.
470000000-1070000000 sould be empty.
A node chunk in this section of code may be a hot-pluggable zone. With
MEMORY_HOTPLUG_SPARSE we don't want to register these regions.
The ranges should not get registered as active memory by
e820_register_active_regions() unless they are marked E820_RAM. My
understanding is that the regions for hotadd would be marked "reserved"
in the e820 map. Is that wrong?
This is wrong. In a mult-node system that last node add area will not
be marked reserved by the e820. The e820 only defines memory <
end_pfn. the last node add area is > end_pfn.
With RESERVE based add-memory you want the add-areas repored by the
srat to be setup during boot like all the other pages.
I have but the e820_register_active_regions as a else to this
statment the absent pages check fails.
The patch below omits this change because I think
e820_register_active_regions() will still have got called by the time
you encounter a hotplug area.
called but then removed in setup arch.
quoted
Also nodes_cover_memory and alot of these check were based against
comparing the srat data against the e820. Now all this code is
comparing SRAT against SRAT....
I don't see why. The SRAT table passes a range to
e820_register_active_regions() so should be comparing SRAT to e820
let me go off and look at e820_register_active_regions() some more.
quoted
I am willing to help here but we should compare the SRAT against to
e820. Table v. Table.
What to you think should be done?
Can you read through this patch and see does it address the problem in any
way? If it doesn't, can you send a complete bootlog so I can see what is
being sent to reserve_hotadd()? Thanks
Sure thing. It is just the hot-add area I am guessing it is an off by
one error of some sort.
What is all this code buying us? Since this code dosen't appear to do
anything to help the arch out (just increases it's vm boot code
complexity a little) maybe insead of weaving
e820_register_active_regions() calls throught out the boot process you
should just waint untill things are sorted out and do a quick scan of
node data that has been setup at the end?
What are the future plans for this api?
Thanks,
Keith u
On (30/08/06 13:57), Keith Mannthey didst pronounce:
quoted
On 8/21/06, Mel Gorman [off-list ref] wrote:
quoted
quoted
ok, great. How much physical memory is installed on the machine? I want to
determine if the "usable" entries in the e820 map contain physical memory
or not.
Usable entries in the e820 contian memory. I have about 20-24gb
depending on config.
ok, that seems to match the (usable) regions in the e820 map.
quoted
When the SRAT is bad, the information is discarded and discovered by an
alternative method later in the boot process.
In this case, numa_initmem_init() is called after acpi_numa_init(). It
calls acpi_scan_nodes() which returns -1 because the SRAT is bad. Once
that happens, either k8_scan_nodes() will be called and the regions
discovered there or if that is not possible, it'll fall through and
e820_register_active_regions will be called without any node awareness.
sorry I have missed some of the logic in this patch.
I see now in numa_initmem_init that if no numa setup is found it calls
e820_register_active_regions(0, start_pfn, end_pfn) again.
right.
So if the srat is discard it runs the e820 code again.
@@ -84,6 +84,7 @@ static __init void bad_srat(void) apicid_to_node[i] = NUMA_NO_NODE; for (i = 0; i < MAX_NUMNODES; i++) nodes_add[i].start = nodes[i].end = 0;+ remove_all_active_ranges();
}
We go back to setup_arch with no active areas?
Yes, and it'll be discovered using an alternative method later. There is
no point returning to setup_arch with known bad information about active
areas.
Totally agreeded! I just didn't the the fallback path.
grand.
quoted
quoted
quoted
static __init inline int srat_disabled(void)
@@ -166,7 +167,7 @@ static int hotadd_enough_memory(struct b if (mem < 0) return 0;- 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) {
unsigned long range;
@@ -238,7 +239,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; }
We really do want to to compare against the e820 map at it contains
the memory that is really present (this info was blown away before
acpi_numa)
The information used by absent_pages_in_range() should match what was
available to e820_hole_size().
Is absent_pages_in_range a check against the e820 or the
add_pages_to_range calls?
absent_pages_in_range() uses information provided via add_active_range()
and on x86_64, add_active_range() is called based on information in the
e820.
quoted
quoted
Anyway I fixed up to have the current chunk added
(e820_register_active_regions) after calling this code so it logicaly
makes sense but it still trip over the check.
I am not sure what you
are printing out in you debug code but dosen't look like pfns or
phys_addresses but maybe it can tell us why the check fails.
My debug code for add_active_range() printing out pfns but I spotted one
case where absent_pages_in_range(I) does not do what one would expect.
Lets say the ranges with physical memory was 0->1000 and 2000-3000 (in
pfns). absent_pages_in_range(0, 3000) would return 1000 as you'd expect
but
absent_pages_in_range(5000-6000) would return 0! I have a patch that might
fix this at the end of the mail but I'm not sure it's the problem you are
hitting. In the bootlog, I see;
SRAT: Node 0 PXM 0 0-80000000
Entering add_active_range(0, 0, 152) 0 entries of 3200 used
Entering add_active_range(0, 256, 524165) 1 entries of 3200 used
SRAT: Node 0 PXM 0 0-470000000
Entering add_active_range(0, 0, 152) 2 entries of 3200 used
Entering add_active_range(0, 256, 524165) 2 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 3200 used
SRAT: Node 0 PXM 0 0-1070000000
SRAT: Hotplug area has existing memory
quoted
The last part (0-1070000000) is checked as a hotplug area but it's clear
that memory exists in that range. As reserve_hotadd() requires that the
whole range be a hole, I'm having trouble seeing how it ever successfully
reserved unless the ranges going into reserve_hotadd() are something other
than the pfn range for 0-1070000000). The patch later will print out the
range used by reserve_hotadd() so we can see.
No the whole node is 0-1070000000 the hot add range is 470000000-1070000000
reserve_hotadd is called with start and end not nd->start nd->end.
470000000-1070000000 sould be empty.
Can you confirm that happens by applying the patch I sent to you and
checking the output? When the reserve fails, it should print out what
range it actually checked. I want to be sure it's not checking the
addresses 0->0x1070000000
A node chunk in this section of code may be a hot-pluggable zone. With
MEMORY_HOTPLUG_SPARSE we don't want to register these regions.
The ranges should not get registered as active memory by
e820_register_active_regions() unless they are marked E820_RAM. My
understanding is that the regions for hotadd would be marked "reserved"
in the e820 map. Is that wrong?
This is wrong. In a mult-node system that last node add area will not
be marked reserved by the e820. The e820 only defines memory <
end_pfn. the last node add area is > end_pfn.
ok, that should still be fine. As long as the ranges are not marked
"usable", add_active_range() will not be called and the holes should be
counted correctly with the patch I sent you.
With RESERVE based add-memory you want the add-areas repored by the
srat to be setup during boot like all the other pages.
So, do you actally expect a lot of unused mem_map to be allocated with
struct pages that are inactive until memory is hot-added in an
x86_64-specific manner? The arch-independent stuff currently will not do
that. It sets up memmap for where memory really exists. If that is not
what you expect, it will hit issues at hotadd time which is not the
current issue but one that can be fixed.
quoted
quoted
quoted
if (ma->flags.hot_pluggable && !reserve_hotadd(node, start, end)
<
quoted
quoted
0) {
/* Ignore hotadd region. Undo damage */
I have but the e820_register_active_regions as a else to this
statment the absent pages check fails.
The patch below omits this change because I think
e820_register_active_regions() will still have got called by the time
you encounter a hotplug area.
called but then removed in setup arch.
By "removed", I assume you mean the active regions removed by the call
to remove_all_active_regions() in setup_arch(). Before reserve_hotadd() is
called, e820_register_active_regions() will have reregistered the active
regions with the NUMA node id.
quoted
quoted
Also nodes_cover_memory and alot of these check were based against
comparing the srat data against the e820. Now all this code is
comparing SRAT against SRAT....
I don't see why. The SRAT table passes a range to
e820_register_active_regions() so should be comparing SRAT to e820
let me go off and look at e820_register_active_regions() some more.
Cool
quoted
quoted
I am willing to help here but we should compare the SRAT against to
e820. Table v. Table.
What to you think should be done?
Can you read through this patch and see does it address the problem in any
way? If it doesn't, can you send a complete bootlog so I can see what is
being sent to reserve_hotadd()? Thanks
Sure thing. It is just the hot-add area I am guessing it is an off by
one error of some sort.
Possible, the change to reserve_hotadd() should tell me.
What is all this code buying us?
Less architecture-specific code across a number of architectures is the
main one.
Since this code dosen't appear to do
anything to help the arch out (just increases it's vm boot code
complexity a little) maybe insead of weaving
e820_register_active_regions() calls throught out the boot process you
should just waint untill things are sorted out and do a quick scan of
node data that has been setup at the end?
That would defeat the purpose of sizing zones and holes in an architecture
independent manner.
What are the future plans for this api?
In the future, I will be releasing patches that set aside a zone (similar
to the Solaris Kernel Cage) used for easily-reclaimed pages that can be
used for growing the huge page pool at runtime (it comes under the heading
of anti-fragmentation) work. The same zone could also be used to give
memory hot-remove a better success rate than 0%. These patches make the
creation of the zone relatively trivial. Without them, the
architecture-specific code is really hairy.
Other possibilities are doing stuff like handling the mem= boot parameter
in an architecture-independent manner. My understanding is that some NUMA
architectures get the handling of that arguement wrong.
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
From: Keith Mannthey <hidden> Date: 2006-09-01 03:09:00
On 8/31/06, Mel Gorman [off-list ref] wrote:
On Thu, 31 Aug 2006, Keith Mannthey wrote:
quoted
On 8/31/06, Mel Gorman [off-list ref] wrote:
quoted
On (30/08/06 13:57), Keith Mannthey didst pronounce:
quoted
On 8/21/06, Mel Gorman [off-list ref] wrote:
quoted
Can you confirm that happens by applying the patch I sent to you and
checking the output? When the reserve fails, it should print out what
range it actually checked. I want to be sure it's not checking the
addresses 0->0x1070000000
A node chunk in this section of code may be a hot-pluggable zone. With
MEMORY_HOTPLUG_SPARSE we don't want to register these regions.
The ranges should not get registered as active memory by
e820_register_active_regions() unless they are marked E820_RAM. My
understanding is that the regions for hotadd would be marked "reserved"
in the e820 map. Is that wrong?
This is wrong. In a mult-node system that last node add area will not
be marked reserved by the e820. The e820 only defines memory <
end_pfn. the last node add area is > end_pfn.
ok, that should still be fine. As long as the ranges are not marked
"usable", add_active_range() will not be called and the holes should be
counted correctly with the patch I sent you.
quoted
With RESERVE based add-memory you want the add-areas repored by the
srat to be setup during boot like all the other pages.
So, do you actally expect a lot of unused mem_map to be allocated with
struct pages that are inactive until memory is hot-added in an
x86_64-specific manner? The arch-independent stuff currently will not do
that. It sets up memmap for where memory really exists. If that is not
what you expect, it will hit issues at hotadd time which is not the
current issue but one that can be fixed.
Yes. RESERVED based is a big waste of mem_map space. The add areas
are marked as RESERVED during boot and then later onlined during add.
It might be ok. I will play with tomorrow. I might just need to
call add_active_range in the right spot :)
quoted
quoted
quoted
quoted
if (ma->flags.hot_pluggable && !reserve_hotadd(node, start, end)
<
quoted
quoted
0) {
/* Ignore hotadd region. Undo damage */
I have but the e820_register_active_regions as a else to this
statment the absent pages check fails.
The patch below omits this change because I think
e820_register_active_regions() will still have got called by the time
you encounter a hotplug area.
called but then removed in setup arch.
By "removed", I assume you mean the active regions removed by the call
to remove_all_active_regions() in setup_arch(). Before reserve_hotadd() is
called, e820_register_active_regions() will have reregistered the active
regions with the NUMA node id.
I see e820_register_active_regions is acting as a filter against the e820
quoted
quoted
quoted
Also nodes_cover_memory and alot of these check were based against
comparing the srat data against the e820. Now all this code is
comparing SRAT against SRAT....
I don't see why. The SRAT table passes a range to
e820_register_active_regions() so should be comparing SRAT to e820
let me go off and look at e820_register_active_regions() some more.
Things get clear :)
Should be ok.
quoted
Sure thing. It is just the hot-add area I am guessing it is an off by
one error of some sort.
See below. I do my e820_register_active_area as an else to to if
(hotplug.....!reserve) and the prink is easy to sort out.
I see your pfn are in base 10. Looks like it considers the last
addres to be a present page. (off by one thing).
Thanks,
Keith
Output below
disabling early console
Linux version 2.6.18-rc4-mm3-smp (root@elm3a153) (gcc version 4.1.0
(SUSE Linux)) #6 SMP Thu Aug 31 22:06:00 EDT 2006
Command line: root=/dev/sda3
ip=9.47.66.153:9.47.66.169:9.47.66.1:255.255.255.0 resume=/dev/sda2
showopts earlyprintk=ttyS0,115200 console=ttyS0,115200 console=tty0
debug numa=hotadd=100
BIOS-provided physical RAM map:
BIOS-e820: 0000000000000000 - 0000000000098400 (usable)
BIOS-e820: 0000000000098400 - 00000000000a0000 (reserved)
BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved)
BIOS-e820: 0000000000100000 - 000000007ff85e00 (usable)
BIOS-e820: 000000007ff85e00 - 000000007ff98880 (ACPI data)
BIOS-e820: 000000007ff98880 - 0000000080000000 (reserved)
BIOS-e820: 00000000fec00000 - 0000000100000000 (reserved)
BIOS-e820: 0000000100000000 - 0000000470000000 (usable)
BIOS-e820: 0000001070000000 - 0000001160000000 (usable)
Entering add_active_range(0, 0, 152) 0 entries of 3200 used
Entering add_active_range(0, 256, 524165) 1 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 3200 used
Entering add_active_range(0, 17235968, 18219008) 3 entries of 3200 used
end_pfn_map = 18219008
DMI 2.3 present.
ACPI: RSDP (v000 IBM ) @ 0x00000000000fdcf0
ACPI: RSDT (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff98800
ACPI: FADT (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff98780
ACPI: MADT (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff98600
ACPI: SRAT (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff983c0
ACPI: HPET (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff98380
ACPI: SSDT (v001 IBM VIGSSDT0 0x00001000 INTL 0x20030122) @
0x000000007ff90780
ACPI: SSDT (v001 IBM VIGSSDT1 0x00001000 INTL 0x20030122) @
0x000000007ff88bc0
ACPI: DSDT (v001 IBM EXA01ZEU 0x00001000 INTL 0x20030122) @
0x0000000000000000
SRAT: PXM 0 -> APIC 0 -> Node 0
SRAT: PXM 0 -> APIC 1 -> Node 0
SRAT: PXM 0 -> APIC 2 -> Node 0
SRAT: PXM 0 -> APIC 3 -> Node 0
SRAT: PXM 0 -> APIC 38 -> Node 0
SRAT: PXM 0 -> APIC 39 -> Node 0
SRAT: PXM 0 -> APIC 36 -> Node 0
SRAT: PXM 0 -> APIC 37 -> Node 0
SRAT: PXM 1 -> APIC 64 -> Node 1
SRAT: PXM 1 -> APIC 65 -> Node 1
SRAT: PXM 1 -> APIC 66 -> Node 1
SRAT: PXM 1 -> APIC 67 -> Node 1
SRAT: PXM 1 -> APIC 102 -> Node 1
SRAT: PXM 1 -> APIC 103 -> Node 1
SRAT: PXM 1 -> APIC 100 -> Node 1
SRAT: PXM 1 -> APIC 101 -> Node 1
SRAT: Node 0 PXM 0 0-80000000
Entering add_active_range(0, 0, 152) 0 entries of 3200 used
Entering add_active_range(0, 256, 524165) 1 entries of 3200 used
SRAT: Node 0 PXM 0 0-470000000
Entering add_active_range(0, 0, 152) 2 entries of 3200 used
Entering add_active_range(0, 256, 524165) 2 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 3200 used
SRAT: Node 0 PXM 0 0-1070000000
reserve_hotadd called with node 0 sart 470000000 end 1070000000
SRAT: Hotplug area has existing memory
Entering add_active_range(0, 0, 152) 3 entries of 3200 used
Entering add_active_range(0, 256, 524165) 3 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 3 entries of 3200 used
SRAT: Node 1 PXM 1 1070000000-1160000000
Entering add_active_range(1, 17235968, 18219008) 3 entries of 3200 used
SRAT: Node 1 PXM 1 1070000000-3200000000
reserve_hotadd called with node 1 sart 1160000000 end 3200000000
SRAT: Hotplug area has existing memory
Entering add_active_range(1, 17235968, 18219008) 4 entries of 3200 used
NUMA: Using 28 for the hash shift.
Bootmem setup node 0 0000000000000000-0000001070000000
Bootmem setup node 1 0000001070000000-0000001160000000
Zone PFN ranges:
DMA 0 -> 4096
DMA32 4096 -> 1048576
Normal 1048576 -> 18219008
early_node_map[4] active PFN ranges
0: 0 -> 152
0: 256 -> 524165
0: 1048576 -> 4653056
1: 17235968 -> 18219008
On node 0 totalpages: 4128541
0 pages used for SPARSE memmap
1149 pages DMA reserved
DMA zone: 2843 pages, LIFO batch:0
0 pages used for SPARSE memmap
DMA32 zone: 520069 pages, LIFO batch:31
0 pages used for SPARSE memmap
Normal zone: 3604480 pages, LIFO batch:31
On node 1 totalpages: 983040
0 pages used for SPARSE memmap
0 pages used for SPARSE memmap
0 pages used for SPARSE memmap
Normal zone: 983040 pages, LIFO batch:31
On (30/08/06 13:57), Keith Mannthey didst pronounce:
quoted
On 8/21/06, Mel Gorman [off-list ref] wrote:
quoted
quoted
Can you confirm that happens by applying the patch I sent to you and
checking the output? When the reserve fails, it should print out what
range it actually checked. I want to be sure it's not checking the
addresses 0->0x1070000000
See below
Perfect, thanks a lot. I should have enough to reproduce without a test
machine what is going on and develop the required patches.
A node chunk in this section of code may be a hot-pluggable zone. With
MEMORY_HOTPLUG_SPARSE we don't want to register these regions.
The ranges should not get registered as active memory by
e820_register_active_regions() unless they are marked E820_RAM. My
understanding is that the regions for hotadd would be marked "reserved"
in the e820 map. Is that wrong?
This is wrong. In a mult-node system that last node add area will not
be marked reserved by the e820. The e820 only defines memory <
end_pfn. the last node add area is > end_pfn.
ok, that should still be fine. As long as the ranges are not marked
"usable", add_active_range() will not be called and the holes should be
counted correctly with the patch I sent you.
quoted
With RESERVE based add-memory you want the add-areas repored by the
srat to be setup during boot like all the other pages.
So, do you actally expect a lot of unused mem_map to be allocated with
struct pages that are inactive until memory is hot-added in an
x86_64-specific manner? The arch-independent stuff currently will not do
that. It sets up memmap for where memory really exists. If that is not
what you expect, it will hit issues at hotadd time which is not the
current issue but one that can be fixed.
Yes. RESERVED based is a big waste of mem_map space.
Right, it's all very clear now. At some point in the future, I'd like to
visit why SPARSEMEM-based hot-add is not always used but it's a separate
issue.
The add areas
are marked as RESERVED during boot and then later onlined during add.
That explains the reserve_bootmem_node()
It might be ok. I will play with tomorrow. I might just need to
call add_active_range in the right spot :)
I'll play with this from the opposite perspective - what is required for
any arch using this API to have spare memmap for reserve-based hot-add.
quoted
quoted
quoted
quoted
quoted
if (ma->flags.hot_pluggable && !reserve_hotadd(node, start,
end)
quoted
quoted
<
quoted
quoted
0) {
/* Ignore hotadd region. Undo damage */
I have but the e820_register_active_regions as a else to this
statment the absent pages check fails.
The patch below omits this change because I think
e820_register_active_regions() will still have got called by the time
you encounter a hotplug area.
called but then removed in setup arch.
By "removed", I assume you mean the active regions removed by the call
to remove_all_active_regions() in setup_arch(). Before reserve_hotadd() is
called, e820_register_active_regions() will have reregistered the active
regions with the NUMA node id.
I see e820_register_active_regions is acting as a filter against the e820
Yes. A range of pfn's in given to e820_register_active_regions() and it
reads the e820 for E820_RAM sections within that range.
quoted
quoted
quoted
quoted
Also nodes_cover_memory and alot of these check were based against
comparing the srat data against the e820. Now all this code is
comparing SRAT against SRAT....
I don't see why. The SRAT table passes a range to
e820_register_active_regions() so should be comparing SRAT to e820
let me go off and look at e820_register_active_regions() some more.
Things get clear :)
Should be ok.
nice.
quoted
quoted
Sure thing. It is just the hot-add area I am guessing it is an off by
one error of some sort.
See below. I do my e820_register_active_area as an else to to if
(hotplug.....!reserve) and the prink is easy to sort out.
yep, it should be easy to put this into a test program.
I see your pfn are in base 10. Looks like it considers the last
addres to be a present page. (off by one thing).
Probably. I'll start poking now. Thanks a lot.
Thanks,
Keith
Output below
disabling early console
Linux version 2.6.18-rc4-mm3-smp (root@elm3a153) (gcc version 4.1.0
(SUSE Linux)) #6 SMP Thu Aug 31 22:06:00 EDT 2006
Command line: root=/dev/sda3
ip=9.47.66.153:9.47.66.169:9.47.66.1:255.255.255.0 resume=/dev/sda2
showopts earlyprintk=ttyS0,115200 console=ttyS0,115200 console=tty0
debug numa=hotadd=100
BIOS-provided physical RAM map:
BIOS-e820: 0000000000000000 - 0000000000098400 (usable)
BIOS-e820: 0000000000098400 - 00000000000a0000 (reserved)
BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved)
BIOS-e820: 0000000000100000 - 000000007ff85e00 (usable)
BIOS-e820: 000000007ff85e00 - 000000007ff98880 (ACPI data)
BIOS-e820: 000000007ff98880 - 0000000080000000 (reserved)
BIOS-e820: 00000000fec00000 - 0000000100000000 (reserved)
BIOS-e820: 0000000100000000 - 0000000470000000 (usable)
BIOS-e820: 0000001070000000 - 0000001160000000 (usable)
Entering add_active_range(0, 0, 152) 0 entries of 3200 used
Entering add_active_range(0, 256, 524165) 1 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 3200 used
Entering add_active_range(0, 17235968, 18219008) 3 entries of 3200 used
end_pfn_map = 18219008
DMI 2.3 present.
ACPI: RSDP (v000 IBM ) @ 0x00000000000fdcf0
ACPI: RSDT (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff98800
ACPI: FADT (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff98780
ACPI: MADT (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff98600
ACPI: SRAT (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff983c0
ACPI: HPET (v001 IBM EXA01ZEU 0x00001000 IBM 0x45444f43) @
0x000000007ff98380
ACPI: SSDT (v001 IBM VIGSSDT0 0x00001000 INTL 0x20030122) @
0x000000007ff90780
ACPI: SSDT (v001 IBM VIGSSDT1 0x00001000 INTL 0x20030122) @
0x000000007ff88bc0
ACPI: DSDT (v001 IBM EXA01ZEU 0x00001000 INTL 0x20030122) @
0x0000000000000000
SRAT: PXM 0 -> APIC 0 -> Node 0
SRAT: PXM 0 -> APIC 1 -> Node 0
SRAT: PXM 0 -> APIC 2 -> Node 0
SRAT: PXM 0 -> APIC 3 -> Node 0
SRAT: PXM 0 -> APIC 38 -> Node 0
SRAT: PXM 0 -> APIC 39 -> Node 0
SRAT: PXM 0 -> APIC 36 -> Node 0
SRAT: PXM 0 -> APIC 37 -> Node 0
SRAT: PXM 1 -> APIC 64 -> Node 1
SRAT: PXM 1 -> APIC 65 -> Node 1
SRAT: PXM 1 -> APIC 66 -> Node 1
SRAT: PXM 1 -> APIC 67 -> Node 1
SRAT: PXM 1 -> APIC 102 -> Node 1
SRAT: PXM 1 -> APIC 103 -> Node 1
SRAT: PXM 1 -> APIC 100 -> Node 1
SRAT: PXM 1 -> APIC 101 -> Node 1
SRAT: Node 0 PXM 0 0-80000000
Entering add_active_range(0, 0, 152) 0 entries of 3200 used
Entering add_active_range(0, 256, 524165) 1 entries of 3200 used
SRAT: Node 0 PXM 0 0-470000000
Entering add_active_range(0, 0, 152) 2 entries of 3200 used
Entering add_active_range(0, 256, 524165) 2 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 3200 used
SRAT: Node 0 PXM 0 0-1070000000
reserve_hotadd called with node 0 sart 470000000 end 1070000000
SRAT: Hotplug area has existing memory
Entering add_active_range(0, 0, 152) 3 entries of 3200 used
Entering add_active_range(0, 256, 524165) 3 entries of 3200 used
Entering add_active_range(0, 1048576, 4653056) 3 entries of 3200 used
SRAT: Node 1 PXM 1 1070000000-1160000000
Entering add_active_range(1, 17235968, 18219008) 3 entries of 3200 used
SRAT: Node 1 PXM 1 1070000000-3200000000
reserve_hotadd called with node 1 sart 1160000000 end 3200000000
SRAT: Hotplug area has existing memory
Entering add_active_range(1, 17235968, 18219008) 4 entries of 3200 used
NUMA: Using 28 for the hash shift.
Bootmem setup node 0 0000000000000000-0000001070000000
Bootmem setup node 1 0000001070000000-0000001160000000
Zone PFN ranges:
DMA 0 -> 4096
DMA32 4096 -> 1048576
Normal 1048576 -> 18219008
early_node_map[4] active PFN ranges
0: 0 -> 152
0: 256 -> 524165
0: 1048576 -> 4653056
1: 17235968 -> 18219008
On node 0 totalpages: 4128541
0 pages used for SPARSE memmap
1149 pages DMA reserved
DMA zone: 2843 pages, LIFO batch:0
0 pages used for SPARSE memmap
DMA32 zone: 520069 pages, LIFO batch:31
0 pages used for SPARSE memmap
Normal zone: 3604480 pages, LIFO batch:31
On node 1 totalpages: 983040
0 pages used for SPARSE memmap
0 pages used for SPARSE memmap
0 pages used for SPARSE memmap
Normal zone: 983040 pages, LIFO batch:31
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
From: Mika Penttilä <hidden> Date: 2006-09-01 08:45:23
Right, it's all very clear now. At some point in the future, I'd like
to visit why SPARSEMEM-based hot-add is not always used but it's a
separate issue.
quoted
The add areas
are marked as RESERVED during boot and then later onlined during add.
That explains the reserve_bootmem_node()
But pages are marked reserved by default. You still have to alloc the
bootmem map for the the whole node range, including reserve hot add
areas and areas beyond e820-end-of-ram. So all the areas are already
reserved, until freed.
--Mika
On (31/08/06 20:08), Keith Mannthey didst pronounce:
quoted
So, do you actally expect a lot of unused mem_map to be allocated with
struct pages that are inactive until memory is hot-added in an
x86_64-specific manner? The arch-independent stuff currently will not do
that. It sets up memmap for where memory really exists. If that is not
what you expect, it will hit issues at hotadd time which is not the
current issue but one that can be fixed.
Yes. RESERVED based is a big waste of mem_map space. The add areas
are marked as RESERVED during boot and then later onlined during add.
It might be ok. I will play with tomorrow. I might just need to
call add_active_range in the right spot :)
Following this mail should be two patches that may address the problem
with reserved memory hot-add. One assumption made by arch-independent
zone-sizing was that the only memory holes of interest were those before the
end of physical memory. Another assumption was that mem_map should only be
allocated for memory that was physically present in the machine.
With MEMORY_HOTPLUG_RESERVE on x86_64, these assumptions do not hold. This
feature expects that mem_map is allocated at boot time and later activated
on a memory hot-add event. To determine if the region is usable for hot-add
in the future, holes are calculated beyond the end of physical memory.
The following two patches fix these two assumptions. They have been boot-tested
on a range of hardware (x86, ppc64, ia64 and x86_64) so there should be no
new regressions.
I don't have access to hardware that can use MEMORY_HOTPLUG_RESERVE so I'd
appreciate hearing if the patches work. I wrote a test program that simulated
the input from the machine the problem was reported on. It registers active
memory and simulates the check made by reserve_hotadd(). push_node_boundaries()
is called to push the end of the node out by 100 pages like what SRAT would
do for reserve hot-add and it appears to do the right thing. Output is below.
mel@arnold:~/patches/brokenout/zonesizing/driver_test$ gcc driver_test.c -o
driver_test && ./driver_test | grep -v "active with" | grep -v
account_node_boundary
Stage 1: Registering active ranges
Entering add_active_range(0, 0, 152) 0 entries of 96 used
Entering add_active_range(0, 256, 524165) 1 entries of 96 used
Entering add_active_range(0, 1048576, 4653056) 2 entries of 96 used
Entering add_active_range(1, 17235968, 18219008) 3 entries of 96 used
Dumping active map
0: 0 0 -> 152
1: 0 256 -> 524165
2: 0 1048576 -> 4653056
3: 1 17235968 -> 18219008
Entering push_node_boundaries(0, 0, 4653156)
Checking reserve-hotadd
absent_pages_in_range(4653056, 17235968) == 17235968 - 4653056 == 12582912
absent_pages_in_range(18219008, 52428800) == 52428800 - 18219008 == 34209792
Stage 2: Calculating zone sizes and holes
Stage 3: Dumping zone sizes and holes
zone_size[0][0] = 4096 zone_holes[0][0] = 104
zone_size[0][1] = 1044480 zone_holes[0][1] = 524411
zone_size[0][2] = 3604580 zone_holes[0][2] = 100
zone_size[1][2] = 983040 zone_holes[1][2] = 0
Stage 4: Printing present pages
On node 0, 4128541 pages
zone 0 present_pages = 3992
zone 1 present_pages = 520069
zone 2 present_pages = 3604480
On node 1, 983040 pages
zone 2 present_pages = 983040
--
Mel Gorman
Part-time Phd Student Linux Technology Center
University of Limerick IBM Dublin Software Lab
absent_pages_in_range() made the assumption that users of the API would
not care about holes beyound the end of physical memory. This was not the
case. This patch will account for ranges outside of physical memory as
holes correctly.
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm3-clean/arch/x86_64/mm/srat.c linux-2.6.18-rc4-mm3-001_account_holes_range/arch/x86_64/mm/srat.c
@@ -240,7 +240,9 @@ static int reserve_hotadd(int node, unsi/* This check might be a bit too strict, but I'm keeping it for now. */if(absent_pages_in_range(s_pfn,e_pfn)!=e_pfn-s_pfn){-printk(KERN_ERR"SRAT: Hotplug area has existing memory\n");+printk(KERN_ERR+"SRAT: Hotplug area %lu -> %lu has existing memory\n",+s_pfn,e_pfn);return-1;}
@@ -2259,6 +2259,10 @@ unsigned long __init __absent_pages_in_rif(i==-1)return0;+/* Account for ranges before physical memory on this node */+if(early_node_map[i].start_pfn>range_start_pfn)+hole_pages=early_node_map[i].start_pfn-range_start_pfn;+prev_end_pfn=early_node_map[i].start_pfn;/* Find all holes for the zone within the node */
@@ -2280,6 +2284,11 @@ unsigned long __init __absent_pages_in_rprev_end_pfn=early_node_map[i].end_pfn;}+/* Account for ranges past physical memory on this node */+if(range_end_pfn>prev_end_pfn)+hole_pages=range_end_pfn-+max(range_start_pfn,prev_end_pfn);+returnhole_pages;}
@@ -2301,9 +2310,16 @@ unsigned long __init zone_absent_pages_iunsignedlongzone_type,unsignedlong*ignored){-return__absent_pages_in_range(nid,-arch_zone_lowest_possible_pfn[zone_type],-arch_zone_highest_possible_pfn[zone_type]);+unsignedlongnode_start_pfn,node_end_pfn;+unsignedlongzone_start_pfn,zone_end_pfn;++get_pfn_range_for_nid(nid,&node_start_pfn,&node_end_pfn);+zone_start_pfn=max(arch_zone_lowest_possible_pfn[zone_type],+node_start_pfn);+zone_end_pfn=min(arch_zone_highest_possible_pfn[zone_type],+node_end_pfn);++return__absent_pages_in_range(nid,zone_start_pfn,zone_end_pfn);}/* Return the zone index a PFN is in */
Arch-independent zone-sizing determines the size of a node
(pgdat->node_spanned_pages) based on the physical memory that was registered
by the architecture. However, when CONFIG_MEMORY_HOTPLUG_RESERVE is set,
the architecture expects that the spanned_pages will be much larger and that
mem_map will be allocated that is used lated on memory hot-add.
This patch allows an architecture that sets CONFIG_MEMORY_HOTPLUG_RESERVE
to call push_node_boundaries() which will set the node beginning and end to
at *least* the requested boundary.
diff -rup -X /usr/src/patchset-0.6/bin//dontdiff linux-2.6.18-rc4-mm3-001_account_holes_range/arch/x86_64/mm/srat.c linux-2.6.18-rc4-mm3-002_push_node_boundaries/arch/x86_64/mm/srat.c
@@ -2186,6 +2190,62 @@ void __init sparse_memory_present_with_a}/**+*push_node_boundaries-Pushnodeboundariestoatleasttherequestedboundary+*@nid:Thenidofthenodetopushtheboundaryfor+*@start_pfn:Thestartpfnofthenode+*@end_pfn:Theendpfnofthenode+*+*Inreserve-basedhot-add,mem_mapisallocatedthatisunuseduntilhotadd+*time.Specifically,onx86_64,SRATwillreportrangesthatcanpotentially+*behotpluggedeventhoughnophysicalmemoryexists.Thisfunctionallows+*anarchtopushoutthenodeboundariessomem_mapisallocatedthatcan+*beusedlater.+*/+#ifdef CONFIG_MEMORY_HOTPLUG_RESERVE+void__initpush_node_boundaries(unsignedintnid,+unsignedlongstart_pfn,unsignedlongend_pfn)+{+printk(KERN_DEBUG"Entering push_node_boundaries(%u, %lu, %lu)\n",+nid,start_pfn,end_pfn);++/* Initialise the boundary for this node if necessary */+if(node_boundary_end_pfn[nid]==0)+node_boundary_start_pfn[nid]=-1UL;++/* Update the boundaries */+if(node_boundary_start_pfn[nid]>start_pfn)+node_boundary_start_pfn[nid]=start_pfn;+if(node_boundary_end_pfn[nid]<end_pfn)+node_boundary_end_pfn[nid]=end_pfn;+}++/* If necessary, push the node boundary out for reserve hotadd */+staticvoid__initaccount_node_boundary(unsignedintnid,+unsignedlong*start_pfn,unsignedlong*end_pfn)+{+printk(KERN_DEBUG"Entering account_node_boundary(%u, %lu, %lu)\n",+nid,*start_pfn,*end_pfn);++/* Return if boundary information has not been provided */+if(node_boundary_end_pfn[nid]==0)+return;++/* Check the boundaries and update if necessary */+if(node_boundary_start_pfn[nid]<*start_pfn)+*start_pfn=node_boundary_start_pfn[nid];+if(node_boundary_end_pfn[nid]>*end_pfn)+*end_pfn=node_boundary_end_pfn[nid];+}+#else+void__initpush_node_boundaries(unsignedintnid,+unsignedlongstart_pfn,unsignedlongend_pfn){}++staticvoid__initaccount_node_boundary(unsignedintnid,+unsignedlong*start_pfn,unsignedlong*end_pfn){}+#endif+++/***get_pfn_range_for_nid-Returnthestartandendpageframesforanode*@nid:Thenidtoreturntherangefor.IfMAX_NUMNODES,theminandmaxPFNarereturned*@start_pfn:Passedbyreference.Onreturn,itwillhavethenodestart_pfn
@@ -2212,6 +2272,9 @@ void __init get_pfn_range_for_nid(unsignprintk(KERN_WARNING"Node %u active with no memory\n",nid);*start_pfn=0;}++/* Push the node boundaries out if requested */+account_node_boundary(nid,start_pfn,end_pfn);}/*
@@ -2655,6 +2718,10 @@ void __init remove_all_active_ranges(){memset(early_node_map,0,sizeof(early_node_map));nr_nodemap_entries=0;+#ifdef CONFIG_MEMORY_HOTPLUG_RESERVE+memset(node_boundary_start_pfn,0,sizeof(node_boundary_start_pfn));+memset(node_boundary_end_pfn,0,sizeof(node_boundary_end_pfn));+#endif /* CONFIG_MEMORY_HOTPLUG_RESERVE */}/* Compare two active node_active_regions */