Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1645765 > unrolled thread

[PATCH] x86/mm: pgds getting out of sync after memory hot remove

Started byJérôme Glisse <jglisse@redhat.com>
First post2017-05-19 19:00 +0200
Last post2017-05-22 16:40 +0200
Articles 6 — 4 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH] x86/mm: pgds getting out of sync after memory hot remove Jérôme Glisse <jglisse@redhat.com> - 2017-05-19 19:00 +0200
    [PATCH] x86/mm: synchronize pgd in vmemmap_free() Jérôme Glisse <jglisse@redhat.com> - 2017-05-19 19:00 +0200
      Re: [PATCH] x86/mm: synchronize pgd in vmemmap_free() John Hubbard <jhubbard@nvidia.com> - 2017-05-20 02:40 +0200
    Re: [PATCH] x86/mm: pgds getting out of sync after memory hot remove "Kirill A. Shutemov" <kirill@shutemov.name> - 2017-05-22 15:30 +0200
      Re: [PATCH] x86/mm: pgds getting out of sync after memory hot remove Jerome Glisse <jglisse@redhat.com> - 2017-05-22 16:20 +0200
        Re: [PATCH] x86/mm: pgds getting out of sync after memory hot remove "Kirill A. Shutemov" <kirill@shutemov.name> - 2017-05-22 16:40 +0200

#1645765 — [PATCH] x86/mm: pgds getting out of sync after memory hot remove

FromJérôme Glisse <jglisse@redhat.com>
Date2017-05-19 19:00 +0200
Subject[PATCH] x86/mm: pgds getting out of sync after memory hot remove
Message-ID<tITpU-4T3-13@gated-at.bofh.it>
After memory hot remove it seems we do not synchronize pgds for kernel
virtual memory range (on vmemmap_free()). This seems bogus to me as it
means we are left with stall entry for process with mm != mm_init

Yet i am puzzle by the fact that i am only now hitting this issue. It
never was an issue with 4.12 or before ie HMM never triggered following
BUG_ON inside sync_global_pgds():

if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
   BUG_ON(p4d_page_vaddr(*p4d) != p4d_page_vaddr(*p4d_ref));


It seems that Kirill 5 level page table changes play a role in this
behavior change. I could not bisect because HMM is painfull to rebase
for each bisection step so that is just my best guess.


Am i missing something here ? Am i wrong in assuming that should sync
pgd on vmemmap_free() ? If so anyone have a good guess on why i am now
seeing the above BUG_ON ?

Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mel Gorman <mgorman@suse.de>

Jérôme Glisse (1):
  x86/mm: synchronize pgd in vmemmap_free()

 arch/x86/mm/init_64.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

-- 
2.4.11

[toc] | [next] | [standalone]


#1645768 — [PATCH] x86/mm: synchronize pgd in vmemmap_free()

FromJérôme Glisse <jglisse@redhat.com>
Date2017-05-19 19:00 +0200
Subject[PATCH] x86/mm: synchronize pgd in vmemmap_free()
Message-ID<tITpU-4T3-17@gated-at.bofh.it>
In reply to#1645765
When we free kernel virtual map we should synchronize p4d/pud for
all the pgds to avoid any stall entry in non canonical pgd.

Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mel Gorman <mgorman@suse.de>
---
 arch/x86/mm/init_64.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index ff95fe8..df753f8 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -108,8 +108,6 @@ void sync_global_pgds(unsigned long start, unsigned long end)
 		BUILD_BUG_ON(pgd_none(*pgd_ref));
 		p4d_ref = p4d_offset(pgd_ref, address);
 
-		if (p4d_none(*p4d_ref))
-			continue;
 
 		spin_lock(&pgd_lock);
 		list_for_each_entry(page, &pgd_list, lru) {
@@ -123,12 +121,16 @@ void sync_global_pgds(unsigned long start, unsigned long end)
 			pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
 			spin_lock(pgt_lock);
 
-			if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
-				BUG_ON(p4d_page_vaddr(*p4d)
-				       != p4d_page_vaddr(*p4d_ref));
-
-			if (p4d_none(*p4d))
+			if (p4d_none(*p4d_ref)) {
 				set_p4d(p4d, *p4d_ref);
+			} else {
+				if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
+					BUG_ON(p4d_page_vaddr(*p4d)
+					       != p4d_page_vaddr(*p4d_ref));
+
+				if (p4d_none(*p4d))
+					set_p4d(p4d, *p4d_ref);
+			}
 
 			spin_unlock(pgt_lock);
 		}
@@ -1024,6 +1026,7 @@ remove_pagetable(unsigned long start, unsigned long end, bool direct)
 void __ref vmemmap_free(unsigned long start, unsigned long end)
 {
 	remove_pagetable(start, end, false);
+	sync_global_pgds(start, end - 1);
 }
 
 #ifdef CONFIG_MEMORY_HOTREMOVE
-- 
2.4.11

