Merge tag 'gfs2-v5.15-rc5-mmap-fault' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / fs / iomap / direct-io.c
index 811c898125a548ef63d6866d7ba2922f4c1047b0..b4dc51063d36a14e2cabcb3f677b92e88c92cc98 100644 (file)
@@ -31,6 +31,7 @@ struct iomap_dio {
        atomic_t                ref;
        unsigned                flags;
        int                     error;
+       size_t                  done_before;
        bool                    wait_for_completion;
 
        union {
@@ -114,6 +115,9 @@ ssize_t iomap_dio_complete(struct iomap_dio *dio)
        if (ret > 0 && (dio->flags & IOMAP_DIO_NEED_SYNC))
                ret = generic_write_sync(iocb, ret);
 
+       if (ret > 0)
+               ret += dio->done_before;
+
        kfree(dio);
 
        return ret;
@@ -375,6 +379,8 @@ static loff_t iomap_dio_hole_iter(const struct iomap_iter *iter,
        loff_t length = iov_iter_zero(iomap_length(iter), dio->submit.iter);
 
        dio->size += length;
+       if (!length)
+               return -EFAULT;
        return length;
 }
 
@@ -406,6 +412,8 @@ static loff_t iomap_dio_inline_iter(const struct iomap_iter *iomi,
                copied = copy_to_iter(inline_data, length, iter);
        }
        dio->size += copied;
+       if (!copied)
+               return -EFAULT;
        return copied;
 }
 
@@ -450,13 +458,21 @@ static loff_t iomap_dio_iter(const struct iomap_iter *iter,
  * may be pure data writes. In that case, we still need to do a full data sync
  * completion.
  *
+ * When page faults are disabled and @dio_flags includes IOMAP_DIO_PARTIAL,
+ * __iomap_dio_rw can return a partial result if it encounters a non-resident
+ * page in @iter after preparing a transfer.  In that case, the non-resident
+ * pages can be faulted in and the request resumed with @done_before set to the
+ * number of bytes previously transferred.  The request will then complete with
+ * the correct total number of bytes transferred; this is essential for
+ * completing partial requests asynchronously.
+ *
  * Returns -ENOTBLK In case of a page invalidation invalidation failure for
  * writes.  The callers needs to fall back to buffered I/O in this case.
  */
 struct iomap_dio *
 __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
                const struct iomap_ops *ops, const struct iomap_dio_ops *dops,
-               unsigned int dio_flags)
+               unsigned int dio_flags, size_t done_before)
 {
        struct address_space *mapping = iocb->ki_filp->f_mapping;
        struct inode *inode = file_inode(iocb->ki_filp);
@@ -486,6 +502,7 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
        dio->dops = dops;
        dio->error = 0;
        dio->flags = 0;
+       dio->done_before = done_before;
 
        dio->submit.iter = iter;
        dio->submit.waiter = current;
@@ -587,6 +604,12 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
        if (iov_iter_rw(iter) == READ && iomi.pos >= dio->i_size)
                iov_iter_revert(iter, iomi.pos - dio->i_size);
 
+       if (ret == -EFAULT && dio->size && (dio_flags & IOMAP_DIO_PARTIAL)) {
+               if (!(iocb->ki_flags & IOCB_NOWAIT))
+                       wait_for_completion = true;
+               ret = 0;
+       }
+
        /* magic error code to fall back to buffered I/O */
        if (ret == -ENOTBLK) {
                wait_for_completion = true;
@@ -649,11 +672,11 @@ EXPORT_SYMBOL_GPL(__iomap_dio_rw);
 ssize_t
 iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
                const struct iomap_ops *ops, const struct iomap_dio_ops *dops,
-               unsigned int dio_flags)
+               unsigned int dio_flags, size_t done_before)
 {
        struct iomap_dio *dio;
 
-       dio = __iomap_dio_rw(iocb, iter, ops, dops, dio_flags);
+       dio = __iomap_dio_rw(iocb, iter, ops, dops, dio_flags, done_before);
        if (IS_ERR_OR_NULL(dio))
                return PTR_ERR_OR_ZERO(dio);
        return iomap_dio_complete(dio);