ANDROID: userfaultfd: add MMAP_TRYLOCK mode for COPY/ZEROPAGE

In case mmap_lock is contended, it is possible that userspace can spend
time performing other tasks rather than waiting in uninterruptible-sleep
state for the lock to become available. Even if no other task is
available, it is better to yield or sleep rather than adding contention
to already contended lock.

We introduce MMAP_TRYLOCK mode so that when possible, userspace can
request to use mmap_read_trylock(), returning -EAGAIN if and when it
fails.

Bug: 320478828
Change-Id: I2d196fd317e054af03dbd35ac1b0c7634cb370dc
Signed-off-by: Lokesh Gidra <lokeshgidra@google.com>
This commit is contained in:
Lokesh Gidra 2024-03-08 10:04:39 -08:00
parent 0ebc4699bd
commit e3aabbf867
4 changed files with 22 additions and 10 deletions

View File

@ -1769,7 +1769,9 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx,
ret = -EINVAL;
if (uffdio_copy.src + uffdio_copy.len <= uffdio_copy.src)
goto out;
if (uffdio_copy.mode & ~(UFFDIO_COPY_MODE_DONTWAKE|UFFDIO_COPY_MODE_WP))
if (uffdio_copy.mode & ~(UFFDIO_COPY_MODE_DONTWAKE|
UFFDIO_COPY_MODE_WP|
UFFDIO_COPY_MODE_MMAP_TRYLOCK))
goto out;
if (mmget_not_zero(ctx->mm)) {
ret = mcopy_atomic(ctx->mm, uffdio_copy.dst, uffdio_copy.src,
@ -1820,13 +1822,14 @@ static int userfaultfd_zeropage(struct userfaultfd_ctx *ctx,
if (ret)
goto out;
ret = -EINVAL;
if (uffdio_zeropage.mode & ~UFFDIO_ZEROPAGE_MODE_DONTWAKE)
if (uffdio_zeropage.mode & ~(UFFDIO_ZEROPAGE_MODE_DONTWAKE|
UFFDIO_ZEROPAGE_MODE_MMAP_TRYLOCK))
goto out;
if (mmget_not_zero(ctx->mm)) {
ret = mfill_zeropage(ctx->mm, uffdio_zeropage.range.start,
uffdio_zeropage.range.len,
&ctx->mmap_changing);
&ctx->mmap_changing, uffdio_zeropage.mode);
mmput(ctx->mm);
} else {
return -ESRCH;

View File

@ -33,6 +33,9 @@
#define UFFD_SHARED_FCNTL_FLAGS (O_CLOEXEC | O_NONBLOCK)
#define UFFD_FLAGS_SET (EFD_SHARED_FCNTL_FLAGS)
static_assert(UFFDIO_ZEROPAGE_MODE_MMAP_TRYLOCK == UFFDIO_COPY_MODE_MMAP_TRYLOCK);
#define UFFDIO_MODE_MMAP_TRYLOCK UFFDIO_COPY_MODE_MMAP_TRYLOCK
extern int sysctl_unprivileged_userfaultfd;
extern vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason);
@ -65,9 +68,8 @@ extern ssize_t mcopy_atomic(struct mm_struct *dst_mm, unsigned long dst_start,
unsigned long src_start, unsigned long len,
bool *mmap_changing, __u64 mode);
extern ssize_t mfill_zeropage(struct mm_struct *dst_mm,
unsigned long dst_start,
unsigned long len,
bool *mmap_changing);
unsigned long dst_start, unsigned long len,
bool *mmap_changing, __u64 mode);
extern ssize_t mcopy_continue(struct mm_struct *dst_mm, unsigned long dst_start,
unsigned long len, bool *mmap_changing);
extern int mwriteprotect_range(struct mm_struct *dst_mm,

View File

@ -237,6 +237,7 @@ struct uffdio_copy {
* according to the uffdio_register.ioctls.
*/
#define UFFDIO_COPY_MODE_WP ((__u64)1<<1)
#define UFFDIO_COPY_MODE_MMAP_TRYLOCK ((__u64)1<<63)
__u64 mode;
/*
@ -249,6 +250,7 @@ struct uffdio_copy {
struct uffdio_zeropage {
struct uffdio_range range;
#define UFFDIO_ZEROPAGE_MODE_DONTWAKE ((__u64)1<<0)
#define UFFDIO_ZEROPAGE_MODE_MMAP_TRYLOCK ((__u64)1<<63)
__u64 mode;
/*

View File

@ -559,14 +559,19 @@ static __always_inline ssize_t __mcopy_atomic(struct mm_struct *dst_mm,
copied = 0;
page = NULL;
retry:
mmap_read_lock(dst_mm);
err = -EAGAIN;
if (mode & UFFDIO_MODE_MMAP_TRYLOCK) {
if (!mmap_read_trylock(dst_mm))
goto out;
} else {
mmap_read_lock(dst_mm);
}
/*
* If memory mappings are changing because of non-cooperative
* operation (e.g. mremap) running in parallel, bail out and
* request the user to retry later
*/
err = -EAGAIN;
if (mmap_changing && READ_ONCE(*mmap_changing))
goto out_unlock;
@ -708,10 +713,10 @@ ssize_t mcopy_atomic(struct mm_struct *dst_mm, unsigned long dst_start,
}
ssize_t mfill_zeropage(struct mm_struct *dst_mm, unsigned long start,
unsigned long len, bool *mmap_changing)
unsigned long len, bool *mmap_changing, __u64 mode)
{
return __mcopy_atomic(dst_mm, start, 0, len, MCOPY_ATOMIC_ZEROPAGE,
mmap_changing, 0);
mmap_changing, mode);
}
ssize_t mcopy_continue(struct mm_struct *dst_mm, unsigned long start,