[toc] | [prev] | [next] | [standalone]


#1646035 — Re: [PATCH] x86/mm: synchronize pgd in vmemmap_free()

FromJohn Hubbard <jhubbard@nvidia.com>
Date2017-05-20 02:40 +0200
SubjectRe: [PATCH] x86/mm: synchronize pgd in vmemmap_free()
Message-ID<tJ0B3-1Qr-1@gated-at.bofh.it>
In reply to#1645768
Hi Jerome,

On 05/19/2017 11:01 AM, Jérôme Glisse wrote:
> When we free kernel virtual map we should synchronize p4d/pud for
> all the pgds to avoid any stall entry in non canonical pgd.

"any stale entry in the non-canonical pgd", is what I think you meant to type there.

Also, it would be nice to clarify that commit description a bit: I'm not sure what is meant here by 
a "non-canonical pgd".

Also, it seems like the reshuffling of the internals of sync_global_pgds() deserves at least some 
mention here. More below.

> 
> Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
> Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Michal Hocko <mhocko@suse.com>
> Cc: Mel Gorman <mgorman@suse.de>
> ---
>   arch/x86/mm/init_64.c | 17 ++++++++++-------
>   1 file changed, 10 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
> index ff95fe8..df753f8 100644
> --- a/arch/x86/mm/init_64.c
> +++ b/arch/x86/mm/init_64.c
> @@ -108,8 +108,6 @@ void sync_global_pgds(unsigned long start, unsigned long end)
>   		BUILD_BUG_ON(pgd_none(*pgd_ref));
>   		p4d_ref = p4d_offset(pgd_ref, address);
>   
> -		if (p4d_none(*p4d_ref))
> -			continue;
>   
>   		spin_lock(&pgd_lock);
>   		list_for_each_entry(page, &pgd_list, lru) {
> @@ -123,12 +121,16 @@ void sync_global_pgds(unsigned long start, unsigned long end)
>   			pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
>   			spin_lock(pgt_lock);
>   
> -			if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
> -				BUG_ON(p4d_page_vaddr(*p4d)
> -				       != p4d_page_vaddr(*p4d_ref));
> -
> -			if (p4d_none(*p4d))
> +			if (p4d_none(*p4d_ref)) {
>   				set_p4d(p4d, *p4d_ref);

Is the intention really to set p4d to a zeroed *p4d_ref, or is that a mistake?

> +			} else {
> +				if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))

I think the code needs to be somewhat restructured, but as it stands, the above !p4d_none(*p4d_ref) 
will always be true, because first part of the if/else checked for the opposite case: 
p4d_none(*p4d_ref).  This is a side effect of moving that block of code.

> +					BUG_ON(p4d_page_vaddr(*p4d)
> +					       != p4d_page_vaddr(*p4d_ref));
> +
> +				if (p4d_none(*p4d))
> +					set_p4d(p4d, *p4d_ref);
> +			}
>   
>   			spin_unlock(pgt_lock);
>   		}
> @@ -1024,6 +1026,7 @@ remove_pagetable(unsigned long start, unsigned long end, bool direct)
>   void __ref vmemmap_free(unsigned long start, unsigned long end)
>   {
>   	remove_pagetable(start, end, false);
> +	sync_global_pgds(start, end - 1);

This does fix the HMM crash that I was seeing in hmm-next.

thanks,
John Hubbard
NVIDIA

>   }
>   
>   #ifdef CONFIG_MEMORY_HOTREMOVE
> -- 
> 2.4.11
> 
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org.  For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
> 

[toc] | [prev] | [next] | [standalone]


#1646887

From"Kirill A. Shutemov" <kirill@shutemov.name>
Date2017-05-22 15:30 +0200
Message-ID<tJVzj-6fx-9@gated-at.bofh.it>
In reply to#1645765
On Fri, May 19, 2017 at 02:01:26PM -0400, Jérôme Glisse wrote:
> After memory hot remove it seems we do not synchronize pgds for kernel
> virtual memory range (on vmemmap_free()). This seems bogus to me as it
> means we are left with stall entry for process with mm != mm_init
> 
> Yet i am puzzle by the fact that i am only now hitting this issue. It
> never was an issue with 4.12 or before ie HMM never triggered following
> BUG_ON inside sync_global_pgds():
> 
> if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
>    BUG_ON(p4d_page_vaddr(*p4d) != p4d_page_vaddr(*p4d_ref));
> 
> 
> It seems that Kirill 5 level page table changes play a role in this
> behavior change. I could not bisect because HMM is painfull to rebase
> for each bisection step so that is just my best guess.
> 
> 
> Am i missing something here ? Am i wrong in assuming that should sync
> pgd on vmemmap_free() ? If so anyone have a good guess on why i am now
> seeing the above BUG_ON ?

What would we gain by syncing pgd on free? Stale pgds are fine as long as
they are not referenced (use-after-free case). Syncing is addtional work.

