Skip to content

Commit

Permalink
userfaultfd: non-cooperative: add mremap() event
Browse files Browse the repository at this point in the history
The event denotes that an area [start:end] moves to different location.
Length change isn't reported as "new" addresses, if they appear on the
uffd reader side they will not contain any data and the latter can just
zeromap them.

Waiting for the event ACK is also done outside of mmap sem, as for fork
event.

Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Pavel Emelyanov <[email protected]>
Signed-off-by: Mike Rapoport <[email protected]>
Signed-off-by: Andrea Arcangeli <[email protected]>
Cc: "Dr. David Alan Gilbert" <[email protected]>
Cc: Hillf Danton <[email protected]>
Cc: Michael Rapoport <[email protected]>
Cc: Mike Kravetz <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
xemul authored and torvalds committed Feb 23, 2017
1 parent d3aadc8 commit 72f8765
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 6 deletions.
37 changes: 37 additions & 0 deletions fs/userfaultfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,43 @@ void dup_userfaultfd_complete(struct list_head *fcs)
}
}

void mremap_userfaultfd_prep(struct vm_area_struct *vma,
struct vm_userfaultfd_ctx *vm_ctx)
{
struct userfaultfd_ctx *ctx;

ctx = vma->vm_userfaultfd_ctx.ctx;
if (ctx && (ctx->features & UFFD_FEATURE_EVENT_REMAP)) {
vm_ctx->ctx = ctx;
userfaultfd_ctx_get(ctx);
}
}

void mremap_userfaultfd_complete(struct vm_userfaultfd_ctx vm_ctx,
unsigned long from, unsigned long to,
unsigned long len)
{
struct userfaultfd_ctx *ctx = vm_ctx.ctx;
struct userfaultfd_wait_queue ewq;

if (!ctx)
return;

if (to & ~PAGE_MASK) {
userfaultfd_ctx_put(ctx);
return;
}

msg_init(&ewq.msg);

ewq.msg.event = UFFD_EVENT_REMAP;
ewq.msg.arg.remap.from = from;
ewq.msg.arg.remap.to = to;
ewq.msg.arg.remap.len = len;

userfaultfd_event_wait_completion(ctx, &ewq);
}

