mbox series

[00/23] userfaultfd-wp: Support shmem and hugetlbfs

Message ID 20210323004912.35132-1-peterx@redhat.com (mailing list archive)
Headers show
Series userfaultfd-wp: Support shmem and hugetlbfs | expand

Message

Peter Xu March 23, 2021, 12:48 a.m. UTC
This patchset is based on tag v5.12-rc3-mmots-2021-03-17-22-26.  To run the
selftest, need to apply the two patches to fix minor mode page leak:

https://lore.kernel.org/lkml/20210322175132.36659-1-peterx@redhat.com/
https://lore.kernel.org/lkml/20210322204836.1650221-1-axelrasmussen@google.com/

Since I didn't get any NACK in the previous RFC series for months, I decided to
remove the RFC tag starting from this version, so this is v1 of uffd-wp support
on shmem & hugetlb.

The whole series can also be found online [1].

The major comment I'd like to get is on the new idea of swap special pte.  That
comes from suggestions from both Hugh and Andrea and I appreciated a lot for
those discussions.

In short, the so-called "swap special pte" in this patchset is a new type of
pte that doesn't exist in the past, but it got used initially in this series in
file-backed memories.  It is used to persist information even if the ptes got
dropped meanwhile when the page cache still existed.  For example, when
splitting a file-backed huge pmd, we could be simply dropping the pmd entry
then wait until another fault coming.  It's okay in the past since all
information in the pte can be retained from the page cache when the next page
fault triggers.  However in this case, uffd-wp is per-pte information which
cannot be kept in page cache, so that information needs to be maintained
somehow still in the pgtable entry, even if the pgtable entry is going to be
dropped.  Here instead of replacing with a none entry, we used the "swap
special pte".  Then when the next page fault triggers, we can observe orig_pte
to retain this information.

I'm copy-pasting some commit message from the patch "mm/swap: Introduce the
idea of special swap ptes", where it tried to explain this pte in another angle:

    We used to have special swap entries, like migration entries, hw-poison
    entries, device private entries, etc.

    Those "special swap entries" reside in the range that they need to be at least
    swap entries first, and their types are decided by swp_type(entry).

    This patch introduces another idea called "special swap ptes".

    It's very easy to get confused against "special swap entries", but a speical
    swap pte should never contain a swap entry at all.  It means, it's illegal to
    call pte_to_swp_entry() upon a special swap pte.

    Make the uffd-wp special pte to be the first special swap pte.

    Before this patch, is_swap_pte()==true means one of the below:

       (a.1) The pte has a normal swap entry (non_swap_entry()==false).  For
             example, when an anonymous page got swapped out.

       (a.2) The pte has a special swap entry (non_swap_entry()==true).  For
             example, a migration entry, a hw-poison entry, etc.

    After this patch, is_swap_pte()==true means one of the below, where case (b) is
    added:

     (a) The pte contains a swap entry.

       (a.1) The pte has a normal swap entry (non_swap_entry()==false).  For
             example, when an anonymous page got swapped out.

       (a.2) The pte has a special swap entry (non_swap_entry()==true).  For
             example, a migration entry, a hw-poison entry, etc.

     (b) The pte does not contain a swap entry at all (so it cannot be passed
         into pte_to_swp_entry()).  For example, uffd-wp special swap pte.

Hugetlbfs needs similar thing because it's also file-backed.  I directly reused
the same special pte there, though the shmem/hugetlb change on supporting this
new pte is different since they don't share code path a lot.

Patch layout
============

Part (1): Shmem support, this is where the special swap pte is introduced.
Some zap rework is needed within the process:

  shmem/userfaultfd: Take care of UFFDIO_COPY_MODE_WP
  mm: Clear vmf->pte after pte_unmap_same() returns
  mm/userfaultfd: Introduce special pte for unmapped file-backed mem
  mm/swap: Introduce the idea of special swap ptes
  shmem/userfaultfd: Handle uffd-wp special pte in page fault handler
  mm: Drop first_index/last_index in zap_details
  mm: Introduce zap_details.zap_flags
  mm: Introduce ZAP_FLAG_SKIP_SWAP
  mm: Pass zap_flags into unmap_mapping_pages()
  shmem/userfaultfd: Persist uffd-wp bit across zapping for file-backed
  shmem/userfaultfd: Allow wr-protect none pte for file-backed mem
  shmem/userfaultfd: Allows file-back mem to be uffd wr-protected on thps
  shmem/userfaultfd: Handle the left-overed special swap ptes
  shmem/userfaultfd: Pass over uffd-wp special swap pte when fork()

