Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / fs / userfaultfd.c
index ba35caf..14f9228 100644 (file)
@@ -1487,6 +1487,10 @@ out_unlock:
                if (!(uffdio_register.mode & UFFDIO_REGISTER_MODE_WP))
                        ioctls_out &= ~((__u64)1 << _UFFDIO_WRITEPROTECT);
 
+               /* CONTINUE ioctl is only supported for MINOR ranges. */
+               if (!(uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR))
+                       ioctls_out &= ~((__u64)1 << _UFFDIO_CONTINUE);
+
                /*
                 * Now that we scanned all vmas we can already tell
                 * userland which ioctls methods are guaranteed to
@@ -1840,6 +1844,66 @@ static int userfaultfd_writeprotect(struct userfaultfd_ctx *ctx,
        return ret;
 }
 
+static int userfaultfd_continue(struct userfaultfd_ctx *ctx, unsigned long arg)
+{
+       __s64 ret;
+       struct uffdio_continue uffdio_continue;
+       struct uffdio_continue __user *user_uffdio_continue;
+       struct userfaultfd_wake_range range;
+
+       user_uffdio_continue = (struct uffdio_continue __user *)arg;
+
+       ret = -EAGAIN;
+       if (READ_ONCE(ctx->mmap_changing))
+               goto out;
+
+       ret = -EFAULT;
+       if (copy_from_user(&uffdio_continue, user_uffdio_continue,
+                          /* don't copy the output fields */
+                          sizeof(uffdio_continue) - (sizeof(__s64))))
+               goto out;
+
+       ret = validate_range(ctx->mm, &uffdio_continue.range.start,
+                            uffdio_continue.range.len);
+       if (ret)
+               goto out;
+
+       ret = -EINVAL;
+       /* double check for wraparound just in case. */
+       if (uffdio_continue.range.start + uffdio_continue.range.len <=
+           uffdio_continue.range.start) {
+               goto out;
+       }
+       if (uffdio_continue.mode & ~UFFDIO_CONTINUE_MODE_DONTWAKE)
+               goto out;
+
+       if (mmget_not_zero(ctx->mm)) {
+               ret = mcopy_continue(ctx->mm, uffdio_continue.range.start,
+                                    uffdio_continue.range.len,
+                                    &ctx->mmap_changing);
+               mmput(ctx->mm);
+       } else {
+               return -ESRCH;
+       }
+
+       if (unlikely(put_user(ret, &user_uffdio_continue->mapped)))
+               return -EFAULT;
+       if (ret < 0)
+               goto out;
+
+       /* len == 0 would wake all */
+       BUG_ON(!ret);
+       range.len = ret;
+       if (!(uffdio_continue.mode & UFFDIO_CONTINUE_MODE_DONTWAKE)) {
+               range.start = uffdio_continue.range.start;
+               wake_userfault(ctx, &range);
+       }
+       ret = range.len == uffdio_continue.range.len ? 0 : -EAGAIN;
+
+out:
+       return ret;
+}
+
 static inline unsigned int uffd_ctx_features(__u64 user_features)
 {
        /*
@@ -1927,6 +1991,9 @@ static long userfaultfd_ioctl(struct file *file, unsigned cmd,
        case UFFDIO_WRITEPROTECT:
                ret = userfaultfd_writeprotect(ctx, arg);
                break;
+       case UFFDIO_CONTINUE:
+               ret = userfaultfd_continue(ctx, arg);
+               break;
        }
        return ret;
 }