static int userfaultfd_release(struct inode *inode, struct file *file)
{
struct userfaultfd_ctx *ctx = file->private_data;
Expand Down
17 changes: 17 additions & 0 deletions include/linux/userfaultfd_k.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ static inline bool userfaultfd_armed(struct vm_area_struct *vma)
extern int dup_userfaultfd(struct vm_area_struct *, struct list_head *);
extern void dup_userfaultfd_complete(struct list_head *);

extern void mremap_userfaultfd_prep(struct vm_area_struct *,
struct vm_userfaultfd_ctx *);
extern void mremap_userfaultfd_complete(struct vm_userfaultfd_ctx,
unsigned long from, unsigned long to,
unsigned long len);

#else /* CONFIG_USERFAULTFD */

/* mm helpers */
Expand Down Expand Up @@ -89,6 +95,17 @@ static inline void dup_userfaultfd_complete(struct list_head *l)
{
}

static inline void mremap_userfaultfd_prep(struct vm_area_struct *vma,
struct vm_userfaultfd_ctx *ctx)
{
}

static inline void mremap_userfaultfd_complete(struct vm_userfaultfd_ctx ctx,
unsigned long from,
unsigned long to,
unsigned long len)
{
}
#endif /* CONFIG_USERFAULTFD */

#endif /* _LINUX_USERFAULTFD_K_H */
11 changes: 10 additions & 1 deletion include/uapi/linux/userfaultfd.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
* means the userland is reading).
*/
#define UFFD_API ((__u64)0xAA)
#define UFFD_API_FEATURES (UFFD_FEATURE_EVENT_FORK)
#define UFFD_API_FEATURES (UFFD_FEATURE_EVENT_FORK | \
UFFD_FEATURE_EVENT_REMAP)
#define UFFD_API_IOCTLS \
((__u64)1 << _UFFDIO_REGISTER | \
(__u64)1 << _UFFDIO_UNREGISTER | \
Expand Down Expand Up @@ -76,6 +77,12 @@ struct uffd_msg {
__u32 ufd;
} fork;

struct {
__u64 from;
__u64 to;
__u64 len;
} remap;

struct {
/* unused reserved fields */
__u64 reserved1;
Expand All @@ -90,6 +97,7 @@ struct uffd_msg {
*/
#define UFFD_EVENT_PAGEFAULT 0x12
#define UFFD_EVENT_FORK 0x13
#define UFFD_EVENT_REMAP 0x14

/* flags for UFFD_EVENT_PAGEFAULT */
#define UFFD_PAGEFAULT_FLAG_WRITE (1<<0) /* If this was a write fault */
Expand All @@ -110,6 +118,7 @@ struct uffdio_api {
*/
#define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0)
#define UFFD_FEATURE_EVENT_FORK (1<<1)
#define UFFD_FEATURE_EVENT_REMAP (1<<2)
__u64 features;

__u64 ioctls;
Expand Down
17 changes: 12 additions & 5 deletions mm/mremap.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/mmu_notifier.h>
#include <linux/uaccess.h>
#include <linux/mm-arch-hooks.h>
#include <linux/userfaultfd_k.h>

#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
Expand Down Expand Up @@ -250,7 +251,8 @@ unsigned long move_page_tables(struct vm_area_struct *vma,

static unsigned long move_vma(struct vm_area_struct *vma,
unsigned long old_addr, unsigned long old_len,
unsigned long new_len, unsigned long new_addr, bool *locked)
unsigned long new_len, unsigned long new_addr,
bool *locked, struct vm_userfaultfd_ctx *uf)
{
struct mm_struct *mm = vma->vm_mm;
struct vm_area_struct *new_vma;
Expand Down Expand Up @@ -309,6 +311,7 @@ static unsigned long move_vma(struct vm_area_struct *vma,
old_addr = new_addr;
new_addr = err;
} else {
mremap_userfaultfd_prep(new_vma, uf);
arch_remap(mm, old_addr, old_addr + old_len,
new_addr, new_addr + new_len);
}
Expand Down Expand Up @@ -413,7 +416,8 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr,
}

static unsigned long mremap_to(unsigned long addr, unsigned long old_len,
unsigned long new_addr, unsigned long new_len, bool *locked)
unsigned long new_addr, unsigned long new_len, bool *locked,
struct vm_userfaultfd_ctx *uf)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
Expand Down Expand Up @@ -458,7 +462,7 @@ static unsigned long mremap_to(unsigned long addr, unsigned long old_len,
if (offset_in_page(ret))
goto out1;

ret = move_vma(vma, addr, old_len, new_len, new_addr, locked);
ret = move_vma(vma, addr, old_len, new_len, new_addr, locked, uf);
if (!(offset_in_page(ret)))
goto out;
out1:
Expand Down Expand Up @@ -497,6 +501,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
unsigned long ret = -EINVAL;
unsigned long charged = 0;
bool locked = false;
struct vm_userfaultfd_ctx uf = NULL_VM_UFFD_CTX;

if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))
return ret;
Expand All @@ -523,7 +528,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,

if (flags & MREMAP_FIXED) {
ret = mremap_to(addr, old_len, new_addr, new_len,
&locked);
&locked, &uf);
goto out;
}

Expand Down Expand Up @@ -592,7 +597,8 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
goto out;
}

ret = move_vma(vma, addr, old_len, new_len, new_addr, &locked);
ret = move_vma(vma, addr, old_len, new_len, new_addr,
&locked, &uf);
}
out:
if (offset_in_page(ret)) {
Expand All @@ -602,5 +608,6 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
up_write(&current->mm->mmap_sem);
if (locked && new_len > old_len)
mm_populate(new_addr + old_len, new_len - old_len);
mremap_userfaultfd_complete(uf, addr, new_addr, old_len);
return ret;
}

0 comments on commit 72f8765

Please sign in to comment.