Part (2): Hugetlb support, we need to disable huge pmd sharing for uffd-wp
because not compatible just like uffd minor mode.  The rest is the changes
required to teach hugetlbfs understand the special swap pte too that introduced
with the uffd-wp change:

  hugetlb/userfaultfd: Hook page faults for uffd write protection
  hugetlb/userfaultfd: Take care of UFFDIO_COPY_MODE_WP
  hugetlb/userfaultfd: Handle UFFDIO_WRITEPROTECT
  hugetlb: Pass vma into huge_pte_alloc()
  hugetlb/userfaultfd: Forbid huge pmd sharing when uffd enabled
  mm/hugetlb: Introduce huge version of special swap pte helpers
  mm/hugetlb: Move flush_hugetlb_tlb_range() into hugetlb.h
  hugetlb/userfaultfd: Unshare all pmds for hugetlbfs when register wp
  hugetlb/userfaultfd: Handle uffd-wp special pte in hugetlb pf handler
  hugetlb/userfaultfd: Allow wr-protect none ptes
  hugetlb/userfaultfd: Only drop uffd-wp special pte if required

Part (3): Enable both features in code and test

  userfaultfd: Enable write protection for shmem & hugetlbfs
  userfaultfd/selftests: Enable uffd-wp for shmem/hugetlbfs

Tests
=========

I've tested it using either userfaultfd kselftest program, but also with
umapsort [2] which should be even stricter.  Tested page swapping in/out during
umapsort.

If anyone would like to try umapsort, need to use an extremely hacked version
of umap library [3], because by default umap only supports anonymous.  So to
test it we need to build [3] then [2].

Any comment would be greatly welcomed.  Thanks,

[1] https://github.com/xzpeter/linux/tree/uffd-wp-shmem-hugetlbfs
[2] https://github.com/LLNL/umap-apps
[3] https://github.com/xzpeter/umap/tree/peter-shmem-hugetlbfs

Peter Xu (23):
  shmem/userfaultfd: Take care of UFFDIO_COPY_MODE_WP
  mm: Clear vmf->pte after pte_unmap_same() returns
  mm/userfaultfd: Introduce special pte for unmapped file-backed mem
  mm/swap: Introduce the idea of special swap ptes
  shmem/userfaultfd: Handle uffd-wp special pte in page fault handler
  mm: Drop first_index/last_index in zap_details
  mm: Introduce zap_details.zap_flags
  mm: Introduce ZAP_FLAG_SKIP_SWAP
  mm: Pass zap_flags into unmap_mapping_pages()
  shmem/userfaultfd: Persist uffd-wp bit across zapping for file-backed
  shmem/userfaultfd: Allow wr-protect none pte for file-backed mem
  shmem/userfaultfd: Allows file-back mem to be uffd wr-protected on
    thps
  shmem/userfaultfd: Handle the left-overed special swap ptes
  shmem/userfaultfd: Pass over uffd-wp special swap pte when fork()
  hugetlb/userfaultfd: Hook page faults for uffd write protection
  hugetlb/userfaultfd: Take care of UFFDIO_COPY_MODE_WP
  hugetlb/userfaultfd: Handle UFFDIO_WRITEPROTECT
  mm/hugetlb: Introduce huge version of special swap pte helpers
  hugetlb/userfaultfd: Handle uffd-wp special pte in hugetlb pf handler
  hugetlb/userfaultfd: Allow wr-protect none ptes
  hugetlb/userfaultfd: Only drop uffd-wp special pte if required
  mm/userfaultfd: Enable write protection for shmem & hugetlbfs
  userfaultfd/selftests: Enable uffd-wp for shmem/hugetlbfs

 arch/arm64/kernel/mte.c                  |   2 +-
 arch/x86/include/asm/pgtable.h           |  28 +++
 fs/dax.c                                 |  10 +-
 fs/hugetlbfs/inode.c                     |  15 +-
 fs/proc/task_mmu.c                       |  14 +-
 fs/userfaultfd.c                         |  38 ++--
 include/asm-generic/hugetlb.h            |  10 +
 include/asm-generic/pgtable_uffd.h       |   3 +
 include/linux/hugetlb.h                  |  25 ++-
 include/linux/mm.h                       |  50 ++++-
 include/linux/mm_inline.h                |  43 ++++
 include/linux/shmem_fs.h                 |   5 +-
 include/linux/swapops.h                  |  39 +++-
 include/linux/userfaultfd_k.h            |  46 +++++
 include/uapi/linux/userfaultfd.h         |   7 +-
 mm/gup.c                                 |   2 +-
 mm/hmm.c                                 |   2 +-
 mm/hugetlb.c                             | 167 ++++++++++++---
 mm/khugepaged.c                          |  14 +-
 mm/madvise.c                             |   4 +-
 mm/memcontrol.c                          |   2 +-
 mm/memory.c                              | 249 ++++++++++++++++++-----
 mm/migrate.c                             |   4 +-
 mm/mincore.c                             |   2 +-
 mm/mprotect.c                            |  63 +++++-
 mm/mremap.c                              |   2 +-
 mm/page_vma_mapped.c                     |   6 +-
 mm/rmap.c                                |   8 +
 mm/shmem.c                               |  31 ++-
 mm/swapfile.c                            |   2 +-
 mm/truncate.c                            |  17 +-
 mm/userfaultfd.c                         |  37 ++--
 tools/testing/selftests/vm/userfaultfd.c |   9 +-
 33 files changed, 776 insertions(+), 180 deletions(-)

