userfaultfd: non-cooperative: wake userfaults after UFFDIO_UNREGISTER
authorAndrea Arcangeli <aarcange@redhat.com>
Wed, 22 Feb 2017 23:42:46 +0000 (15:42 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 23 Feb 2017 00:41:28 +0000 (16:41 -0800)
Userfaults may still happen after the userfaultfd monitor thread
received a UFFD_EVENT_MADVDONTNEED until UFFDIO_UNREGISTER is run.

Wake any pending userfault within UFFDIO_UNREGISTER protected by the
mmap_sem for writing, so they will not be reported to userland leading
to UFFDIO_COPY returning -EINVAL (as the range was already unregistered)
and they will not hang permanently either.

Link: http://lkml.kernel.org/r/20161216144821.5183-16-aarcange@redhat.com
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Cc: Hillf Danton <hillf.zj@alibaba-inc.com>
Cc: Michael Rapoport <RAPOPORT@il.ibm.com>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/userfaultfd.c

index ea9008254df46f3acb935876468b0c0e9c4e8df5..26e1ef00b63c1f2c58be46440f892033127bcf4b 100644 (file)
@@ -1302,6 +1302,19 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
                        start = vma->vm_start;
                vma_end = min(end, vma->vm_end);
 
+               if (userfaultfd_missing(vma)) {
+                       /*
+                        * Wake any concurrent pending userfault while
+                        * we unregister, so they will not hang
+                        * permanently and it avoids userland to call
+                        * UFFDIO_WAKE explicitly.
+                        */
+                       struct userfaultfd_wake_range range;
+                       range.start = start;
+                       range.len = vma_end - start;
+                       wake_userfault(vma->vm_userfaultfd_ctx.ctx, &range);
+               }
+
                new_flags = vma->vm_flags & ~(VM_UFFD_MISSING | VM_UFFD_WP);
                prev = vma_merge(mm, prev, start, vma_end, new_flags,
                                 vma->anon_vma, vma->vm_file, vma->vm_pgoff,