generic_perform_write()/iomap_write_actor(): saner logics for short copy
authorAl Viro <viro@zeniv.linux.org.uk>
Mon, 31 May 2021 04:32:44 +0000 (00:32 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Wed, 2 Jun 2021 21:50:44 +0000 (17:50 -0400)
if we run into a short copy and ->write_end() refuses to advance at all,
use the amount we'd managed to copy for the next iteration to handle.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/iomap/buffered-io.c
mm/filemap.c

index f2cd203..354b41d 100644 (file)
@@ -771,10 +771,6 @@ again:
                 * Otherwise there's a nasty deadlock on copying from the
                 * same page as we're writing to, without it being marked
                 * up-to-date.
-                *
-                * Not only is this an optimisation, but it is also required
-                * to check that the address is actually valid, when atomic
-                * usercopies are used, below.
                 */
                if (unlikely(iov_iter_fault_in_readable(i, bytes))) {
                        status = -EFAULT;
@@ -791,25 +787,24 @@ again:
 
                copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
 
-               copied = iomap_write_end(inode, pos, bytes, copied, page, iomap,
+               status = iomap_write_end(inode, pos, bytes, copied, page, iomap,
                                srcmap);
 
                cond_resched();
 
-               iov_iter_advance(i, copied);
-               if (unlikely(copied == 0)) {
+               if (unlikely(status == 0)) {
                        /*
-                        * If we were unable to copy any data at all, we must
-                        * fall back to a single segment length write.
-                        *
-                        * If we didn't fallback here, we could livelock
-                        * because not all segments in the iov can be copied at
-                        * once without a pagefault.
+                        * A short copy made iomap_write_end() reject the
+                        * thing entirely.  Might be memory poisoning
+                        * halfway through, might be a race with munmap,
+                        * might be severe memory pressure.
                         */
-                       bytes = min_t(unsigned long, PAGE_SIZE - offset,
-                                               iov_iter_single_seg_count(i));
+                       if (copied)
+                               bytes = copied;
                        goto again;
                }
+               copied = status;
+               iov_iter_advance(i, copied);
                pos += copied;
                written += copied;
                length -= copied;
index 66f7e9f..0be2494 100644 (file)
@@ -3642,10 +3642,6 @@ again:
                 * Otherwise there's a nasty deadlock on copying from the
                 * same page as we're writing to, without it being marked
                 * up-to-date.
-                *
-                * Not only is this an optimisation, but it is also required
-                * to check that the address is actually valid, when atomic
-                * usercopies are used, below.
                 */
                if (unlikely(iov_iter_fault_in_readable(i, bytes))) {
                        status = -EFAULT;
@@ -3672,24 +3668,22 @@ again:
                                                page, fsdata);
                if (unlikely(status < 0))
                        break;
-               copied = status;
 
                cond_resched();
 
-               iov_iter_advance(i, copied);
-               if (unlikely(copied == 0)) {
+               if (unlikely(status == 0)) {
                        /*
-                        * If we were unable to copy any data at all, we must
-                        * fall back to a single segment length write.
-                        *
-                        * If we didn't fallback here, we could livelock
-                        * because not all segments in the iov can be copied at
-                        * once without a pagefault.
+                        * A short copy made ->write_end() reject the
+                        * thing entirely.  Might be memory poisoning
+                        * halfway through, might be a race with munmap,
+                        * might be severe memory pressure.
                         */
-                       bytes = min_t(unsigned long, PAGE_SIZE - offset,
-                                               iov_iter_single_seg_count(i));
+                       if (copied)
+                               bytes = copied;
                        goto again;
                }
+               copied = status;
+               iov_iter_advance(i, copied);
                pos += copied;
                written += copied;