See af2cf278ef4f ("x86/mm/hotplug: Don't remove PGD entries in remove_pagetable()")
and 5372e155a28f ("x86/mm: Drop unused argument 'removed' from sync_global_pgds()").

-- 
 Kirill A. Shutemov

[toc] | [prev] | [next] | [standalone]


#1646962

FromJerome Glisse <jglisse@redhat.com>
Date2017-05-22 16:20 +0200
Message-ID<tJWlI-6KH-21@gated-at.bofh.it>
In reply to#1646887
On Mon, May 22, 2017 at 04:12:15PM +0300, Kirill A. Shutemov wrote:
> On Fri, May 19, 2017 at 02:01:26PM -0400, Jérôme Glisse wrote:
> > After memory hot remove it seems we do not synchronize pgds for kernel
> > virtual memory range (on vmemmap_free()). This seems bogus to me as it
> > means we are left with stall entry for process with mm != mm_init
> > 
> > Yet i am puzzle by the fact that i am only now hitting this issue. It
> > never was an issue with 4.12 or before ie HMM never triggered following
> > BUG_ON inside sync_global_pgds():
> > 
> > if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
> >    BUG_ON(p4d_page_vaddr(*p4d) != p4d_page_vaddr(*p4d_ref));
> > 
> > 
> > It seems that Kirill 5 level page table changes play a role in this
> > behavior change. I could not bisect because HMM is painfull to rebase
> > for each bisection step so that is just my best guess.
> > 
> > 
> > Am i missing something here ? Am i wrong in assuming that should sync
> > pgd on vmemmap_free() ? If so anyone have a good guess on why i am now
> > seeing the above BUG_ON ?
> 
> What would we gain by syncing pgd on free? Stale pgds are fine as long as
> they are not referenced (use-after-free case). Syncing is addtional work.

Well then how do i avoid the BUG_ON above ? Because the init_mm pgd is
clear but none of the stall entry in any other mm. So if i unplug memory
and replug memory at exact same address it tries to allocate new p4d/pud
for struct page area and then when sync_global_pgds() is call it goes
over the list of pgd and BUG_ON() :

if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
    BUG_ON(p4d_page_vaddr(*p4d) != p4d_page_vaddr(*p4d_ref));


So to me either above check need to go and we should overwritte pgd no
matter what or we should restore previous behavior. I don't mind either
one.

Cheers,
Jérôme

[toc] | [prev] | [next] | [standalone]


#1646982

From"Kirill A. Shutemov" <kirill@shutemov.name>
Date2017-05-22 16:40 +0200
Message-ID<tJWF3-6Ra-1@gated-at.bofh.it>
In reply to#1646962
On Mon, May 22, 2017 at 10:11:51AM -0400, Jerome Glisse wrote:
> On Mon, May 22, 2017 at 04:12:15PM +0300, Kirill A. Shutemov wrote:
> > On Fri, May 19, 2017 at 02:01:26PM -0400, Jérôme Glisse wrote:
> > > After memory hot remove it seems we do not synchronize pgds for kernel
> > > virtual memory range (on vmemmap_free()). This seems bogus to me as it
> > > means we are left with stall entry for process with mm != mm_init
> > > 
> > > Yet i am puzzle by the fact that i am only now hitting this issue. It
> > > never was an issue with 4.12 or before ie HMM never triggered following
> > > BUG_ON inside sync_global_pgds():
> > > 
> > > if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
> > >    BUG_ON(p4d_page_vaddr(*p4d) != p4d_page_vaddr(*p4d_ref));
> > > 
> > > 
> > > It seems that Kirill 5 level page table changes play a role in this
> > > behavior change. I could not bisect because HMM is painfull to rebase
> > > for each bisection step so that is just my best guess.
> > > 
> > > 
> > > Am i missing something here ? Am i wrong in assuming that should sync
> > > pgd on vmemmap_free() ? If so anyone have a good guess on why i am now
> > > seeing the above BUG_ON ?
> > 
> > What would we gain by syncing pgd on free? Stale pgds are fine as long as
> > they are not referenced (use-after-free case). Syncing is addtional work.
> 
> Well then how do i avoid the BUG_ON above ? Because the init_mm pgd is
> clear but none of the stall entry in any other mm. So if i unplug memory
> and replug memory at exact same address it tries to allocate new p4d/pud
> for struct page area and then when sync_global_pgds() is call it goes
> over the list of pgd and BUG_ON() :
> 
> if (!p4d_none(*p4d_ref) && !p4d_none(*p4d))
>     BUG_ON(p4d_page_vaddr(*p4d) != p4d_page_vaddr(*p4d_ref));
> 
> 
> So to me either above check need to go and we should overwritte pgd no
> matter what or we should restore previous behavior. I don't mind either
> one.

I would prefer to drop the BUG_ON.

Ingo?

-- 
 Kirill A. Shutemov

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web