From: Pavel Tatashin <hidden> Date: 2017-08-29 02:03:53
Changelog:
v7 - v6
- Addressed comments from Michal Hocko
- memblock_discard() patch was removed from this series and integrated
separately
- Fixed bug reported by kbuild test robot new patch:
mm: zero reserved and unavailable struct pages
- Removed patch
x86/mm: reserve only exiting low pages
As, it is not needed anymore, because of the previous fix
- Re-wrote deferred_init_memmap(), found and fixed an existing bug, where
page variable is not reset when zone holes present.
- Merged several patches together per Michal request
- Added performance data including raw logs
v6 - v5
- Fixed ARM64 + kasan code, as reported by Ard Biesheuvel
- Tested ARM64 code in qemu and found few more issues, that I fixed in this
iteration
- Added page roundup/rounddown to x86 and arm zeroing routines to zero the
whole allocated range, instead of only provided address range.
- Addressed SPARC related comment from Sam Ravnborg
- Fixed section mismatch warnings related to memblock_discard().
v5 - v4
- Fixed build issues reported by kbuild on various configurations
v4 - v3
- Rewrote code to zero sturct pages in __init_single_page() as
suggested by Michal Hocko
- Added code to handle issues related to accessing struct page
memory before they are initialized.
v3 - v2
- Addressed David Miller comments about one change per patch:
* Splited changes to platforms into 4 patches
* Made "do not zero vmemmap_buf" as a separate patch
v2 - v1
- Per request, added s390 to deferred "struct page" zeroing
- Collected performance data on x86 which proofs the importance to
keep memset() as prefetch (see below).
SMP machines can benefit from the DEFERRED_STRUCT_PAGE_INIT config option,
which defers initializing struct pages until all cpus have been started so
it can be done in parallel.
However, this feature is sub-optimal, because the deferred page
initialization code expects that the struct pages have already been zeroed,
and the zeroing is done early in boot with a single thread only. Also, we
access that memory and set flags before struct pages are initialized. All
of this is fixed in this patchset.
In this work we do the following:
- Never read access struct page until it was initialized
- Never set any fields in struct pages before they are initialized
- Zero struct page at the beginning of struct page initialization
==========================================================================
Performance improvements on x86 machine with 8 nodes:
Intel(R) Xeon(R) CPU E7-8895 v3 @ 2.60GHz and 1T of memory:
TIME SPEED UP
base no deferred: 95.796233s
fix no deferred: 79.978956s 19.77%
base deferred: 77.254713s
fix deferred: 55.050509s 40.34%
==========================================================================
SPARC M6 3600 MHz with 15T of memory
TIME SPEED UP
base no deferred: 358.335727s
fix no deferred: 302.320936s 18.52%
base deferred: 237.534603s
fix deferred: 182.103003s 30.44%
==========================================================================
Raw dmesg output with timestamps:
x86 base no deferred: https://hastebin.com/ofunepurit.scala
x86 base deferred: https://hastebin.com/ifazegeyas.scala
x86 fix no deferred: https://hastebin.com/pegocohevo.scala
x86 fix deferred: https://hastebin.com/ofupevikuk.scala
sparc base no deferred: https://hastebin.com/ibobeteken.go
sparc base deferred: https://hastebin.com/fariqimiyu.go
sparc fix no deferred: https://hastebin.com/muhegoheyi.go
sparc fix deferred: https://hastebin.com/xadinobutu.go
Pavel Tatashin (11):
x86/mm: setting fields in deferred pages
sparc64/mm: setting fields in deferred pages
mm: deferred_init_memmap improvements
sparc64: simplify vmemmap_populate
mm: defining memblock_virt_alloc_try_nid_raw
mm: zero struct pages during initialization
sparc64: optimized struct page zeroing
mm: zero reserved and unavailable struct pages
x86/kasan: explicitly zero kasan shadow memory
arm64/kasan: explicitly zero kasan shadow memory
mm: stop zeroing memory during allocation in vmemmap
arch/arm64/mm/kasan_init.c | 42 ++++++++
arch/sparc/include/asm/pgtable_64.h | 30 ++++++
arch/sparc/mm/init_64.c | 31 +++---
arch/x86/mm/init_64.c | 9 +-
arch/x86/mm/kasan_init_64.c | 66 ++++++++++++
include/linux/bootmem.h | 27 +++++
include/linux/memblock.h | 16 +++
include/linux/mm.h | 26 +++++
mm/memblock.c | 60 +++++++++--
mm/page_alloc.c | 207 ++++++++++++++++++++----------------
mm/sparse-vmemmap.c | 14 +--
mm/sparse.c | 6 +-
12 files changed, 406 insertions(+), 128 deletions(-)
--
2.14.1
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:03:54
To optimize the performance of struct page initialization,
vmemmap_populate() will no longer zero memory.
We must explicitly zero the memory that is allocated by vmemmap_populate()
for kasan, as this memory does not go through struct page initialization
path.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
arch/arm64/mm/kasan_init.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
@@ -135,6 +135,41 @@ static void __init clear_pgds(unsigned long start,set_pgd(pgd_offset_k(start),__pgd(0));}+/*+*Memorythatwasallocatedbyvmemmap_populateisnotzeroed,sowemust+*zeroithereexplicitly.+*/+staticvoid+zero_vmemmap_populated_memory(void)+{+structmemblock_region*reg;+u64start,end;++for_each_memblock(memory,reg){+start=__phys_to_virt(reg->base);+end=__phys_to_virt(reg->base+reg->size);++if(start>=end)+break;++start=(u64)kasan_mem_to_shadow((void*)start);+end=(u64)kasan_mem_to_shadow((void*)end);++/* Round to the start end of the mapped pages */+start=round_down(start,SWAPPER_BLOCK_SIZE);+end=round_up(end,SWAPPER_BLOCK_SIZE);+memset((void*)start,0,end-start);+}++start=(u64)kasan_mem_to_shadow(_text);+end=(u64)kasan_mem_to_shadow(_end);++/* Round to the start end of the mapped pages */+start=round_down(start,SWAPPER_BLOCK_SIZE);+end=round_up(end,SWAPPER_BLOCK_SIZE);+memset((void*)start,0,end-start);+}+void__initkasan_init(void){u64kimg_shadow_start,kimg_shadow_end;
@@ -205,8 +240,15 @@ void __init kasan_init(void)pfn_pte(sym_to_pfn(kasan_zero_page),PAGE_KERNEL_RO));memset(kasan_zero_page,0,PAGE_SIZE);+cpu_replace_ttbr1(lm_alias(swapper_pg_dir));+/*+*vmemmap_populatedoesnotzerothememory,soweneedtozeroit+*explicitly+*/+zero_vmemmap_populated_memory();+/* At this point kasan is fully initialized. Enable error messages */init_task.kasan_depth=0;pr_info("KernelAddressSanitizer initialized\n");
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:03:56
Without deferred struct page feature (CONFIG_DEFERRED_STRUCT_PAGE_INIT),
flags and other fields in "struct page"es are never changed prior to first
initializing struct pages by going through __init_single_page().
With deferred struct page feature enabled there is a case where we set some
fields prior to initializing:
mem_init() {
register_page_bootmem_info();
free_all_bootmem();
...
}
When register_page_bootmem_info() is called only non-deferred struct pages
are initialized. But, this function goes through some reserved pages which
might be part of the deferred, and thus are not yet initialized.
mem_init
register_page_bootmem_info
register_page_bootmem_info_node
get_page_bootmem
.. setting fields here ..
such as: page->freelist = (void *)type;
free_all_bootmem()
free_low_memory_core_early()
for_each_reserved_mem_region()
reserve_bootmem_region()
init_reserved_page() <- Only if this is deferred reserved page
__init_single_pfn()
__init_single_page()
memset(0) <-- Loose the set fields here
We end-up with similar issue as in the previous patch, where currently we
do not observe problem as memory is zeroed. But, if flag asserts are
changed we can start hitting issues.
Also, because in this patch series we will stop zeroing struct page memory
during allocation, we must make sure that struct pages are properly
initialized prior to using them.
The deferred-reserved pages are initialized in free_all_bootmem().
Therefore, the fix is to switch the above calls.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
arch/sparc/mm/init_64.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
@@ -2508,9 +2508,15 @@ void __init mem_init(void){high_memory=__va(last_valid_pfn<<PAGE_SHIFT);-register_page_bootmem_info();free_all_bootmem();+/* Must be done after boot memory is put on freelist, because here we+*mightsetfieldsindeferredstructpagesthathavenotyetbeen+*initialized,andfree_all_bootmem()initializesallthereserved+*deferredpagesforus.+*/+register_page_bootmem_info();+/**Setupthezeropage,markitreserved,sothatpagecount*isnotmanipulatedwhenfreeingthepagefromuserptes.
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:03:57
Without deferred struct page feature (CONFIG_DEFERRED_STRUCT_PAGE_INIT),
flags and other fields in "struct page"es are never changed prior to first
initializing struct pages by going through __init_single_page().
With deferred struct page feature enabled, however, we set fields in
register_page_bootmem_info that are subsequently clobbered right after in
free_all_bootmem:
mem_init() {
register_page_bootmem_info();
free_all_bootmem();
...
}
When register_page_bootmem_info() is called only non-deferred struct pages
are initialized. But, this function goes through some reserved pages which
might be part of the deferred, and thus are not yet initialized.
mem_init
register_page_bootmem_info
register_page_bootmem_info_node
get_page_bootmem
.. setting fields here ..
such as: page->freelist = (void *)type;
free_all_bootmem()
free_low_memory_core_early()
for_each_reserved_mem_region()
reserve_bootmem_region()
init_reserved_page() <- Only if this is deferred reserved page
__init_single_pfn()
__init_single_page()
memset(0) <-- Loose the set fields here
We end-up with issue where, currently we do not observe problem as memory
is explicitly zeroed. But, if flag asserts are changed we can start hitting
issues.
Also, because in this patch series we will stop zeroing struct page memory
during allocation, we must make sure that struct pages are properly
initialized prior to using them.
The deferred-reserved pages are initialized in free_all_bootmem().
Therefore, the fix is to switch the above calls.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
arch/x86/mm/init_64.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
@@ -1174,12 +1174,17 @@ void __init mem_init(void)/* clear_bss() already clear the empty_zero_page */-register_page_bootmem_info();-/* this will put all memory onto the freelists */free_all_bootmem();after_bootmem=1;+/* Must be done after boot memory is put on freelist, because here we+*mightsetfieldsindeferredstructpagesthathavenotyetbeen+*initialized,andfree_all_bootmem()initializesallthereserved+*deferredpagesforus.+*/+register_page_bootmem_info();+/* Register memory areas for /proc/kcore */kclist_add(&kcore_vsyscall,(void*)VSYSCALL_ADDR,PAGE_SIZE,KCORE_OTHER);
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:04:03
This patch fixes two issues in deferred_init_memmap
=====
In deferred_init_memmap() where all deferred struct pages are initialized
we have a check like this:
if (page->flags) {
VM_BUG_ON(page_zone(page) != zone);
goto free_range;
}
This way we are checking if the current deferred page has already been
initialized. It works, because memory for struct pages has been zeroed, and
the only way flags are not zero if it went through __init_single_page()
before. But, once we change the current behavior and won't zero the memory
in memblock allocator, we cannot trust anything inside "struct page"es
until they are initialized. This patch fixes this.
The deferred_init_memmap() is re-written to loop through only free memory
ranges provided by memblock.
=====
This patch fixes another existing issue on systems that have holes in
zones i.e CONFIG_HOLES_IN_ZONE is defined.
In for_each_mem_pfn_range() we have code like this:
if (!pfn_valid_within(pfn)
goto free_range;
Note: 'page' is not set to NULL and is not incremented but 'pfn' advances.
Thus means if deferred struct pages are enabled on systems with these kind
of holes, linux would get memory corruptions. I have fixed this issue by
defining a new macro that performs all the necessary operations when we
free the current set of pages.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
mm/page_alloc.c | 161 +++++++++++++++++++++++++++-----------------------------
1 file changed, 78 insertions(+), 83 deletions(-)
@@ -1409,14 +1409,17 @@ void clear_zone_contiguous(struct zone *zone)}#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT-staticvoid__initdeferred_free_range(structpage*page,-unsignedlongpfn,intnr_pages)+staticvoid__initdeferred_free_range(unsignedlongpfn,+unsignedlongnr_pages){-inti;+structpage*page;+unsignedlongi;-if(!page)+if(!nr_pages)return;+page=pfn_to_page(pfn);+/* Free a large naturally-aligned chunk if possible */if(nr_pages==pageblock_nr_pages&&(pfn&(pageblock_nr_pages-1))==0){
@@ -1442,19 +1445,82 @@ static inline void __init pgdat_init_report_one_done(void)complete(&pgdat_init_all_done_comp);}+#define DEFERRED_FREE(nr_free, free_base_pfn, page) \+({\+unsignedlongnr=(nr_free);\+\+deferred_free_range((free_base_pfn),(nr));\+(free_base_pfn)=0;\+(nr_free)=0;\+page=NULL;\+nr;\+})++staticunsignedlongdeferred_init_range(intnid,intzid,unsignedlongpfn,+unsignedlongend_pfn)+{+structmminit_pfnnid_cachenid_init_state={};+unsignedlongnr_pgmask=pageblock_nr_pages-1;+unsignedlongfree_base_pfn=0;+unsignedlongnr_pages=0;+unsignedlongnr_free=0;+structpage*page=NULL;++for(;pfn<end_pfn;pfn++){+/*+*Firstwecheckifpfnisvalidonarchitectureswhereitis+*possibletohaveholeswithinpageblock_nr_pages.Onsystems+*whereitisnotpossible,thisfunctionisoptimizedout.+*+*Then,wecheckifacurrentlargepageisvalidbyonly+*checkingthevalidityoftheheadpfn.+*+*meminit_pfn_in_nidischeckedonsystemswherepfnscan+*interleavewithinanode:apfnisbetweenstartandend+*ofanode,butdoesnotbelongtothismemorynode.+*+*Finally,weminimizepfnpagelookupsandschedulerchecksby+*performingitonlyonceeverypageblock_nr_pages.+*/+if(!pfn_valid_within(pfn)){+nr_pages+=DEFERRED_FREE(nr_free,free_base_pfn,page);+}elseif(!(pfn&nr_pgmask)&&!pfn_valid(pfn)){+nr_pages+=DEFERRED_FREE(nr_free,free_base_pfn,page);+}elseif(!meminit_pfn_in_nid(pfn,nid,&nid_init_state)){+nr_pages+=DEFERRED_FREE(nr_free,free_base_pfn,page);+}elseif(page&&(pfn&nr_pgmask)){+page++;+__init_single_page(page,pfn,zid,nid);+nr_free++;+}else{+nr_pages+=DEFERRED_FREE(nr_free,free_base_pfn,page);+page=pfn_to_page(pfn);+__init_single_page(page,pfn,zid,nid);+free_base_pfn=pfn;+nr_free=1;+cond_resched();+}+}+/* Free the last block of pages to allocator */+nr_pages+=DEFERRED_FREE(nr_free,free_base_pfn,page);++returnnr_pages;+}+/* Initialise remaining memory on a node */staticint__initdeferred_init_memmap(void*data){pg_data_t*pgdat=data;intnid=pgdat->node_id;-structmminit_pfnnid_cachenid_init_state={};unsignedlongstart=jiffies;unsignedlongnr_pages=0;-unsignedlongwalk_start,walk_end;-inti,zid;+unsignedlongspfn,epfn;+phys_addr_tspa,epa;+intzid;structzone*zone;unsignedlongfirst_init_pfn=pgdat->first_deferred_pfn;conststructcpumask*cpumask=cpumask_of_node(pgdat->node_id);+u64i;if(first_init_pfn==ULONG_MAX){pgdat_init_report_one_done();
@@ -1476,83 +1542,12 @@ static int __init deferred_init_memmap(void *data)if(first_init_pfn<zone_end_pfn(zone))break;}+first_init_pfn=max(zone->zone_start_pfn,first_init_pfn);-for_each_mem_pfn_range(i,nid,&walk_start,&walk_end,NULL){-unsignedlongpfn,end_pfn;-structpage*page=NULL;-structpage*free_base_page=NULL;-unsignedlongfree_base_pfn=0;-intnr_to_free=0;--end_pfn=min(walk_end,zone_end_pfn(zone));-pfn=first_init_pfn;-if(pfn<walk_start)-pfn=walk_start;-if(pfn<zone->zone_start_pfn)-pfn=zone->zone_start_pfn;--for(;pfn<end_pfn;pfn++){-if(!pfn_valid_within(pfn))-gotofree_range;--/*-*Ensurepfn_validischeckedevery-*pageblock_nr_pagesformemoryholes-*/-if((pfn&(pageblock_nr_pages-1))==0){-if(!pfn_valid(pfn)){-page=NULL;-gotofree_range;-}-}--if(!meminit_pfn_in_nid(pfn,nid,&nid_init_state)){-page=NULL;-gotofree_range;-}--/* Minimise pfn page lookups and scheduler checks */-if(page&&(pfn&(pageblock_nr_pages-1))!=0){-page++;-}else{-nr_pages+=nr_to_free;-deferred_free_range(free_base_page,-free_base_pfn,nr_to_free);-free_base_page=NULL;-free_base_pfn=nr_to_free=0;--page=pfn_to_page(pfn);-cond_resched();-}--if(page->flags){-VM_BUG_ON(page_zone(page)!=zone);-gotofree_range;-}--__init_single_page(page,pfn,zid,nid);-if(!free_base_page){-free_base_page=page;-free_base_pfn=pfn;-nr_to_free=0;-}-nr_to_free++;--/* Where possible, batch up pages for a single free */-continue;-free_range:-/* Free the current block of pages to allocator */-nr_pages+=nr_to_free;-deferred_free_range(free_base_page,free_base_pfn,-nr_to_free);-free_base_page=NULL;-free_base_pfn=nr_to_free=0;-}-/* Free the last block of pages to allocator */-nr_pages+=nr_to_free;-deferred_free_range(free_base_page,free_base_pfn,nr_to_free);--first_init_pfn=max(end_pfn,first_init_pfn);+for_each_free_mem_range(i,nid,MEMBLOCK_NONE,&spa,&epa,NULL){+spfn=max_t(unsignedlong,first_init_pfn,PFN_UP(spa));+epfn=min_t(unsignedlong,zone_end_pfn(zone),PFN_DOWN(epa));+nr_pages+=deferred_init_range(nid,zid,spfn,epfn);}/* Sanity check that the next zone really is unpopulated */
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:04:12
vmemmap_alloc_block() will no longer zero the block, so zero memory
at its call sites for everything except struct pages. Struct page memory
is zero'd by struct page initialization.
Replace allocators in sprase-vmemmap to use the non-zeroing version. So,
we will get the performance improvement by zeroing the memory in parallel
when struct pages are zeroed.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
include/linux/mm.h | 11 +++++++++++
mm/sparse-vmemmap.c | 14 +++++++-------
mm/sparse.c | 6 +++---
3 files changed, 21 insertions(+), 10 deletions(-)
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:04:13
Add struct page zeroing as a part of initialization of other fields in
__init_single_page().
This single thread performance collected on: Intel(R) Xeon(R) CPU E7-8895
v3 @ 2.60GHz with 1T of memory (268400646 pages in 8 nodes):
BASE FIX
sparse_init 11.244671836s 0.007199623s
zone_sizes_init 4.879775891s 8.355182299s
--------------------------
Total 16.124447727s 8.362381922s
sparse_init is where memory for struct pages is zeroed, and the zeroing
part is moved later in this patch into __init_single_page(), which is
called from zone_sizes_init().
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
Acked-by: Michal Hocko <mhocko@suse.com>
---
include/linux/mm.h | 9 +++++++++
mm/page_alloc.c | 1 +
2 files changed, 10 insertions(+)
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:04:15
* A new variant of memblock_virt_alloc_* allocations:
memblock_virt_alloc_try_nid_raw()
- Does not zero the allocated memory
- Does not panic if request cannot be satisfied
* optimize early system hash allocations
Clients can call alloc_large_system_hash() with flag: HASH_ZERO to specify
that memory that was allocated for system hash needs to be zeroed,
otherwise the memory does not need to be zeroed, and client will initialize
it.
If memory does not need to be zero'd, call the new
memblock_virt_alloc_raw() interface, and thus improve the boot performance.
* debug for raw alloctor
When CONFIG_DEBUG_VM is enabled, this patch sets all the memory that is
returned by memblock_virt_alloc_try_nid_raw() to ones to ensure that no
places excpect zeroed memory.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
Acked-by: Michal Hocko <mhocko@suse.com>
---
include/linux/bootmem.h | 27 ++++++++++++++++++++++
mm/memblock.c | 60 +++++++++++++++++++++++++++++++++++++++++++------
mm/page_alloc.c | 15 ++++++-------
3 files changed, 87 insertions(+), 15 deletions(-)
@@ -160,6 +160,9 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat,#define BOOTMEM_ALLOC_ANYWHERE (~(phys_addr_t)0)/* FIXME: Move to memblock.h at a point where we remove nobootmem.c */+void*memblock_virt_alloc_try_nid_raw(phys_addr_tsize,phys_addr_talign,+phys_addr_tmin_addr,+phys_addr_tmax_addr,intnid);void*memblock_virt_alloc_try_nid_nopanic(phys_addr_tsize,phys_addr_talign,phys_addr_tmin_addr,phys_addr_tmax_addr,intnid);
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:05:49
Some memory is reserved but unavailable: not present in memblock.memory
(because not backed by physical pages), but present in memblock.reserved.
Such memory has backing struct pages, but they are not initialized by going
through __init_single_page().
In some cases these struct pages are accessed even if they do not contain
any data. One example is page_to_pfn() might access page->flags if this is
where section information is stored (CONFIG_SPARSEMEM,
SECTION_IN_PAGE_FLAGS).
Since, struct pages are zeroed in __init_single_page(), and not during
allocation time, we must zero such struct pages explicitly.
The patch involves adding a new memblock iterator:
for_each_resv_unavail_range(i, p_start, p_end)
Which iterates through reserved && !memory lists, and we zero struct pages
explicitly by calling mm_zero_struct_page().
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
include/linux/memblock.h | 16 ++++++++++++++++
include/linux/mm.h | 6 ++++++
mm/page_alloc.c | 30 ++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+)
@@ -6261,6 +6261,34 @@ void __paginginit free_area_init_node(int nid, unsigned long *zones_size,free_area_init_core(pgdat);}+#ifdef CONFIG_HAVE_MEMBLOCK+/*+*Onlystructpagesthatarebackedbyphysicalmemoryarezeroedand+*initializedbygoingthrough__init_single_page().But,therearesome+*structpageswhicharereservedinmemblockallocatorandtheirfields+*maybeaccessed(forexamplepage_to_pfn()onsomeconfigurationaccesses+*flags).Wemustexplicitlyzerothosestructpages.+*/+void__paginginitzero_resv_unavail(void)+{+phys_addr_tstart,end;+unsignedlongpfn;+u64i,pgcnt;++/* Loop through ranges that are reserved, but do not have reported+*physicalmemorybacking.+*/+pgcnt=0;+for_each_resv_unavail_range(i,&start,&end){+for(pfn=PFN_DOWN(start);pfn<PFN_UP(end);pfn++){+mm_zero_struct_page(pfn_to_page(pfn));+pgcnt++;+}+}+pr_info("Reserved but unavailable: %lld pages",pgcnt);+}+#endif /* CONFIG_HAVE_MEMBLOCK */+#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP#if MAX_NUMNODES > 1
@@ -6684,6 +6712,7 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn)node_set_state(nid,N_MEMORY);check_for_memory(pgdat,nid);}+zero_resv_unavail();}staticint__initcmdline_parse_core(char*p,unsignedlong*core)
@@ -6847,6 +6876,7 @@ void __init free_area_init(unsigned long *zones_size){free_area_init_node(0,zones_size,__pa(PAGE_OFFSET)>>PAGE_SHIFT,NULL);+zero_resv_unavail();}staticintpage_alloc_cpu_dead(unsignedintcpu)
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:06:15
Add an optimized mm_zero_struct_page(), so struct page's are zeroed without
calling memset(). We do eight to ten regular stores based on the size of
struct page. Compiler optimizes out the conditions of switch() statement.
SPARC-M6 with 15T of memory, single thread performance:
BASE FIX OPTIMIZED_FIX
bootmem_init 28.440467985s 2.305674818s 2.305161615s
free_area_init_nodes 202.845901673s 225.343084508s 172.556506560s
--------------------------------------------
Total 231.286369658s 227.648759326s 174.861668175s
BASE: current linux
FIX: This patch series without "optimized struct page zeroing"
OPTIMIZED_FIX: This patch series including the current patch.
bootmem_init() is where memory for struct pages is zeroed during
allocation. Note, about two seconds in this function is a fixed time: it
does not increase as memory is increased.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
arch/sparc/include/asm/pgtable_64.h | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
@@ -230,6 +230,36 @@ extern unsigned long _PAGE_ALL_SZ_BITS;externstructpage*mem_map_zero;#define ZERO_PAGE(vaddr) (mem_map_zero)+/* This macro must be updated when the size of struct page grows above 80+*orreducesbelow64.+*Theideathatcompileroptimizesoutswitch()statement,andonly+*leavesclrxinstructions+*/+#define mm_zero_struct_page(pp) do { \+unsignedlong*_pp=(void*)(pp);\+\+/* Check that struct page is either 64, 72, or 80 bytes */\+BUILD_BUG_ON(sizeof(structpage)&7);\+BUILD_BUG_ON(sizeof(structpage)<64);\+BUILD_BUG_ON(sizeof(structpage)>80);\+\+switch(sizeof(structpage)){\+case80:\+_pp[9]=0;/* fallthrough */\+case72:\+_pp[8]=0;/* fallthrough */\+default:\+_pp[7]=0;\+_pp[6]=0;\+_pp[5]=0;\+_pp[4]=0;\+_pp[3]=0;\+_pp[2]=0;\+_pp[1]=0;\+_pp[0]=0;\+}\+}while(0)+/* PFNs are real physical page numbers. However, mem_map only begins to record*per-pageinformationstartingatpfn_base.Thisistohandlesystemswhere*thefirstphysicalpageinthemachineisatsomehugephysicaladdress,
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:06:16
To optimize the performance of struct page initialization,
vmemmap_populate() will no longer zero memory.
We must explicitly zero the memory that is allocated by vmemmap_populate()
for kasan, as this memory does not go through struct page initialization
path.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
arch/x86/mm/kasan_init_64.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
@@ -84,6 +84,66 @@ static struct notifier_block kasan_die_notifier = {};#endif+/*+*x86variantofvmemmap_populate()useseitherPMD_SIZEpagesorbasepages+*tomapallocatedmemory.Thisroutinedeterminesthepagesizeforthegiven+*addressfromvmemmap.+*/+staticu64get_vmemmap_pgsz(u64addr)+{+pgd_t*pgd;+p4d_t*p4d;+pud_t*pud;+pmd_t*pmd;++pgd=pgd_offset_k(addr);+BUG_ON(pgd_none(*pgd)||pgd_large(*pgd));++p4d=p4d_offset(pgd,addr);+BUG_ON(p4d_none(*p4d)||p4d_large(*p4d));++pud=pud_offset(p4d,addr);+BUG_ON(pud_none(*pud)||pud_large(*pud));++pmd=pmd_offset(pud,addr);+BUG_ON(pmd_none(*pmd));++if(pmd_large(*pmd))+returnPMD_SIZE;+returnPAGE_SIZE;+}++/*+*Memorythatwasallocatedbyvmemmap_populateisnotzeroed,sowemust+*zeroithereexplicitly.+*/+staticvoid+zero_vmemmap_populated_memory(void)+{+u64i,start,end;++for(i=0;i<E820_MAX_ENTRIES&&pfn_mapped[i].end;i++){+void*kaddr_start=pfn_to_kaddr(pfn_mapped[i].start);+void*kaddr_end=pfn_to_kaddr(pfn_mapped[i].end);++start=(u64)kasan_mem_to_shadow(kaddr_start);+end=(u64)kasan_mem_to_shadow(kaddr_end);++/* Round to the start end of the mapped pages */+start=rounddown(start,get_vmemmap_pgsz(start));+end=roundup(end,get_vmemmap_pgsz(start));+memset((void*)start,0,end-start);+}++start=(u64)kasan_mem_to_shadow(_stext);+end=(u64)kasan_mem_to_shadow(_end);++/* Round to the start end of the mapped pages */+start=rounddown(start,get_vmemmap_pgsz(start));+end=roundup(end,get_vmemmap_pgsz(start));+memset((void*)start,0,end-start);+}+void__initkasan_early_init(void){inti;
From: Pavel Tatashin <hidden> Date: 2017-08-29 02:06:17
Remove duplicating code by using common functions
vmemmap_pud_populate and vmemmap_pgd_populate.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
---
arch/sparc/mm/init_64.c | 23 ++++++-----------------
1 file changed, 6 insertions(+), 17 deletions(-)
@@ -2611,30 +2611,19 @@ int __meminit vmemmap_populate(unsigned long vstart, unsigned long vend,vstart=vstart&PMD_MASK;vend=ALIGN(vend,PMD_SIZE);for(;vstart<vend;vstart+=PMD_SIZE){-pgd_t*pgd=pgd_offset_k(vstart);+pgd_t*pgd=vmemmap_pgd_populate(vstart,node);unsignedlongpte;pud_t*pud;pmd_t*pmd;-if(pgd_none(*pgd)){-pud_t*new=vmemmap_alloc_block(PAGE_SIZE,node);+if(!pgd)+return-ENOMEM;-if(!new)-return-ENOMEM;-pgd_populate(&init_mm,pgd,new);-}--pud=pud_offset(pgd,vstart);-if(pud_none(*pud)){-pmd_t*new=vmemmap_alloc_block(PAGE_SIZE,node);--if(!new)-return-ENOMEM;-pud_populate(&init_mm,pud,new);-}+pud=vmemmap_pud_populate(pgd,vstart,node);+if(!pud)+return-ENOMEM;pmd=pmd_offset(pud,vstart);-pte=pmd_val(*pmd);if(!(pte&_PAGE_VALID)){void*block=vmemmap_alloc_block(PMD_SIZE,node);
From: David Miller <davem@davemloft.net> Date: 2017-08-30 01:08:48
From: Pavel Tatashin <redacted>
Date: Mon, 28 Aug 2017 22:02:15 -0400
Remove duplicating code by using common functions
vmemmap_pud_populate and vmemmap_pgd_populate.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
From: David Miller <davem@davemloft.net> Date: 2017-08-30 01:09:31
From: Pavel Tatashin <redacted>
Date: Mon, 28 Aug 2017 22:02:13 -0400
Without deferred struct page feature (CONFIG_DEFERRED_STRUCT_PAGE_INIT),
flags and other fields in "struct page"es are never changed prior to first
initializing struct pages by going through __init_single_page().
With deferred struct page feature enabled there is a case where we set some
fields prior to initializing:
mem_init() {
register_page_bootmem_info();
free_all_bootmem();
...
}
When register_page_bootmem_info() is called only non-deferred struct pages
are initialized. But, this function goes through some reserved pages which
might be part of the deferred, and thus are not yet initialized.
mem_init
register_page_bootmem_info
register_page_bootmem_info_node
get_page_bootmem
.. setting fields here ..
such as: page->freelist = (void *)type;
free_all_bootmem()
free_low_memory_core_early()
for_each_reserved_mem_region()
reserve_bootmem_region()
init_reserved_page() <- Only if this is deferred reserved page
__init_single_pfn()
__init_single_page()
memset(0) <-- Loose the set fields here
We end-up with similar issue as in the previous patch, where currently we
do not observe problem as memory is zeroed. But, if flag asserts are
changed we can start hitting issues.
Also, because in this patch series we will stop zeroing struct page memory
during allocation, we must make sure that struct pages are properly
initialized prior to using them.
The deferred-reserved pages are initialized in free_all_bootmem().
Therefore, the fix is to switch the above calls.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
From: David Miller <davem@davemloft.net> Date: 2017-08-30 01:12:12
From: Pavel Tatashin <redacted>
Date: Mon, 28 Aug 2017 22:02:18 -0400
Add an optimized mm_zero_struct_page(), so struct page's are zeroed without
calling memset(). We do eight to ten regular stores based on the size of
struct page. Compiler optimizes out the conditions of switch() statement.
SPARC-M6 with 15T of memory, single thread performance:
BASE FIX OPTIMIZED_FIX
bootmem_init 28.440467985s 2.305674818s 2.305161615s
free_area_init_nodes 202.845901673s 225.343084508s 172.556506560s
--------------------------------------------
Total 231.286369658s 227.648759326s 174.861668175s
BASE: current linux
FIX: This patch series without "optimized struct page zeroing"
OPTIMIZED_FIX: This patch series including the current patch.
bootmem_init() is where memory for struct pages is zeroed during
allocation. Note, about two seconds in this function is a fixed time: it
does not increase as memory is increased.
Signed-off-by: Pavel Tatashin <redacted>
Reviewed-by: Steven Sistare <redacted>
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Reviewed-by: Bob Picco <redacted>
You should probably use initializing stores when you are doing 8
stores and we thus know the page struct is cache line aligned.
But other than that:
Acked-by: David S. Miller <davem@davemloft.net>
Hi Dave,
Thank you for acking.
The reason I am not doing initializing stores is because they require a
membar, even if only regular stores are following (I hoped to do a
membar before first load). This is something I was thinking was not
true, but after consulting with colleagues and checking processor
manual, I verified that it is the case.
Pasha
You should probably use initializing stores when you are doing 8
stores and we thus know the page struct is cache line aligned.
But other than that:
Acked-by: David S. Miller <davem@davemloft.net>
The reason I am not doing initializing stores is because they require
a membar, even if only regular stores are following (I hoped to do a
membar before first load). This is something I was thinking was not
true, but after consulting with colleagues and checking processor
manual, I verified that it is the case.
From: kbuild test robot <hidden> Date: 2017-08-30 23:13:24
Hi Pavel,
[auto build test ERROR on sparc/master]
[also build test ERROR on v4.13-rc7 next-20170829]
[cannot apply to mmotm/master]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Pavel-Tatashin/complete-deferred-page-initialization/20170831-041021
base: https://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc.git master
config: cris-allmodconfig (attached as .config)
compiler: cris-linux-gcc (GCC) 6.2.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=cris
All errors (new ones prefixed by >>):
In file included from mm/page_alloc.c:18:0:
include/linux/mm.h:1974:33: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'zero_resv_unavail'
static inline void __paginginit zero_resv_unavail(void) {}
^~~~~~~~~~~~~~~~~
mm/page_alloc.c: In function 'free_area_init':
quoted
mm/page_alloc.c:6863:2: error: implicit declaration of function 'zero_resv_unavail' [-Werror=implicit-function-declaration]
zero_resv_unavail();
^~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +/zero_resv_unavail +6863 mm/page_alloc.c
6858
6859 void __init free_area_init(unsigned long *zones_size)
6860 {
6861 free_area_init_node(0, zones_size,
6862 __pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL);