Re: [PATCH v3 2/3] userfaultfd: UFFDIO_MOVE uABI

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 12.10.23 22:11, Peter Xu wrote:
On Mon, Oct 09, 2023 at 05:29:08PM +0100, Lokesh Gidra wrote:
On Mon, Oct 9, 2023 at 5:24 PM David Hildenbrand <david@xxxxxxxxxx> wrote:

On 09.10.23 18:21, Suren Baghdasaryan wrote:
On Mon, Oct 9, 2023 at 7:38 AM David Hildenbrand <david@xxxxxxxxxx> wrote:

On 09.10.23 08:42, Suren Baghdasaryan wrote:
From: Andrea Arcangeli <aarcange@xxxxxxxxxx>

Implement the uABI of UFFDIO_MOVE ioctl.
UFFDIO_COPY performs ~20% better than UFFDIO_MOVE when the application
needs pages to be allocated [1]. However, with UFFDIO_MOVE, if pages are
available (in userspace) for recycling, as is usually the case in heap
compaction algorithms, then we can avoid the page allocation and memcpy
(done by UFFDIO_COPY). Also, since the pages are recycled in the
userspace, we avoid the need to release (via madvise) the pages back to
the kernel [2].
We see over 40% reduction (on a Google pixel 6 device) in the compacting
thread’s completion time by using UFFDIO_MOVE vs. UFFDIO_COPY. This was
measured using a benchmark that emulates a heap compaction implementation
using userfaultfd (to allow concurrent accesses by application threads).
More details of the usecase are explained in [2].
Furthermore, UFFDIO_MOVE enables moving swapped-out pages without
touching them within the same vma. Today, it can only be done by mremap,
however it forces splitting the vma.

[1] https://lore.kernel.org/all/1425575884-2574-1-git-send-email-aarcange@xxxxxxxxxx/
[2] https://lore.kernel.org/linux-mm/CA+EESO4uO84SSnBhArH4HvLNhaUQ5nZKNKXqxRCyjniNVjp0Aw@xxxxxxxxxxxxxx/

Update for the ioctl_userfaultfd(2)  manpage:

      UFFDIO_MOVE
          (Since Linux xxx)  Move a continuous memory chunk into the
          userfault registered range and optionally wake up the blocked
          thread. The source and destination addresses and the number of
          bytes to move are specified by the src, dst, and len fields of
          the uffdio_move structure pointed to by argp:

              struct uffdio_move {
                  __u64 dst;    /* Destination of move */
                  __u64 src;    /* Source of move */
                  __u64 len;    /* Number of bytes to move */
                  __u64 mode;   /* Flags controlling behavior of move */
                  __s64 move;   /* Number of bytes moved, or negated error */
              };

          The following value may be bitwise ORed in mode to change the
          behavior of the UFFDIO_MOVE operation:

          UFFDIO_MOVE_MODE_DONTWAKE
                 Do not wake up the thread that waits for page-fault
                 resolution

          UFFDIO_MOVE_MODE_ALLOW_SRC_HOLES
                 Allow holes in the source virtual range that is being moved.
                 When not specified, the holes will result in ENOENT error.
                 When specified, the holes will be accounted as successfully
                 moved memory. This is mostly useful to move hugepage aligned
                 virtual regions without knowing if there are transparent
                 hugepages in the regions or not, but preventing the risk of
                 having to split the hugepage during the operation.

          The move field is used by the kernel to return the number of
          bytes that was actually moved, or an error (a negated errno-
          style value).  If the value returned in move doesn't match the
          value that was specified in len, the operation fails with the
          error EAGAIN.  The move field is output-only; it is not read by
          the UFFDIO_MOVE operation.

          The operation may fail for various reasons. Usually, remapping of
          pages that are not exclusive to the given process fail; once KSM
          might deduplicate pages or fork() COW-shares pages during fork()
          with child processes, they are no longer exclusive. Further, the
          kernel might only perform lightweight checks for detecting whether
          the pages are exclusive, and return -EBUSY in case that check fails.
          To make the operation more likely to succeed, KSM should be
          disabled, fork() should be avoided or MADV_DONTFORK should be
          configured for the source VMA before fork().

          This ioctl(2) operation returns 0 on success.  In this case, the
          entire area was moved.  On error, -1 is returned and errno is
          set to indicate the error.  Possible errors include:

          EAGAIN The number of bytes moved (i.e., the value returned in
                 the move field) does not equal the value that was
                 specified in the len field.

          EINVAL Either dst or len was not a multiple of the system page
                 size, or the range specified by src and len or dst and len
                 was invalid.

          EINVAL An invalid bit was specified in the mode field.

          ENOENT
                 The source virtual memory range has unmapped holes and
                 UFFDIO_MOVE_MODE_ALLOW_SRC_HOLES is not set.

          EEXIST
                 The destination virtual memory range is fully or partially
                 mapped.

          EBUSY
                 The pages in the source virtual memory range are not
                 exclusive to the process. The kernel might only perform
                 lightweight checks for detecting whether the pages are
                 exclusive. To make the operation more likely to succeed,
                 KSM should be disabled, fork() should be avoided or
                 MADV_DONTFORK should be configured for the source virtual
                 memory area before fork().

          ENOMEM Allocating memory needed for the operation failed.

          ESRCH
                 The faulting process has exited at the time of a
                 UFFDIO_MOVE operation.