Comments

Peter Xu March 23, 2021, 12:54 a.m. UTC | #1
On Mon, Mar 22, 2021 at 08:48:49PM -0400, Peter Xu wrote:
> This patchset is based on tag v5.12-rc3-mmots-2021-03-17-22-26.  To run the
> selftest, need to apply the two patches to fix minor mode page leak:
> 
> https://lore.kernel.org/lkml/20210322175132.36659-1-peterx@redhat.com/
> https://lore.kernel.org/lkml/20210322204836.1650221-1-axelrasmussen@google.com/
> 
> Since I didn't get any NACK in the previous RFC series for months, I decided to
> remove the RFC tag starting from this version, so this is v1 of uffd-wp support
> on shmem & hugetlb.

Attaching changelog, rfc->v1:
- fix up syzbot reported issue
- add a new feature bit UFFD_FEATURE_WP_SHMEM_HUGETLBFS exported in uapi, so
  that apps can detect the new kernel capability.
- check for all pte_to_swp_entry callers too (hmm, etc.) [JasonG]
- dropped the first few patches that are not directly related to this series; I
  will post them separately as standalone series

Add Cc too (I'll remember to send the series with full cc list next time..).

Thanks,
Peter Xu April 21, 2021, 4:03 p.m. UTC | #2
On Mon, Mar 22, 2021 at 08:48:49PM -0400, Peter Xu wrote:
> This patchset is based on tag v5.12-rc3-mmots-2021-03-17-22-26.  To run the
> selftest, need to apply the two patches to fix minor mode page leak:
> 
> https://lore.kernel.org/lkml/20210322175132.36659-1-peterx@redhat.com/
> https://lore.kernel.org/lkml/20210322204836.1650221-1-axelrasmussen@google.com/
> 
> Since I didn't get any NACK in the previous RFC series for months, I decided to
> remove the RFC tag starting from this version, so this is v1 of uffd-wp support
> on shmem & hugetlb.
> 
> The whole series can also be found online [1].
> 
> The major comment I'd like to get is on the new idea of swap special pte.  That
> comes from suggestions from both Hugh and Andrea and I appreciated a lot for
> those discussions.
> 
> In short, the so-called "swap special pte" in this patchset is a new type of
> pte that doesn't exist in the past, but it got used initially in this series in
> file-backed memories.  It is used to persist information even if the ptes got
> dropped meanwhile when the page cache still existed.  For example, when
> splitting a file-backed huge pmd, we could be simply dropping the pmd entry
> then wait until another fault coming.  It's okay in the past since all
> information in the pte can be retained from the page cache when the next page
> fault triggers.  However in this case, uffd-wp is per-pte information which
> cannot be kept in page cache, so that information needs to be maintained
> somehow still in the pgtable entry, even if the pgtable entry is going to be
> dropped.  Here instead of replacing with a none entry, we used the "swap
> special pte".  Then when the next page fault triggers, we can observe orig_pte
> to retain this information.
> 
> I'm copy-pasting some commit message from the patch "mm/swap: Introduce the
> idea of special swap ptes", where it tried to explain this pte in another angle:
> 
>     We used to have special swap entries, like migration entries, hw-poison
>     entries, device private entries, etc.
> 
>     Those "special swap entries" reside in the range that they need to be at least
>     swap entries first, and their types are decided by swp_type(entry).
> 
>     This patch introduces another idea called "special swap ptes".
> 
>     It's very easy to get confused against "special swap entries", but a speical
>     swap pte should never contain a swap entry at all.  It means, it's illegal to
>     call pte_to_swp_entry() upon a special swap pte.
> 
>     Make the uffd-wp special pte to be the first special swap pte.
> 
>     Before this patch, is_swap_pte()==true means one of the below:
> 
>        (a.1) The pte has a normal swap entry (non_swap_entry()==false).  For
>              example, when an anonymous page got swapped out.
> 
>        (a.2) The pte has a special swap entry (non_swap_entry()==true).  For
>              example, a migration entry, a hw-poison entry, etc.
> 
>     After this patch, is_swap_pte()==true means one of the below, where case (b) is
>     added:
> 
>      (a) The pte contains a swap entry.
> 
>        (a.1) The pte has a normal swap entry (non_swap_entry()==false).  For
>              example, when an anonymous page got swapped out.
> 
>        (a.2) The pte has a special swap entry (non_swap_entry()==true).  For
>              example, a migration entry, a hw-poison entry, etc.
> 
>      (b) The pte does not contain a swap entry at all (so it cannot be passed
>          into pte_to_swp_entry()).  For example, uffd-wp special swap pte.
> 
> Hugetlbfs needs similar thing because it's also file-backed.  I directly reused
> the same special pte there, though the shmem/hugetlb change on supporting this
> new pte is different since they don't share code path a lot.
> 
> Patch layout
> ============
> 
> Part (1): Shmem support, this is where the special swap pte is introduced.
> Some zap rework is needed within the process:
> 
>   shmem/userfaultfd: Take care of UFFDIO_COPY_MODE_WP
>   mm: Clear vmf->pte after pte_unmap_same() returns
>   mm/userfaultfd: Introduce special pte for unmapped file-backed mem
>   mm/swap: Introduce the idea of special swap ptes
>   shmem/userfaultfd: Handle uffd-wp special pte in page fault handler
>   mm: Drop first_index/last_index in zap_details
>   mm: Introduce zap_details.zap_flags
>   mm: Introduce ZAP_FLAG_SKIP_SWAP
>   mm: Pass zap_flags into unmap_mapping_pages()
>   shmem/userfaultfd: Persist uffd-wp bit across zapping for file-backed
>   shmem/userfaultfd: Allow wr-protect none pte for file-backed mem
>   shmem/userfaultfd: Allows file-back mem to be uffd wr-protected on thps
>   shmem/userfaultfd: Handle the left-overed special swap ptes
>   shmem/userfaultfd: Pass over uffd-wp special swap pte when fork()
> 
> Part (2): Hugetlb support, we need to disable huge pmd sharing for uffd-wp
> because not compatible just like uffd minor mode.  The rest is the changes
> required to teach hugetlbfs understand the special swap pte too that introduced
> with the uffd-wp change:
> 
>   hugetlb/userfaultfd: Hook page faults for uffd write protection
>   hugetlb/userfaultfd: Take care of UFFDIO_COPY_MODE_WP
>   hugetlb/userfaultfd: Handle UFFDIO_WRITEPROTECT
>   hugetlb: Pass vma into huge_pte_alloc()
>   hugetlb/userfaultfd: Forbid huge pmd sharing when uffd enabled
>   mm/hugetlb: Introduce huge version of special swap pte helpers
>   mm/hugetlb: Move flush_hugetlb_tlb_range() into hugetlb.h
>   hugetlb/userfaultfd: Unshare all pmds for hugetlbfs when register wp
>   hugetlb/userfaultfd: Handle uffd-wp special pte in hugetlb pf handler
>   hugetlb/userfaultfd: Allow wr-protect none ptes
>   hugetlb/userfaultfd: Only drop uffd-wp special pte if required
> 
> Part (3): Enable both features in code and test
> 
>   userfaultfd: Enable write protection for shmem & hugetlbfs
>   userfaultfd/selftests: Enable uffd-wp for shmem/hugetlbfs
> 
> Tests
> =========
> 
> I've tested it using either userfaultfd kselftest program, but also with
> umapsort [2] which should be even stricter.  Tested page swapping in/out during
> umapsort.
> 
> If anyone would like to try umapsort, need to use an extremely hacked version
> of umap library [3], because by default umap only supports anonymous.  So to
> test it we need to build [3] then [2].
> 
> Any comment would be greatly welcomed.  Thanks,
> 
> [1] https://github.com/xzpeter/linux/tree/uffd-wp-shmem-hugetlbfs
> [2] https://github.com/LLNL/umap-apps
> [3] https://github.com/xzpeter/umap/tree/peter-shmem-hugetlbfs

Hugh, Mike, Andrew,

Any comment for this series?

Thanks,
Mike Kravetz April 21, 2021, 9:39 p.m. UTC | #3
On 4/21/21 9:03 AM, Peter Xu wrote:
> Hugh, Mike, Andrew,
> 
> Any comment for this series?
> 

Sorry Peter, always get preempted with something else.

I'll start looking at the hugetlb specific changes and back my way into
swap special pte support.  I feel qualified to review the hugetlb stuff
and hope others will join in on the common infrastructure changes.
Peter Xu April 22, 2021, 1:16 a.m. UTC | #4
On Wed, Apr 21, 2021 at 02:39:38PM -0700, Mike Kravetz wrote:
> Sorry Peter, always get preempted with something else.

No worry.

> 
> I'll start looking at the hugetlb specific changes and back my way into
> swap special pte support.  I feel qualified to review the hugetlb stuff
> and hope others will join in on the common infrastructure changes.

That'll be great; thanks Mike!