Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1636499 > unrolled thread
| Started by | Pavel Tatashin <pasha.tatashin@oracle.com> |
|---|---|
| First post | 2017-05-05 19:10 +0200 |
| Last post | 2017-05-11 16:40 +0200 |
| Articles | 18 — 6 participants |
Back to article view | Back to linux.kernel
[v3 0/9] parallelized "struct page" zeroing Pavel Tatashin <pasha.tatashin@oracle.com> - 2017-05-05 19:10 +0200
[v3 8/9] powerpc: teach platforms not to zero struct pages memory Pavel Tatashin <pasha.tatashin@oracle.com> - 2017-05-05 19:10 +0200
[v3 9/9] s390: teach platforms not to zero struct pages memory Pavel Tatashin <pasha.tatashin@oracle.com> - 2017-05-05 19:10 +0200
Re: [v3 9/9] s390: teach platforms not to zero struct pages memory Heiko Carstens <heiko.carstens@de.ibm.com> - 2017-05-08 13:40 +0200
[v3 1/9] sparc64: simplify vmemmap_populate Pavel Tatashin <pasha.tatashin@oracle.com> - 2017-05-05 19:10 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Michal Hocko <mhocko@kernel.org> - 2017-05-09 20:20 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Pasha Tatashin <pasha.tatashin@oracle.com> - 2017-05-09 21:00 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Michal Hocko <mhocko@kernel.org> - 2017-05-10 09:30 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Pasha Tatashin <pasha.tatashin@oracle.com> - 2017-05-10 15:50 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Michal Hocko <mhocko@kernel.org> - 2017-05-10 17:00 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Pasha Tatashin <pasha.tatashin@oracle.com> - 2017-05-10 17:10 +0200
Re: [v3 0/9] parallelized "struct page" zeroing David Miller <davem@davemloft.net> - 2017-05-10 17:30 +0200
Re: [v3 0/9] parallelized "struct page" zeroing David Miller <davem@davemloft.net> - 2017-05-10 17:20 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Matthew Wilcox <willy@infradead.org> - 2017-05-10 19:20 +0200
Re: [v3 0/9] parallelized "struct page" zeroing David Miller <davem@davemloft.net> - 2017-05-10 20:10 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Matthew Wilcox <willy@infradead.org> - 2017-05-10 23:20 +0200
Re: [v3 0/9] parallelized "struct page" zeroing Michal Hocko <mhocko@kernel.org> - 2017-05-11 10:10 +0200
Re: [v3 0/9] parallelized "struct page" zeroing David Miller <davem@davemloft.net> - 2017-05-11 16:40 +0200
| From | Pavel Tatashin <pasha.tatashin@oracle.com> |
|---|---|
| Date | 2017-05-05 19:10 +0200 |
| Subject | [v3 0/9] parallelized "struct page" zeroing |
| Message-ID | <tDOTT-4Dx-3@gated-at.bofh.it> |
Changelog: v2 - v3 - Addressed David's comments about one change per patch: * Splited changes to platforms into 4 patches * Made "do not zero vmemmap_buf" as a separate patch v1 - v2 - 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). When deferred struct page initialization feature is enabled, we get a performance gain of initializing vmemmap in parallel after other CPUs are started. However, we still zero the memory for vmemmap using one boot CPU. This patch-set fixes the memset-zeroing limitation by deferring it as well. Performance gain on SPARC with 32T: base: https://hastebin.com/ozanelatat.go fix: https://hastebin.com/utonawukof.go As you can see without the fix it takes: 97.89s to boot With the fix it takes: 46.91 to boot. Performance gain on x86 with 1T: base: https://hastebin.com/uvifasohon.pas fix: https://hastebin.com/anodiqaguj.pas On Intel we save 10.66s/T while on SPARC we save 1.59s/T. Intel has twice as many pages, and also fewer nodes than SPARC (sparc 32 nodes, vs. intel 8 nodes). It takes one thread 11.25s to zero vmemmap on Intel for 1T, so it should take additional 11.25 / 8 = 1.4s (this machine has 8 nodes) per node to initialize the memory, but it takes only additional 0.456s per node, which means on Intel we also benefit from having memset() and initializing all other fields in one place. Pavel Tatashin (9): sparc64: simplify vmemmap_populate mm: defining memblock_virt_alloc_try_nid_raw mm: add "zero" argument to vmemmap allocators mm: do not zero vmemmap_buf mm: zero struct pages during initialization sparc64: teach sparc not to zero struct pages memory x86: teach x86 not to zero struct pages memory powerpc: teach platforms not to zero struct pages memory s390: teach platforms not to zero struct pages memory arch/powerpc/mm/init_64.c | 4 +- arch/s390/mm/vmem.c | 5 ++- arch/sparc/mm/init_64.c | 26 +++++++---------------- arch/x86/mm/init_64.c | 3 +- include/linux/bootmem.h | 3 ++ include/linux/mm.h | 15 +++++++++++-- mm/memblock.c | 46 ++++++++++++++++++++++++++++++++++++------ mm/page_alloc.c | 3 ++ mm/sparse-vmemmap.c | 48 +++++++++++++++++++++++++++++--------------- 9 files changed, 103 insertions(+), 50 deletions(-)
[toc] | [next] | [standalone]
| From | Pavel Tatashin <pasha.tatashin@oracle.com> |
|---|---|
| Date | 2017-05-05 19:10 +0200 |
| Subject | [v3 8/9] powerpc: teach platforms not to zero struct pages memory |
| Message-ID | <tDOTU-4Dx-25@gated-at.bofh.it> |
| In reply to | #1636499 |
If we are using deferred struct page initialization feature, most of "struct page"es are getting initialized after other CPUs are started, and hence we are benefiting from doing this job in parallel. However, we are still zeroing all the memory that is allocated for "struct pages" using the boot CPU. This patch solves this problem, by deferring zeroing "struct pages" to only when they are initialized on PowerPC platforms. Signed-off-by: Pavel Tatashin <pasha.tatashin@oracle.com> Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com> --- arch/powerpc/mm/init_64.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index d42c6b3..c381bd7 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -181,7 +181,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) if (vmemmap_populated(start, page_size)) continue; - p = vmemmap_alloc_block(page_size, node, true); + p = vmemmap_alloc_block(page_size, node, VMEMMAP_ZERO); if (!p) return -ENOMEM; -- 1.7.1
[toc] | [prev] | [next] | [standalone]
| From | Pavel Tatashin <pasha.tatashin@oracle.com> |
|---|---|
| Date | 2017-05-05 19:10 +0200 |
| Subject | [v3 9/9] s390: teach platforms not to zero struct pages memory |
| Message-ID | <tDOTV-4Dx-29@gated-at.bofh.it> |
| In reply to | #1636499 |
If we are using deferred struct page initialization feature, most of "struct page"es are getting initialized after other CPUs are started, and hence we are benefiting from doing this job in parallel. However, we are still zeroing all the memory that is allocated for "struct pages" using the boot CPU. This patch solves this problem, by deferring zeroing "struct pages" to only when they are initialized on s390 platforms. Signed-off-by: Pavel Tatashin <pasha.tatashin@oracle.com> Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com> --- arch/s390/mm/vmem.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 9c75214..ffe9ba1 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -252,7 +252,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) void *new_page; new_page = vmemmap_alloc_block(PMD_SIZE, node, - true); + VMEMMAP_ZERO); if (!new_page) goto out; pmd_val(*pm_dir) = __pa(new_page) | sgt_prot; -- 1.7.1
[toc] | [prev] | [next] | [standalone]
| From | Heiko Carstens <heiko.carstens@de.ibm.com> |
|---|---|
| Date | 2017-05-08 13:40 +0200 |
| Subject | Re: [v3 9/9] s390: teach platforms not to zero struct pages memory |
| Message-ID | <tEPbb-3lD-5@gated-at.bofh.it> |
| In reply to | #1636502 |
On Fri, May 05, 2017 at 01:03:16PM -0400, Pavel Tatashin wrote:
> If we are using deferred struct page initialization feature, most of
> "struct page"es are getting initialized after other CPUs are started, and
> hence we are benefiting from doing this job in parallel. However, we are
> still zeroing all the memory that is allocated for "struct pages" using the
> boot CPU. This patch solves this problem, by deferring zeroing "struct
> pages" to only when they are initialized on s390 platforms.
>
> Signed-off-by: Pavel Tatashin <pasha.tatashin@oracle.com>
> Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com>
> ---
> arch/s390/mm/vmem.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
> index 9c75214..ffe9ba1 100644
> --- a/arch/s390/mm/vmem.c
> +++ b/arch/s390/mm/vmem.c
> @@ -252,7 +252,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
> void *new_page;
>
> new_page = vmemmap_alloc_block(PMD_SIZE, node,
> - true);
> + VMEMMAP_ZERO);
> if (!new_page)
> goto out;
> pmd_val(*pm_dir) = __pa(new_page) | sgt_prot;
If you add the hunk below then this is
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com>
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index ffe9ba1aec8b..bf88a8b9c24d 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -272,7 +272,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
if (pte_none(*pt_dir)) {
void *new_page;
- new_page = vmemmap_alloc_block(PAGE_SIZE, node, true);
+ new_page = vmemmap_alloc_block(PAGE_SIZE, node, VMEMMAP_ZERO);
if (!new_page)
goto out;
pte_val(*pt_dir) = __pa(new_page) | pgt_prot;
[toc] | [prev] | [next] | [standalone]
| From | Pavel Tatashin <pasha.tatashin@oracle.com> |
|---|---|
| Date | 2017-05-05 19:10 +0200 |
| Subject | [v3 1/9] sparc64: simplify vmemmap_populate |
| Message-ID | <tDOTV-4Dx-31@gated-at.bofh.it> |
| In reply to | #1636499 |
Remove duplicating code, by using common functions
vmemmap_pud_populate and vmemmap_pgd_populate functions.
Signed-off-by: Pavel Tatashin <pasha.tatashin@oracle.com>
Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com>
---
arch/sparc/mm/init_64.c | 23 ++++++-----------------
1 files changed, 6 insertions(+), 17 deletions(-)
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 0cda653..14cc1fc 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2530,30 +2530,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);
unsigned long pte;
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);
--
1.7.1
[toc] | [prev] | [next] | [standalone]
| From | Michal Hocko <mhocko@kernel.org> |
|---|---|
| Date | 2017-05-09 20:20 +0200 |
| Message-ID | <tFhTP-5nA-11@gated-at.bofh.it> |
| In reply to | #1636499 |
On Fri 05-05-17 13:03:07, Pavel Tatashin wrote:
> Changelog:
> v2 - v3
> - Addressed David's comments about one change per patch:
> * Splited changes to platforms into 4 patches
> * Made "do not zero vmemmap_buf" as a separate patch
> v1 - v2
> - 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).
>
> When deferred struct page initialization feature is enabled, we get a
> performance gain of initializing vmemmap in parallel after other CPUs are
> started. However, we still zero the memory for vmemmap using one boot CPU.
> This patch-set fixes the memset-zeroing limitation by deferring it as well.
I like the idea of postponing the zeroing from the allocation to the
init time. To be honest the improvement looks much larger than I would
expect (Btw. this should be a part of the changelog rather than a
outside link).
The implementation just looks too large to what I would expect. E.g. do
we really need to add zero argument to the large part of the memblock
API? Wouldn't it be easier to simply export memblock_virt_alloc_internal
(or its tiny wrapper memblock_virt_alloc_core) and move the zeroing
outside to its 2 callers? A completely untested scratched version at the
end of the email.
Also it seems that this is not 100% correct either as it only cares
about VMEMMAP while DEFERRED_STRUCT_PAGE_INIT might be enabled also for
SPARSEMEM. This would suggest that we would zero out pages twice,
right?
A similar concern would go to the memory hotplug patch which will
fall back to the slab/page allocator IIRC. On the other hand
__init_single_page is shared with the hotplug code so again we would
initialize 2 times.
So I suspect more changes are needed. I will have a closer look tomorrow.
> arch/powerpc/mm/init_64.c | 4 +-
> arch/s390/mm/vmem.c | 5 ++-
> arch/sparc/mm/init_64.c | 26 +++++++----------------
> arch/x86/mm/init_64.c | 3 +-
> include/linux/bootmem.h | 3 ++
> include/linux/mm.h | 15 +++++++++++--
> mm/memblock.c | 46 ++++++++++++++++++++++++++++++++++++------
> mm/page_alloc.c | 3 ++
> mm/sparse-vmemmap.c | 48 +++++++++++++++++++++++++++++---------------
> 9 files changed, 103 insertions(+), 50 deletions(-)
The bootmem API change mentioned above.
include/linux/bootmem.h | 3 +++
mm/memblock.c | 41 ++++++++++++++++++++++++++---------------
mm/sparse-vmemmap.c | 2 +-
3 files changed, 30 insertions(+), 16 deletions(-)
diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h
index 962164d36506..c9a08463d9a8 100644
--- a/include/linux/bootmem.h
+++ b/include/linux/bootmem.h
@@ -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_core(phys_addr_t size, phys_addr_t align,
+ phys_addr_t min_addr, phys_addr_t max_addr,
+ int nid);
void *memblock_virt_alloc_try_nid_nopanic(phys_addr_t size,
phys_addr_t align, phys_addr_t min_addr,
phys_addr_t max_addr, int nid);
diff --git a/mm/memblock.c b/mm/memblock.c
index b049c9b2dba8..eab7da94f873 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -1271,8 +1271,7 @@ phys_addr_t __init memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, i
*
* The memory block is aligned on SMP_CACHE_BYTES if @align == 0.
*
- * The phys address of allocated boot memory block is converted to virtual and
- * allocated memory is reset to 0.
+ * The function has to be zeroed out explicitly.
*
* In addition, function sets the min_count to 0 using kmemleak_alloc for
* allocated boot memory block, so that it is never reported as leaks.
@@ -1280,15 +1279,18 @@ phys_addr_t __init memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, i
* RETURNS:
* Virtual address of allocated memory block on success, NULL on failure.
*/
-static void * __init memblock_virt_alloc_internal(
+static inline void * __init memblock_virt_alloc_internal(
phys_addr_t size, phys_addr_t align,
phys_addr_t min_addr, phys_addr_t max_addr,
- int nid)
+ int nid, void *caller)
{
phys_addr_t alloc;
void *ptr;
ulong flags = choose_memblock_flags();
+ memblock_dbg("%s: %llu bytes align=0x%llx nid=%d from=0x%llx max_addr=0x%llx %pF\n",
+ __func__, (u64)size, (u64)align, nid, (u64)min_addr,
+ (u64)max_addr, caller);
if (WARN_ONCE(nid == MAX_NUMNODES, "Usage of MAX_NUMNODES is deprecated. Use NUMA_NO_NODE instead\n"))
nid = NUMA_NO_NODE;
@@ -1334,7 +1336,6 @@ static void * __init memblock_virt_alloc_internal(
return NULL;
done:
ptr = phys_to_virt(alloc);
- memset(ptr, 0, size);
/*
* The min_count is set to 0 so that bootmem allocated blocks
@@ -1347,6 +1348,14 @@ static void * __init memblock_virt_alloc_internal(
return ptr;
}
+void * __init memblock_virt_alloc_core(phys_addr_t size, phys_addr_t align,
+ phys_addr_t min_addr, phys_addr_t max_addr,
+ int nid)
+{
+ return memblock_virt_alloc_internal(size, align, min_addr, max_addr, nid,
+ (void *)_RET_IP_);
+}
+
/**
* memblock_virt_alloc_try_nid_nopanic - allocate boot memory block
* @size: size of memory block to be allocated in bytes
@@ -1369,11 +1378,14 @@ void * __init memblock_virt_alloc_try_nid_nopanic(
phys_addr_t min_addr, phys_addr_t max_addr,
int nid)
{
- memblock_dbg("%s: %llu bytes align=0x%llx nid=%d from=0x%llx max_addr=0x%llx %pF\n",
- __func__, (u64)size, (u64)align, nid, (u64)min_addr,
- (u64)max_addr, (void *)_RET_IP_);
- return memblock_virt_alloc_internal(size, align, min_addr,
- max_addr, nid);
+ void *ptr;
+
+ ptr = memblock_virt_alloc_internal(size, align, min_addr,
+ max_addr, nid, (void *)_RET_IP_);
+ if (ptr)
+ memset(ptr, 0, size);
+
+ return ptr;
}
/**
@@ -1401,13 +1413,12 @@ void * __init memblock_virt_alloc_try_nid(
{
void *ptr;
- memblock_dbg("%s: %llu bytes align=0x%llx nid=%d from=0x%llx max_addr=0x%llx %pF\n",
- __func__, (u64)size, (u64)align, nid, (u64)min_addr,
- (u64)max_addr, (void *)_RET_IP_);
ptr = memblock_virt_alloc_internal(size, align,
- min_addr, max_addr, nid);
- if (ptr)
+ min_addr, max_addr, nid, (void *)_RET_IP_);
+ if (ptr) {
+ memset(ptr, 0, size);
return ptr;
+ }
panic("%s: Failed to allocate %llu bytes align=0x%llx nid=%d from=0x%llx max_addr=0x%llx\n",
__func__, (u64)size, (u64)align, nid, (u64)min_addr,
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index a56c3989f773..4e060f0f9fe5 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -41,7 +41,7 @@ static void * __ref __earlyonly_bootmem_alloc(int node,
unsigned long align,
unsigned long goal)
{
- return memblock_virt_alloc_try_nid(size, align, goal,
+ return memblock_virt_alloc_core(size, align, goal,
BOOTMEM_ALLOC_ACCESSIBLE, node);
}
--
Michal Hocko
SUSE Labs
[toc] | [prev] | [next] | [standalone]
| From | Pasha Tatashin <pasha.tatashin@oracle.com> |
|---|---|
| Date | 2017-05-09 21:00 +0200 |
| Message-ID | <tFiwy-5FK-15@gated-at.bofh.it> |
| In reply to | #1638330 |
Hi Michal, > I like the idea of postponing the zeroing from the allocation to the > init time. To be honest the improvement looks much larger than I would > expect (Btw. this should be a part of the changelog rather than a > outside link). The improvements are larger, because this time was never measured, as Linux does not have early boot time stamps. I added them for x86 and SPARC to emasure the performance. I am pushing those changes through separate patchsets. > > The implementation just looks too large to what I would expect. E.g. do > we really need to add zero argument to the large part of the memblock > API? Wouldn't it be easier to simply export memblock_virt_alloc_internal > (or its tiny wrapper memblock_virt_alloc_core) and move the zeroing > outside to its 2 callers? A completely untested scratched version at the > end of the email. I am OK, with this change. But, I do not really see a difference between: memblock_virt_alloc_raw() and memblock_virt_alloc_core() In both cases we use memblock_virt_alloc_internal(), but the only difference is that in my case we tell memblock_virt_alloc_internal() to zero the pages if needed, and in your case the other two callers are zeroing it. I like moving memblock_dbg() inside memblock_virt_alloc_internal() > > Also it seems that this is not 100% correct either as it only cares > about VMEMMAP while DEFERRED_STRUCT_PAGE_INIT might be enabled also for > SPARSEMEM. This would suggest that we would zero out pages twice, > right? Thank you, I will check this combination before sending out the next patch. > > A similar concern would go to the memory hotplug patch which will > fall back to the slab/page allocator IIRC. On the other hand > __init_single_page is shared with the hotplug code so again we would > initialize 2 times. Correct, when memory it hotplugged, to gain the benefit of this fix, and also not to regress by actually double zeroing "struct pages" we should not zero it out. However, I do not really have means to test it. > > So I suspect more changes are needed. I will have a closer look tomorrow. Thank you for reviewing this work. I will wait for your comments before sending out updated patches. Pasha
[toc] | [prev] | [next] | [standalone]
| From | Michal Hocko <mhocko@kernel.org> |
|---|---|
| Date | 2017-05-10 09:30 +0200 |
| Message-ID | <tFuem-6dd-7@gated-at.bofh.it> |
| In reply to | #1638350 |
On Tue 09-05-17 14:54:50, Pasha Tatashin wrote: [...] > >The implementation just looks too large to what I would expect. E.g. do > >we really need to add zero argument to the large part of the memblock > >API? Wouldn't it be easier to simply export memblock_virt_alloc_internal > >(or its tiny wrapper memblock_virt_alloc_core) and move the zeroing > >outside to its 2 callers? A completely untested scratched version at the > >end of the email. > > I am OK, with this change. But, I do not really see a difference between: > > memblock_virt_alloc_raw() > and > memblock_virt_alloc_core() > > In both cases we use memblock_virt_alloc_internal(), but the only difference > is that in my case we tell memblock_virt_alloc_internal() to zero the pages > if needed, and in your case the other two callers are zeroing it. I like > moving memblock_dbg() inside memblock_virt_alloc_internal() Well, I didn't object to this particular part. I was mostly concerned about http://lkml.kernel.org/r/1494003796-748672-4-git-send-email-pasha.tatashin@oracle.com and the "zero" argument for other functions. I guess we can do without that. I _think_ that we should simply _always_ initialize the page at the __init_single_page time rather than during the allocation. That would require dropping __GFP_ZERO for non-memblock allocations. Or do you think we could regress for single threaded initialization? > >Also it seems that this is not 100% correct either as it only cares > >about VMEMMAP while DEFERRED_STRUCT_PAGE_INIT might be enabled also for > >SPARSEMEM. This would suggest that we would zero out pages twice, > >right? > > Thank you, I will check this combination before sending out the next patch. > > > > >A similar concern would go to the memory hotplug patch which will > >fall back to the slab/page allocator IIRC. On the other hand > >__init_single_page is shared with the hotplug code so again we would > >initialize 2 times. > > Correct, when memory it hotplugged, to gain the benefit of this fix, and > also not to regress by actually double zeroing "struct pages" we should not > zero it out. However, I do not really have means to test it. It should be pretty easy to test with kvm, but I can help with testing on the real HW as well. Thanks! -- Michal Hocko SUSE Labs
[toc] | [prev] | [next] | [standalone]
| From | Pasha Tatashin <pasha.tatashin@oracle.com> |
|---|---|
| Date | 2017-05-10 15:50 +0200 |
| Message-ID | <tFAa6-1mz-19@gated-at.bofh.it> |
| In reply to | #1638606 |
> > Well, I didn't object to this particular part. I was mostly concerned > about > http://lkml.kernel.org/r/1494003796-748672-4-git-send-email-pasha.tatashin@oracle.com > and the "zero" argument for other functions. I guess we can do without > that. I _think_ that we should simply _always_ initialize the page at the > __init_single_page time rather than during the allocation. That would > require dropping __GFP_ZERO for non-memblock allocations. Or do you > think we could regress for single threaded initialization? > Hi Michal, Thats exactly right, I am worried that we will regress when there is no parallelized initialization of "struct pages" if we force unconditionally do memset() in __init_single_page(). The overhead of calling memset() on a smaller chunks (64-bytes) may cause the regression, this is why I opted only for parallelized case to zero this metadata. This way, we are guaranteed to see great improvements from this change without having regressions on platforms and builds that do not support parallelized initialization of "struct pages". However, on some chips such as latest SPARCs it is beneficial to have memset() right inside __init_single_page() even for single threaded case, because it can act as a prefetch on chips with optimized block initialized store instructions. Pasha
[toc] | [prev] | [next] | [standalone]
| From | Michal Hocko <mhocko@kernel.org> |
|---|---|
| Date | 2017-05-10 17:00 +0200 |
| Message-ID | <tFBfQ-20m-9@gated-at.bofh.it> |
| In reply to | #1638840 |
On Wed 10-05-17 09:42:22, Pasha Tatashin wrote: > > > >Well, I didn't object to this particular part. I was mostly concerned > >about > >http://lkml.kernel.org/r/1494003796-748672-4-git-send-email-pasha.tatashin@oracle.com > >and the "zero" argument for other functions. I guess we can do without > >that. I _think_ that we should simply _always_ initialize the page at the > >__init_single_page time rather than during the allocation. That would > >require dropping __GFP_ZERO for non-memblock allocations. Or do you > >think we could regress for single threaded initialization? > > > > Hi Michal, > > Thats exactly right, I am worried that we will regress when there is no > parallelized initialization of "struct pages" if we force unconditionally do > memset() in __init_single_page(). The overhead of calling memset() on a > smaller chunks (64-bytes) may cause the regression, this is why I opted only > for parallelized case to zero this metadata. This way, we are guaranteed to > see great improvements from this change without having regressions on > platforms and builds that do not support parallelized initialization of > "struct pages". Have you measured that? I do not think it would be super hard to measure. I would be quite surprised if this added much if anything at all as the whole struct page should be in the cache line already. We do set reference count and other struct members. Almost nobody should be looking at our page at this time and stealing the cache line. On the other hand a large memcpy will basically wipe everything away from the cpu cache. Or am I missing something? -- Michal Hocko SUSE Labs
[toc] | [prev] | [next] | [standalone]
| From | Pasha Tatashin <pasha.tatashin@oracle.com> |
|---|---|
| Date | 2017-05-10 17:10 +0200 |
| Message-ID | <tFBpw-2iN-21@gated-at.bofh.it> |
| In reply to | #1638896 |
On 05/10/2017 10:57 AM, Michal Hocko wrote: > On Wed 10-05-17 09:42:22, Pasha Tatashin wrote: >>> >>> Well, I didn't object to this particular part. I was mostly concerned >>> about >>> http://lkml.kernel.org/r/1494003796-748672-4-git-send-email-pasha.tatashin@oracle.com >>> and the "zero" argument for other functions. I guess we can do without >>> that. I _think_ that we should simply _always_ initialize the page at the >>> __init_single_page time rather than during the allocation. That would >>> require dropping __GFP_ZERO for non-memblock allocations. Or do you >>> think we could regress for single threaded initialization? >>> >> >> Hi Michal, >> >> Thats exactly right, I am worried that we will regress when there is no >> parallelized initialization of "struct pages" if we force unconditionally do >> memset() in __init_single_page(). The overhead of calling memset() on a >> smaller chunks (64-bytes) may cause the regression, this is why I opted only >> for parallelized case to zero this metadata. This way, we are guaranteed to >> see great improvements from this change without having regressions on >> platforms and builds that do not support parallelized initialization of >> "struct pages". > > Have you measured that? I do not think it would be super hard to > measure. I would be quite surprised if this added much if anything at > all as the whole struct page should be in the cache line already. We do > set reference count and other struct members. Almost nobody should be > looking at our page at this time and stealing the cache line. On the > other hand a large memcpy will basically wipe everything away from the > cpu cache. Or am I missing something? > Perhaps you are right, and I will measure on x86. But, I suspect hit can become unacceptable on some platfoms: there is an overhead of calling a function, even if it is leaf-optimized, and there is an overhead in memset() to check for alignments of size and address, types of setting (zeroing vs. non-zeroing), etc., that adds up quickly. Pasha
[toc] | [prev] | [next] | [standalone]
| From | David Miller <davem@davemloft.net> |
|---|---|
| Date | 2017-05-10 17:30 +0200 |
| Message-ID | <tFBIR-2p1-1@gated-at.bofh.it> |
| In reply to | #1638899 |
From: Pasha Tatashin <pasha.tatashin@oracle.com> Date: Wed, 10 May 2017 11:01:40 -0400 > Perhaps you are right, and I will measure on x86. But, I suspect hit > can become unacceptable on some platfoms: there is an overhead of > calling a function, even if it is leaf-optimized, and there is an > overhead in memset() to check for alignments of size and address, > types of setting (zeroing vs. non-zeroing), etc., that adds up > quickly. Another source of overhead on the sparc64 side is that we much do memory barriers around the block initializiing stores. So batching calls to memset() amortize that as well.
[toc] | [prev] | [next] | [standalone]
| From | David Miller <davem@davemloft.net> |
|---|---|
| Date | 2017-05-10 17:20 +0200 |
| Message-ID | <tFBzc-2lT-21@gated-at.bofh.it> |
| In reply to | #1638896 |
From: Michal Hocko <mhocko@kernel.org> Date: Wed, 10 May 2017 16:57:26 +0200 > Have you measured that? I do not think it would be super hard to > measure. I would be quite surprised if this added much if anything at > all as the whole struct page should be in the cache line already. We do > set reference count and other struct members. Almost nobody should be > looking at our page at this time and stealing the cache line. On the > other hand a large memcpy will basically wipe everything away from the > cpu cache. Or am I missing something? I guess it might be clearer if you understand what the block initializing stores do on sparc64. There are no memory accesses at all. The cpu just zeros out the cache line, that's it. No L3 cache line is allocated. So this "wipe everything" behavior will not happen in the L3.
[toc] | [prev] | [next] | [standalone]
| From | Matthew Wilcox <willy@infradead.org> |
|---|---|
| Date | 2017-05-10 19:20 +0200 |
| Message-ID | <tFDrk-3t7-13@gated-at.bofh.it> |
| In reply to | #1638906 |
On Wed, May 10, 2017 at 11:19:43AM -0400, David Miller wrote: > From: Michal Hocko <mhocko@kernel.org> > Date: Wed, 10 May 2017 16:57:26 +0200 > > > Have you measured that? I do not think it would be super hard to > > measure. I would be quite surprised if this added much if anything at > > all as the whole struct page should be in the cache line already. We do > > set reference count and other struct members. Almost nobody should be > > looking at our page at this time and stealing the cache line. On the > > other hand a large memcpy will basically wipe everything away from the > > cpu cache. Or am I missing something? > > I guess it might be clearer if you understand what the block > initializing stores do on sparc64. There are no memory accesses at > all. > > The cpu just zeros out the cache line, that's it. > > No L3 cache line is allocated. So this "wipe everything" behavior > will not happen in the L3. There's either something wrong with your explanation or my reading skills :-) "There are no memory accesses" "No L3 cache line is allocated" You can have one or the other ... either the CPU sends a cacheline-sized write of zeroes to memory without allocating an L3 cache line (maybe using the store buffer?), or the CPU allocates an L3 cache line and sets its contents to zeroes, probably putting it in the last way of the set so it's the first thing to be evicted if not touched. Or there's some magic in the memory bus protocol where the CPU gets to tell the DRAM "hey, clear these cache lines". Although that's also a memory access of sorts ...
[toc] | [prev] | [next] | [standalone]
| From | David Miller <davem@davemloft.net> |
|---|---|
| Date | 2017-05-10 20:10 +0200 |
| Message-ID | <tFEdJ-40t-49@gated-at.bofh.it> |
| In reply to | #1638978 |
From: Matthew Wilcox <willy@infradead.org> Date: Wed, 10 May 2017 10:17:03 -0700 > On Wed, May 10, 2017 at 11:19:43AM -0400, David Miller wrote: >> From: Michal Hocko <mhocko@kernel.org> >> Date: Wed, 10 May 2017 16:57:26 +0200 >> >> > Have you measured that? I do not think it would be super hard to >> > measure. I would be quite surprised if this added much if anything at >> > all as the whole struct page should be in the cache line already. We do >> > set reference count and other struct members. Almost nobody should be >> > looking at our page at this time and stealing the cache line. On the >> > other hand a large memcpy will basically wipe everything away from the >> > cpu cache. Or am I missing something? >> >> I guess it might be clearer if you understand what the block >> initializing stores do on sparc64. There are no memory accesses at >> all. >> >> The cpu just zeros out the cache line, that's it. >> >> No L3 cache line is allocated. So this "wipe everything" behavior >> will not happen in the L3. > > There's either something wrong with your explanation or my reading > skills :-) > > "There are no memory accesses" > "No L3 cache line is allocated" > > You can have one or the other ... either the CPU sends a cacheline-sized > write of zeroes to memory without allocating an L3 cache line (maybe > using the store buffer?), or the CPU allocates an L3 cache line and sets > its contents to zeroes, probably putting it in the last way of the set > so it's the first thing to be evicted if not touched. There is no conflict in what I said. Only an L2 cache line is allocated and cleared. L3 is left alone.
[toc] | [prev] | [next] | [standalone]
| From | Matthew Wilcox <willy@infradead.org> |
|---|---|
| Date | 2017-05-10 23:20 +0200 |
| Message-ID | <tFHbz-5MI-3@gated-at.bofh.it> |
| In reply to | #1639010 |
On Wed, May 10, 2017 at 02:00:26PM -0400, David Miller wrote: > From: Matthew Wilcox <willy@infradead.org> > Date: Wed, 10 May 2017 10:17:03 -0700 > > On Wed, May 10, 2017 at 11:19:43AM -0400, David Miller wrote: > >> I guess it might be clearer if you understand what the block > >> initializing stores do on sparc64. There are no memory accesses at > >> all. > >> > >> The cpu just zeros out the cache line, that's it. > >> > >> No L3 cache line is allocated. So this "wipe everything" behavior > >> will not happen in the L3. > > > > There's either something wrong with your explanation or my reading > > skills :-) > > > > "There are no memory accesses" > > "No L3 cache line is allocated" > > > > You can have one or the other ... either the CPU sends a cacheline-sized > > write of zeroes to memory without allocating an L3 cache line (maybe > > using the store buffer?), or the CPU allocates an L3 cache line and sets > > its contents to zeroes, probably putting it in the last way of the set > > so it's the first thing to be evicted if not touched. > > There is no conflict in what I said. > > Only an L2 cache line is allocated and cleared. L3 is left alone. I thought SPARC had inclusive caches. So allocating an L2 cacheline would necessitate allocating an L3 cacheline. Or is this an exception to the normal order of things?
[toc] | [prev] | [next] | [standalone]
| From | Michal Hocko <mhocko@kernel.org> |
|---|---|
| Date | 2017-05-11 10:10 +0200 |
| Message-ID | <tFRkC-3Q2-9@gated-at.bofh.it> |
| In reply to | #1638906 |
On Wed 10-05-17 11:19:43, David S. Miller wrote: > From: Michal Hocko <mhocko@kernel.org> > Date: Wed, 10 May 2017 16:57:26 +0200 > > > Have you measured that? I do not think it would be super hard to > > measure. I would be quite surprised if this added much if anything at > > all as the whole struct page should be in the cache line already. We do > > set reference count and other struct members. Almost nobody should be > > looking at our page at this time and stealing the cache line. On the > > other hand a large memcpy will basically wipe everything away from the > > cpu cache. Or am I missing something? > > I guess it might be clearer if you understand what the block > initializing stores do on sparc64. There are no memory accesses at > all. > > The cpu just zeros out the cache line, that's it. > > No L3 cache line is allocated. So this "wipe everything" behavior > will not happen in the L3. OK, good to know. My undestanding of sparc64 is close to zero. Anyway, do you agree that doing the struct page initialization along with other writes to it shouldn't add a measurable overhead comparing to pre-zeroing of larger block of struct pages? We already have an exclusive cache line and doing one 64B write along with few other stores should be basically the same. -- Michal Hocko SUSE Labs
[toc] | [prev] | [next] | [standalone]
| From | David Miller <davem@davemloft.net> |
|---|---|
| Date | 2017-05-11 16:40 +0200 |
| Message-ID | <tFXq3-7wN-37@gated-at.bofh.it> |
| In reply to | #1639236 |
From: Michal Hocko <mhocko@kernel.org> Date: Thu, 11 May 2017 10:05:38 +0200 > Anyway, do you agree that doing the struct page initialization along > with other writes to it shouldn't add a measurable overhead comparing > to pre-zeroing of larger block of struct pages? We already have an > exclusive cache line and doing one 64B write along with few other stores > should be basically the same. Yes, it should be reasonably cheap.
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web