A general comment simply because I realized that just now: does anything
speak against limiting the operations now to a single MM?

The use cases I heard so far don't need it. If ever required, we could
consider extending it.

Let's reduce complexity and KIS unless really required.

Let me check if there are use cases that require moves between MMs.
Andrea seems to have put considerable effort to make it work between
MMs and it would be a pity to lose that. I can send a follow-up patch
to recover that functionality and even if it does not get merged, it
can be used in the future as a reference. But first let me check if we
can drop it.

For the compaction use case that we have it's fine to limit it to
single MM. However, for general use I think Peter will have a better
idea.


Hi Peter,

I used to have the same thought with David on whether we can simplify the
design to e.g. limit it to single mm.  Then I found that the trickiest is
actually patch 1 together with the anon_vma manipulations, and the problem
is that's not avoidable even if we restrict the api to apply on single mm.

What else we can benefit from single mm?  One less mmap read lock, but
probably that's all we can get; IIUC we need to keep most of the rest of
the code, e.g. pgtable walks, double pgtable lockings, etc.

No existing mechanisms move anon pages between unrelated processes, that naturally makes me nervous if we're doing it "just because we can".


Actually, even though I have no solid clue, but I had a feeling that there
can be some interesting way to leverage this across-mm movement, while
keeping things all safe (by e.g. elaborately requiring other proc to create
uffd and deliver to this proc).

Okay, but no real use cases yet.


Considering Andrea's original version already contains those bits and all
above, I'd vote that we go ahead with supporting two MMs.

You can do nasty things with that, as it stands, on the upstream codebase.

If you pin the page in src_mm and move it to dst_mm, you successfully broke an invariant that "exclusive" means "no other references from other processes". That page is marked exclusive but it is, in fact, not exclusive.

Once you achieved that, you can easily have src_mm not have MMF_HAS_PINNED, so you can just COW-share that page. Now you successfully broke the invariant that COW-shared pages must not be pinned. And you can even trigger VM_BUG_ONs, like in sanity_check_pinned_pages().

Can it all be fixed? Sure, with more complexity. For something without clear motivation, I'll have to pass.

Once there is real demand, we can revisit it and explore what else we would have to take care of (I don't know how memcg behaves when moving between completely unrelated processes, maybe that works as expected, I don't know and I have no time to spare on reviewing features with no real use cases) and announce it as a new feature.


Note: that (with only reading the documentation) it also kept me wondering how the MMs are even implied from

       struct uffdio_move {
           __u64 dst;    /* Destination of move */
           __u64 src;    /* Source of move */
           __u64 len;    /* Number of bytes to move */
           __u64 mode;   /* Flags controlling behavior of move */
           __s64 move;   /* Number of bytes moved, or negated error */
       };

That probably has to be documented as well, in which address space dst and src reside.

--
Cheers,

David / dhildenb





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux