* [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork()
[not found] <20230413231120.544685-1-peterx@redhat.com>
@ 2023-04-13 23:11 ` Peter Xu
2023-04-14 9:37 ` David Hildenbrand
` (2 more replies)
2023-04-13 23:11 ` [PATCH 2/6] mm/hugetlb: Fix uffd-wp bit lost when unsharing happens Peter Xu
1 sibling, 3 replies; 10+ messages in thread
From: Peter Xu @ 2023-04-13 23:11 UTC (permalink / raw)
To: linux-kernel, linux-mm
Cc: Axel Rasmussen, Andrew Morton, David Hildenbrand, peterx,
Mike Kravetz, Nadav Amit, Andrea Arcangeli, linux-stable
There're a bunch of things that were wrong:
- Reading uffd-wp bit from a swap entry should use pte_swp_uffd_wp()
rather than huge_pte_uffd_wp().
- When copying over a pte, we should drop uffd-wp bit when
!EVENT_FORK (aka, when !userfaultfd_wp(dst_vma)).
- When doing early CoW for private hugetlb (e.g. when the parent page was
pinned), uffd-wp bit should be properly carried over if necessary.
No bug reported probably because most people do not even care about these
corner cases, but they are still bugs and can be exposed by the recent unit
tests introduced, so fix all of them in one shot.
Cc: linux-stable <stable@vger.kernel.org>
Fixes: bc70fbf269fd ("mm/hugetlb: handle uffd-wp during fork()")
Signed-off-by: Peter Xu <peterx@redhat.com>
---
mm/hugetlb.c | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index f16b25b1a6b9..7320e64aacc6 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -4953,11 +4953,15 @@ static bool is_hugetlb_entry_hwpoisoned(pte_t pte)
static void
hugetlb_install_folio(struct vm_area_struct *vma, pte_t *ptep, unsigned long addr,
- struct folio *new_folio)
+ struct folio *new_folio, pte_t old)
{
+ pte_t newpte = make_huge_pte(vma, &new_folio->page, 1);
+
__folio_mark_uptodate(new_folio);
hugepage_add_new_anon_rmap(new_folio, vma, addr);
- set_huge_pte_at(vma->vm_mm, addr, ptep, make_huge_pte(vma, &new_folio->page, 1));
+ if (userfaultfd_wp(vma) && huge_pte_uffd_wp(old))
+ newpte = huge_pte_mkuffd_wp(newpte);
+ set_huge_pte_at(vma->vm_mm, addr, ptep, newpte);
hugetlb_count_add(pages_per_huge_page(hstate_vma(vma)), vma->vm_mm);
folio_set_hugetlb_migratable(new_folio);
}
@@ -5032,14 +5036,11 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
*/
;
} else if (unlikely(is_hugetlb_entry_hwpoisoned(entry))) {
- bool uffd_wp = huge_pte_uffd_wp(entry);
-
- if (!userfaultfd_wp(dst_vma) && uffd_wp)
+ if (!userfaultfd_wp(dst_vma))
entry = huge_pte_clear_uffd_wp(entry);
set_huge_pte_at(dst, addr, dst_pte, entry);
} else if (unlikely(is_hugetlb_entry_migration(entry))) {
swp_entry_t swp_entry = pte_to_swp_entry(entry);
- bool uffd_wp = huge_pte_uffd_wp(entry);
if (!is_readable_migration_entry(swp_entry) && cow) {
/*
@@ -5049,11 +5050,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
swp_entry = make_readable_migration_entry(
swp_offset(swp_entry));
entry = swp_entry_to_pte(swp_entry);
- if (userfaultfd_wp(src_vma) && uffd_wp)
- entry = huge_pte_mkuffd_wp(entry);
+ if (userfaultfd_wp(src_vma) &&
+ pte_swp_uffd_wp(entry))
+ entry = pte_swp_mkuffd_wp(entry);
set_huge_pte_at(src, addr, src_pte, entry);
}
- if (!userfaultfd_wp(dst_vma) && uffd_wp)
+ if (!userfaultfd_wp(dst_vma))
entry = huge_pte_clear_uffd_wp(entry);
set_huge_pte_at(dst, addr, dst_pte, entry);
} else if (unlikely(is_pte_marker(entry))) {
@@ -5114,7 +5116,8 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
/* huge_ptep of dst_pte won't change as in child */
goto again;
}
- hugetlb_install_folio(dst_vma, dst_pte, addr, new_folio);
+ hugetlb_install_folio(dst_vma, dst_pte, addr,
+ new_folio, src_pte_old);
spin_unlock(src_ptl);
spin_unlock(dst_ptl);
continue;
@@ -5132,6 +5135,9 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
entry = huge_pte_wrprotect(entry);
}
+ if (!userfaultfd_wp(dst_vma))
+ entry = huge_pte_clear_uffd_wp(entry);
+
set_huge_pte_at(dst, addr, dst_pte, entry);
hugetlb_count_add(npages, dst);
}
--
2.39.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/6] mm/hugetlb: Fix uffd-wp bit lost when unsharing happens
[not found] <20230413231120.544685-1-peterx@redhat.com>
2023-04-13 23:11 ` [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork() Peter Xu
@ 2023-04-13 23:11 ` Peter Xu
2023-04-14 9:23 ` David Hildenbrand
2023-04-14 22:19 ` Mike Kravetz
1 sibling, 2 replies; 10+ messages in thread
From: Peter Xu @ 2023-04-13 23:11 UTC (permalink / raw)
To: linux-kernel, linux-mm
Cc: Axel Rasmussen, Andrew Morton, David Hildenbrand, peterx,
Mike Kravetz, Nadav Amit, Andrea Arcangeli, linux-stable
When we try to unshare a pinned page for a private hugetlb, uffd-wp bit can
get lost during unsharing. Fix it by carrying it over.
This should be very rare, only if an unsharing happened on a private
hugetlb page with uffd-wp protected (e.g. in a child which shares the same
page with parent with UFFD_FEATURE_EVENT_FORK enabled).
Cc: linux-stable <stable@vger.kernel.org>
Fixes: 166f3ecc0daf ("mm/hugetlb: hook page faults for uffd write protection")
Reported-by: Mike Kravetz <mike.kravetz@oracle.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
---
mm/hugetlb.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 7320e64aacc6..083aae35bff8 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -5637,13 +5637,16 @@ static vm_fault_t hugetlb_wp(struct mm_struct *mm, struct vm_area_struct *vma,
spin_lock(ptl);
ptep = hugetlb_walk(vma, haddr, huge_page_size(h));
if (likely(ptep && pte_same(huge_ptep_get(ptep), pte))) {
+ pte_t newpte = make_huge_pte(vma, &new_folio->page, !unshare);
+
/* Break COW or unshare */
huge_ptep_clear_flush(vma, haddr, ptep);
mmu_notifier_invalidate_range(mm, range.start, range.end);
page_remove_rmap(old_page, vma, true);
hugepage_add_new_anon_rmap(new_folio, vma, haddr);
- set_huge_pte_at(mm, haddr, ptep,
- make_huge_pte(vma, &new_folio->page, !unshare));
+ if (huge_pte_uffd_wp(pte))
+ newpte = huge_pte_mkuffd_wp(newpte);
+ set_huge_pte_at(mm, haddr, ptep, newpte);
folio_set_hugetlb_migratable(new_folio);
/* Make the old page be freed below */
new_folio = page_folio(old_page);
--
2.39.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/6] mm/hugetlb: Fix uffd-wp bit lost when unsharing happens
2023-04-13 23:11 ` [PATCH 2/6] mm/hugetlb: Fix uffd-wp bit lost when unsharing happens Peter Xu
@ 2023-04-14 9:23 ` David Hildenbrand
2023-04-14 22:19 ` Mike Kravetz
1 sibling, 0 replies; 10+ messages in thread
From: David Hildenbrand @ 2023-04-14 9:23 UTC (permalink / raw)
To: Peter Xu, linux-kernel, linux-mm
Cc: Axel Rasmussen, Andrew Morton, Mike Kravetz, Nadav Amit,
Andrea Arcangeli, linux-stable
On 14.04.23 01:11, Peter Xu wrote:
> When we try to unshare a pinned page for a private hugetlb, uffd-wp bit can
> get lost during unsharing. Fix it by carrying it over.
>
> This should be very rare, only if an unsharing happened on a private
> hugetlb page with uffd-wp protected (e.g. in a child which shares the same
> page with parent with UFFD_FEATURE_EVENT_FORK enabled).
>
> Cc: linux-stable <stable@vger.kernel.org>
> Fixes: 166f3ecc0daf ("mm/hugetlb: hook page faults for uffd write protection")
> Reported-by: Mike Kravetz <mike.kravetz@oracle.com>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> mm/hugetlb.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> index 7320e64aacc6..083aae35bff8 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -5637,13 +5637,16 @@ static vm_fault_t hugetlb_wp(struct mm_struct *mm, struct vm_area_struct *vma,
> spin_lock(ptl);
> ptep = hugetlb_walk(vma, haddr, huge_page_size(h));
> if (likely(ptep && pte_same(huge_ptep_get(ptep), pte))) {
> + pte_t newpte = make_huge_pte(vma, &new_folio->page, !unshare);
> +
> /* Break COW or unshare */
> huge_ptep_clear_flush(vma, haddr, ptep);
> mmu_notifier_invalidate_range(mm, range.start, range.end);
> page_remove_rmap(old_page, vma, true);
> hugepage_add_new_anon_rmap(new_folio, vma, haddr);
> - set_huge_pte_at(mm, haddr, ptep,
> - make_huge_pte(vma, &new_folio->page, !unshare));
> + if (huge_pte_uffd_wp(pte))
> + newpte = huge_pte_mkuffd_wp(newpte);
> + set_huge_pte_at(mm, haddr, ptep, newpte);
> folio_set_hugetlb_migratable(new_folio);
> /* Make the old page be freed below */
> new_folio = page_folio(old_page);
LGTM, thanks
Reviewed-by: David Hildenbrand <david@redhat.com>
--
Thanks,
David / dhildenb
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork()
2023-04-13 23:11 ` [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork() Peter Xu
@ 2023-04-14 9:37 ` David Hildenbrand
2023-04-14 9:45 ` Mika Penttilä
2023-04-14 22:17 ` Mike Kravetz
2 siblings, 0 replies; 10+ messages in thread
From: David Hildenbrand @ 2023-04-14 9:37 UTC (permalink / raw)
To: Peter Xu, linux-kernel, linux-mm
Cc: Axel Rasmussen, Andrew Morton, Mike Kravetz, Nadav Amit,
Andrea Arcangeli, linux-stable
On 14.04.23 01:11, Peter Xu wrote:
> There're a bunch of things that were wrong:
>
> - Reading uffd-wp bit from a swap entry should use pte_swp_uffd_wp()
> rather than huge_pte_uffd_wp().
>
> - When copying over a pte, we should drop uffd-wp bit when
> !EVENT_FORK (aka, when !userfaultfd_wp(dst_vma)).
>
> - When doing early CoW for private hugetlb (e.g. when the parent page was
> pinned), uffd-wp bit should be properly carried over if necessary.
>
> No bug reported probably because most people do not even care about these
> corner cases, but they are still bugs and can be exposed by the recent unit
> tests introduced, so fix all of them in one shot.
>
> Cc: linux-stable <stable@vger.kernel.org>
> Fixes: bc70fbf269fd ("mm/hugetlb: handle uffd-wp during fork()")
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> mm/hugetlb.c | 26 ++++++++++++++++----------
> 1 file changed, 16 insertions(+), 10 deletions(-)
>
> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> index f16b25b1a6b9..7320e64aacc6 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -4953,11 +4953,15 @@ static bool is_hugetlb_entry_hwpoisoned(pte_t pte)
>
> static void
> hugetlb_install_folio(struct vm_area_struct *vma, pte_t *ptep, unsigned long addr,
> - struct folio *new_folio)
> + struct folio *new_folio, pte_t old)
> {
Nit: The function now expects old to be !swap_pte. Which works perfectly
fine with existing code -- the function name is a bit generic and
misleading, unfortunately. IMHO, instead of factoring that functionality
out to desperately try keeping copy_hugetlb_page_range() somewhat
readable, we should just have factored out the complete copy+replace
into a copy_hugetlb_page() function -- similar to the ordinary page
handling -- which would have made copy_hugetlb_page_range() more
readable eventually.
Anyhow, unrelated.
> + pte_t newpte = make_huge_pte(vma, &new_folio->page, 1);
> +
> __folio_mark_uptodate(new_folio);
> hugepage_add_new_anon_rmap(new_folio, vma, addr);
> - set_huge_pte_at(vma->vm_mm, addr, ptep, make_huge_pte(vma, &new_folio->page, 1));
> + if (userfaultfd_wp(vma) && huge_pte_uffd_wp(old))
> + newpte = huge_pte_mkuffd_wp(newpte);
> + set_huge_pte_at(vma->vm_mm, addr, ptep, newpte);
> hugetlb_count_add(pages_per_huge_page(hstate_vma(vma)), vma->vm_mm);
> folio_set_hugetlb_migratable(new_folio);
> }
> @@ -5032,14 +5036,11 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> */
> ;
> } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry))) {
> - bool uffd_wp = huge_pte_uffd_wp(entry);
> -
> - if (!userfaultfd_wp(dst_vma) && uffd_wp)
> + if (!userfaultfd_wp(dst_vma))
> entry = huge_pte_clear_uffd_wp(entry);
> set_huge_pte_at(dst, addr, dst_pte, entry);
> } else if (unlikely(is_hugetlb_entry_migration(entry))) {
> swp_entry_t swp_entry = pte_to_swp_entry(entry);
> - bool uffd_wp = huge_pte_uffd_wp(entry);
>
> if (!is_readable_migration_entry(swp_entry) && cow) {
> /*
> @@ -5049,11 +5050,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> swp_entry = make_readable_migration_entry(
> swp_offset(swp_entry));
> entry = swp_entry_to_pte(swp_entry);
> - if (userfaultfd_wp(src_vma) && uffd_wp)
> - entry = huge_pte_mkuffd_wp(entry);
> + if (userfaultfd_wp(src_vma) &&
> + pte_swp_uffd_wp(entry))
> + entry = pte_swp_mkuffd_wp(entry);
> set_huge_pte_at(src, addr, src_pte, entry);
> }
> - if (!userfaultfd_wp(dst_vma) && uffd_wp)
> + if (!userfaultfd_wp(dst_vma))
> entry = huge_pte_clear_uffd_wp(entry);
> set_huge_pte_at(dst, addr, dst_pte, entry);
> } else if (unlikely(is_pte_marker(entry))) {
> @@ -5114,7 +5116,8 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> /* huge_ptep of dst_pte won't change as in child */
> goto again;
> }
> - hugetlb_install_folio(dst_vma, dst_pte, addr, new_folio);
> + hugetlb_install_folio(dst_vma, dst_pte, addr,
> + new_folio, src_pte_old);
> spin_unlock(src_ptl);
> spin_unlock(dst_ptl);
> continue;
> @@ -5132,6 +5135,9 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> entry = huge_pte_wrprotect(entry);
> }
>
> + if (!userfaultfd_wp(dst_vma))
> + entry = huge_pte_clear_uffd_wp(entry);
> +
> set_huge_pte_at(dst, addr, dst_pte, entry);
> hugetlb_count_add(npages, dst);
> }
LGTM
Reviewed-by: David Hildenbrand <david@redhat.com>
--
Thanks,
David / dhildenb
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork()
2023-04-13 23:11 ` [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork() Peter Xu
2023-04-14 9:37 ` David Hildenbrand
@ 2023-04-14 9:45 ` Mika Penttilä
2023-04-14 14:09 ` Peter Xu
2023-04-14 22:17 ` Mike Kravetz
2 siblings, 1 reply; 10+ messages in thread
From: Mika Penttilä @ 2023-04-14 9:45 UTC (permalink / raw)
To: Peter Xu, linux-kernel, linux-mm
Cc: Axel Rasmussen, Andrew Morton, David Hildenbrand, Mike Kravetz,
Nadav Amit, Andrea Arcangeli, linux-stable
On 14.4.2023 2.11, Peter Xu wrote:
> There're a bunch of things that were wrong:
>
> - Reading uffd-wp bit from a swap entry should use pte_swp_uffd_wp()
> rather than huge_pte_uffd_wp().
>
> - When copying over a pte, we should drop uffd-wp bit when
> !EVENT_FORK (aka, when !userfaultfd_wp(dst_vma)).
>
> - When doing early CoW for private hugetlb (e.g. when the parent page was
> pinned), uffd-wp bit should be properly carried over if necessary.
>
> No bug reported probably because most people do not even care about these
> corner cases, but they are still bugs and can be exposed by the recent unit
> tests introduced, so fix all of them in one shot.
>
> Cc: linux-stable <stable@vger.kernel.org>
> Fixes: bc70fbf269fd ("mm/hugetlb: handle uffd-wp during fork()")
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> mm/hugetlb.c | 26 ++++++++++++++++----------
> 1 file changed, 16 insertions(+), 10 deletions(-)
>
> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> index f16b25b1a6b9..7320e64aacc6 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -4953,11 +4953,15 @@ static bool is_hugetlb_entry_hwpoisoned(pte_t pte)
>
> static void
> hugetlb_install_folio(struct vm_area_struct *vma, pte_t *ptep, unsigned long addr,
> - struct folio *new_folio)
> + struct folio *new_folio, pte_t old)
> {
> + pte_t newpte = make_huge_pte(vma, &new_folio->page, 1);
> +
> __folio_mark_uptodate(new_folio);
> hugepage_add_new_anon_rmap(new_folio, vma, addr);
> - set_huge_pte_at(vma->vm_mm, addr, ptep, make_huge_pte(vma, &new_folio->page, 1));
> + if (userfaultfd_wp(vma) && huge_pte_uffd_wp(old))
> + newpte = huge_pte_mkuffd_wp(newpte);
> + set_huge_pte_at(vma->vm_mm, addr, ptep, newpte);
> hugetlb_count_add(pages_per_huge_page(hstate_vma(vma)), vma->vm_mm);
> folio_set_hugetlb_migratable(new_folio);
> }
> @@ -5032,14 +5036,11 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> */
> ;
> } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry))) {
> - bool uffd_wp = huge_pte_uffd_wp(entry);
> -
> - if (!userfaultfd_wp(dst_vma) && uffd_wp)
> + if (!userfaultfd_wp(dst_vma))
> entry = huge_pte_clear_uffd_wp(entry);
> set_huge_pte_at(dst, addr, dst_pte, entry);
> } else if (unlikely(is_hugetlb_entry_migration(entry))) {
> swp_entry_t swp_entry = pte_to_swp_entry(entry);
> - bool uffd_wp = huge_pte_uffd_wp(entry);
>
> if (!is_readable_migration_entry(swp_entry) && cow) {
> /*
> @@ -5049,11 +5050,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> swp_entry = make_readable_migration_entry(
> swp_offset(swp_entry));
> entry = swp_entry_to_pte(swp_entry);
> - if (userfaultfd_wp(src_vma) && uffd_wp)
> - entry = huge_pte_mkuffd_wp(entry);
> + if (userfaultfd_wp(src_vma) &&
> + pte_swp_uffd_wp(entry))
> + entry = pte_swp_mkuffd_wp(entry);
This looks interesting with pte_swp_uffd_wp and pte_swp_mkuffd_wp ?
> set_huge_pte_at(src, addr, src_pte, entry);
> }
> - if (!userfaultfd_wp(dst_vma) && uffd_wp)
> + if (!userfaultfd_wp(dst_vma))
> entry = huge_pte_clear_uffd_wp(entry);
> set_huge_pte_at(dst, addr, dst_pte, entry);
> } else if (unlikely(is_pte_marker(entry))) {
> @@ -5114,7 +5116,8 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> /* huge_ptep of dst_pte won't change as in child */
> goto again;
> }
> - hugetlb_install_folio(dst_vma, dst_pte, addr, new_folio);
> + hugetlb_install_folio(dst_vma, dst_pte, addr,
> + new_folio, src_pte_old);
> spin_unlock(src_ptl);
> spin_unlock(dst_ptl);
> continue;
> @@ -5132,6 +5135,9 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> entry = huge_pte_wrprotect(entry);
> }
>
> + if (!userfaultfd_wp(dst_vma))
> + entry = huge_pte_clear_uffd_wp(entry);
> +
> set_huge_pte_at(dst, addr, dst_pte, entry);
> hugetlb_count_add(npages, dst);
> }
--Mika
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork()
2023-04-14 9:45 ` Mika Penttilä
@ 2023-04-14 14:09 ` Peter Xu
2023-04-14 14:23 ` Mika Penttilä
0 siblings, 1 reply; 10+ messages in thread
From: Peter Xu @ 2023-04-14 14:09 UTC (permalink / raw)
To: Mika Penttilä
Cc: linux-kernel, linux-mm, Axel Rasmussen, Andrew Morton,
David Hildenbrand, Mike Kravetz, Nadav Amit, Andrea Arcangeli,
linux-stable
On Fri, Apr 14, 2023 at 12:45:29PM +0300, Mika Penttilä wrote:
> > } else if (unlikely(is_hugetlb_entry_migration(entry))) {
> > swp_entry_t swp_entry = pte_to_swp_entry(entry);
> > - bool uffd_wp = huge_pte_uffd_wp(entry);
[1]
> > if (!is_readable_migration_entry(swp_entry) && cow) {
> > /*
> > @@ -5049,11 +5050,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> > swp_entry = make_readable_migration_entry(
> > swp_offset(swp_entry));
> > entry = swp_entry_to_pte(swp_entry);
[2]
> > - if (userfaultfd_wp(src_vma) && uffd_wp)
> > - entry = huge_pte_mkuffd_wp(entry);
> > + if (userfaultfd_wp(src_vma) &&
> > + pte_swp_uffd_wp(entry))
> > + entry = pte_swp_mkuffd_wp(entry);
>
>
> This looks interesting with pte_swp_uffd_wp and pte_swp_mkuffd_wp ?
Could you explain what do you mean?
I think these helpers are the right ones to use, as afaict hugetlb
migration should follow the same pte format with !hugetlb. However, I
noticed I did it wrong when dropping the temp var - when at [1], "entry"
still points to the src entry, but at [2] it's already pointing to the
newly created one.. so I think I can't drop the var, a fixup should like:
===8<===
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 083aae35bff8..cd3a9d8f4b70 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -5041,6 +5041,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
set_huge_pte_at(dst, addr, dst_pte, entry);
} else if (unlikely(is_hugetlb_entry_migration(entry))) {
swp_entry_t swp_entry = pte_to_swp_entry(entry);
+ bool uffd_wp = pte_swp_uffd_wp(entry);
if (!is_readable_migration_entry(swp_entry) && cow) {
/*
@@ -5050,8 +5051,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
swp_entry = make_readable_migration_entry(
swp_offset(swp_entry));
entry = swp_entry_to_pte(swp_entry);
- if (userfaultfd_wp(src_vma) &&
- pte_swp_uffd_wp(entry))
+ if (userfaultfd_wp(src_vma) && uffd_wp)
entry = pte_swp_mkuffd_wp(entry);
set_huge_pte_at(src, addr, src_pte, entry);
===8<===
Besides, did I miss something else?
Thanks,
--
Peter Xu
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork()
2023-04-14 14:09 ` Peter Xu
@ 2023-04-14 14:23 ` Mika Penttilä
2023-04-14 15:21 ` Peter Xu
0 siblings, 1 reply; 10+ messages in thread
From: Mika Penttilä @ 2023-04-14 14:23 UTC (permalink / raw)
To: Peter Xu
Cc: linux-kernel, linux-mm, Axel Rasmussen, Andrew Morton,
David Hildenbrand, Mike Kravetz, Nadav Amit, Andrea Arcangeli,
linux-stable
On 14.4.2023 17.09, Peter Xu wrote:
> On Fri, Apr 14, 2023 at 12:45:29PM +0300, Mika Penttilä wrote:
>>> } else if (unlikely(is_hugetlb_entry_migration(entry))) {
>>> swp_entry_t swp_entry = pte_to_swp_entry(entry);
>>> - bool uffd_wp = huge_pte_uffd_wp(entry);
>
> [1]
>
>>> if (!is_readable_migration_entry(swp_entry) && cow) {
>>> /*
>>> @@ -5049,11 +5050,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
>>> swp_entry = make_readable_migration_entry(
>>> swp_offset(swp_entry));
>>> entry = swp_entry_to_pte(swp_entry);
>
> [2]
>
>>> - if (userfaultfd_wp(src_vma) && uffd_wp)
>>> - entry = huge_pte_mkuffd_wp(entry);
>>> + if (userfaultfd_wp(src_vma) &&
>>> + pte_swp_uffd_wp(entry))
>>> + entry = pte_swp_mkuffd_wp(entry);
>>
>>
>> This looks interesting with pte_swp_uffd_wp and pte_swp_mkuffd_wp ?
>
> Could you explain what do you mean?
>
Yes like you noticed also you called pte_swp_mkuffd_wp(entry) iff
pte_swp_uffd_wp(entry) which is of course a nop.
But the fixup not dropping the temp var should work.
> I think these helpers are the right ones to use, as afaict hugetlb
> migration should follow the same pte format with !hugetlb. However, I
> noticed I did it wrong when dropping the temp var - when at [1], "entry"
> still points to the src entry, but at [2] it's already pointing to the
> newly created one.. so I think I can't drop the var, a fixup should like:
>
> ===8<===
> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> index 083aae35bff8..cd3a9d8f4b70 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -5041,6 +5041,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> set_huge_pte_at(dst, addr, dst_pte, entry);
> } else if (unlikely(is_hugetlb_entry_migration(entry))) {
> swp_entry_t swp_entry = pte_to_swp_entry(entry);
> + bool uffd_wp = pte_swp_uffd_wp(entry);
>
> if (!is_readable_migration_entry(swp_entry) && cow) {
> /*
> @@ -5050,8 +5051,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
> swp_entry = make_readable_migration_entry(
> swp_offset(swp_entry));
> entry = swp_entry_to_pte(swp_entry);
> - if (userfaultfd_wp(src_vma) &&
> - pte_swp_uffd_wp(entry))
> + if (userfaultfd_wp(src_vma) && uffd_wp)
> entry = pte_swp_mkuffd_wp(entry);
> set_huge_pte_at(src, addr, src_pte, entry);
> ===8<===
>
> Besides, did I miss something else?
>
> Thanks,
>
--Mika
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork()
2023-04-14 14:23 ` Mika Penttilä
@ 2023-04-14 15:21 ` Peter Xu
0 siblings, 0 replies; 10+ messages in thread
From: Peter Xu @ 2023-04-14 15:21 UTC (permalink / raw)
To: Mika Penttilä
Cc: linux-kernel, linux-mm, Axel Rasmussen, Andrew Morton,
David Hildenbrand, Mike Kravetz, Nadav Amit, Andrea Arcangeli,
linux-stable
On Fri, Apr 14, 2023 at 05:23:12PM +0300, Mika Penttilä wrote:
> But the fixup not dropping the temp var should work.
Ok I see. I'll wait for a few more days for a respin. Thanks,
--
Peter Xu
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork()
2023-04-13 23:11 ` [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork() Peter Xu
2023-04-14 9:37 ` David Hildenbrand
2023-04-14 9:45 ` Mika Penttilä
@ 2023-04-14 22:17 ` Mike Kravetz
2 siblings, 0 replies; 10+ messages in thread
From: Mike Kravetz @ 2023-04-14 22:17 UTC (permalink / raw)
To: Peter Xu
Cc: linux-kernel, linux-mm, Axel Rasmussen, Andrew Morton,
David Hildenbrand, Nadav Amit, Andrea Arcangeli, linux-stable
On 04/13/23 19:11, Peter Xu wrote:
> There're a bunch of things that were wrong:
>
> - Reading uffd-wp bit from a swap entry should use pte_swp_uffd_wp()
> rather than huge_pte_uffd_wp().
That was/is quite confusing to me at least.
>
> - When copying over a pte, we should drop uffd-wp bit when
> !EVENT_FORK (aka, when !userfaultfd_wp(dst_vma)).
>
> - When doing early CoW for private hugetlb (e.g. when the parent page was
> pinned), uffd-wp bit should be properly carried over if necessary.
>
> No bug reported probably because most people do not even care about these
> corner cases, but they are still bugs and can be exposed by the recent unit
> tests introduced, so fix all of them in one shot.
>
> Cc: linux-stable <stable@vger.kernel.org>
> Fixes: bc70fbf269fd ("mm/hugetlb: handle uffd-wp during fork()")
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> mm/hugetlb.c | 26 ++++++++++++++++----------
> 1 file changed, 16 insertions(+), 10 deletions(-)
No issues except losing information in pte entry as pointed out by Mika.
--
Mike Kravetz
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/6] mm/hugetlb: Fix uffd-wp bit lost when unsharing happens
2023-04-13 23:11 ` [PATCH 2/6] mm/hugetlb: Fix uffd-wp bit lost when unsharing happens Peter Xu
2023-04-14 9:23 ` David Hildenbrand
@ 2023-04-14 22:19 ` Mike Kravetz
1 sibling, 0 replies; 10+ messages in thread
From: Mike Kravetz @ 2023-04-14 22:19 UTC (permalink / raw)
To: Peter Xu
Cc: linux-kernel, linux-mm, Axel Rasmussen, Andrew Morton,
David Hildenbrand, Nadav Amit, Andrea Arcangeli, linux-stable
On 04/13/23 19:11, Peter Xu wrote:
> When we try to unshare a pinned page for a private hugetlb, uffd-wp bit can
> get lost during unsharing. Fix it by carrying it over.
>
> This should be very rare, only if an unsharing happened on a private
> hugetlb page with uffd-wp protected (e.g. in a child which shares the same
> page with parent with UFFD_FEATURE_EVENT_FORK enabled).
>
> Cc: linux-stable <stable@vger.kernel.org>
> Fixes: 166f3ecc0daf ("mm/hugetlb: hook page faults for uffd write protection")
> Reported-by: Mike Kravetz <mike.kravetz@oracle.com>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
> mm/hugetlb.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> index 7320e64aacc6..083aae35bff8 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -5637,13 +5637,16 @@ static vm_fault_t hugetlb_wp(struct mm_struct *mm, struct vm_area_struct *vma,
> spin_lock(ptl);
> ptep = hugetlb_walk(vma, haddr, huge_page_size(h));
> if (likely(ptep && pte_same(huge_ptep_get(ptep), pte))) {
> + pte_t newpte = make_huge_pte(vma, &new_folio->page, !unshare);
> +
> /* Break COW or unshare */
> huge_ptep_clear_flush(vma, haddr, ptep);
> mmu_notifier_invalidate_range(mm, range.start, range.end);
> page_remove_rmap(old_page, vma, true);
> hugepage_add_new_anon_rmap(new_folio, vma, haddr);
> - set_huge_pte_at(mm, haddr, ptep,
> - make_huge_pte(vma, &new_folio->page, !unshare));
> + if (huge_pte_uffd_wp(pte))
> + newpte = huge_pte_mkuffd_wp(newpte);
> + set_huge_pte_at(mm, haddr, ptep, newpte);
> folio_set_hugetlb_migratable(new_folio);
> /* Make the old page be freed below */
> new_folio = page_folio(old_page);
> --
> 2.39.1
>
Thanks! Looks good,
Reviewed-by: Mike Kravetz <mike.kravetz@oracle.com>
--
Mike Kravetz
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2023-04-14 22:20 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20230413231120.544685-1-peterx@redhat.com>
2023-04-13 23:11 ` [PATCH 1/6] mm/hugetlb: Fix uffd-wp during fork() Peter Xu
2023-04-14 9:37 ` David Hildenbrand
2023-04-14 9:45 ` Mika Penttilä
2023-04-14 14:09 ` Peter Xu
2023-04-14 14:23 ` Mika Penttilä
2023-04-14 15:21 ` Peter Xu
2023-04-14 22:17 ` Mike Kravetz
2023-04-13 23:11 ` [PATCH 2/6] mm/hugetlb: Fix uffd-wp bit lost when unsharing happens Peter Xu
2023-04-14 9:23 ` David Hildenbrand
2023-04-14 22:19 ` Mike Kravetz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox