iomap: factor out a iomap_writepage_handle_eof helper
authorChristoph Hellwig <hch@lst.de>
Thu, 7 Dec 2023 07:27:01 +0000 (08:27 +0100)
committerChristian Brauner <brauner@kernel.org>
Thu, 1 Feb 2024 13:20:11 +0000 (14:20 +0100)
Most of iomap_do_writepage is dedidcated to handling a folio crossing or
beyond i_size.  Split this is into a separate helper and update the
commens to deal with folios instead of pages and make them more readable.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20231207072710.176093-6-hch@lst.de
Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/iomap/buffered-io.c

index 292ab7dade213a86f96c6d93dc995563544fa388..75278e1b05f822f1b06606cc32cdb157bd625aef 100644 (file)
@@ -1758,6 +1758,64 @@ iomap_add_to_ioend(struct inode *inode, loff_t pos, struct folio *folio,
        wbc_account_cgroup_owner(wbc, &folio->page, len);
 }
 
+/*
+ * Check interaction of the folio with the file end.
+ *
+ * If the folio is entirely beyond i_size, return false.  If it straddles
+ * i_size, adjust end_pos and zero all data beyond i_size.
+ */
+static bool iomap_writepage_handle_eof(struct folio *folio, struct inode *inode,
+               u64 *end_pos)
+{
+       u64 isize = i_size_read(inode);
+
+       if (*end_pos > isize) {
+               size_t poff = offset_in_folio(folio, isize);
+               pgoff_t end_index = isize >> PAGE_SHIFT;
+
+               /*
+                * If the folio is entirely ouside of i_size, skip it.
+                *
+                * This can happen due to a truncate operation that is in
+                * progress and in that case truncate will finish it off once
+                * we've dropped the folio lock.
+                *
+                * Note that the pgoff_t used for end_index is an unsigned long.
+                * If the given offset is greater than 16TB on a 32-bit system,
+                * then if we checked if the folio is fully outside i_size with
+                * "if (folio->index >= end_index + 1)", "end_index + 1" would
+                * overflow and evaluate to 0.  Hence this folio would be
+                * redirtied and written out repeatedly, which would result in
+                * an infinite loop; the user program performing this operation
+                * would hang.  Instead, we can detect this situation by
+                * checking if the folio is totally beyond i_size or if its
+                * offset is just equal to the EOF.
+                */
+               if (folio->index > end_index ||
+                   (folio->index == end_index && poff == 0))
+                       return false;
+
+               /*
+                * The folio straddles i_size.
+                *
+                * It must be zeroed out on each and every writepage invocation
+                * because it may be mmapped:
+                *
+                *    A file is mapped in multiples of the page size.  For a
+                *    file that is not a multiple of the page size, the
+                *    remaining memory is zeroed when mapped, and writes to that
+                *    region are not written out to the file.
+                *
+                * Also adjust the writeback range to skip all blocks entirely
+                * beyond i_size.
+                */
+               folio_zero_segment(folio, poff, folio_size(folio));
+               *end_pos = isize;
+       }
+
+       return true;
+}
+
 /*
  * We implement an immediate ioend submission policy here to avoid needing to
  * chain multiple ioends and hence nest mempool allocations which can violate
@@ -1898,78 +1956,16 @@ static int iomap_do_writepage(struct folio *folio,
 {
        struct iomap_writepage_ctx *wpc = data;
        struct inode *inode = folio->mapping->host;
-       u64 end_pos, isize;
+       u64 end_pos = folio_pos(folio) + folio_size(folio);
 
        trace_iomap_writepage(inode, folio_pos(folio), folio_size(folio));
 
-       /*
-        * Is this folio beyond the end of the file?
-        *
-        * The folio index is less than the end_index, adjust the end_pos
-        * to the highest offset that this folio should represent.
-        * -----------------------------------------------------
-        * |                    file mapping           | <EOF> |
-        * -----------------------------------------------------
-        * | Page ... | Page N-2 | Page N-1 |  Page N  |       |
-        * ^--------------------------------^----------|--------
-        * |     desired writeback range    |      see else    |
-        * ---------------------------------^------------------|
-        */
-       isize = i_size_read(inode);
-       end_pos = folio_pos(folio) + folio_size(folio);
-       if (end_pos > isize) {
-               /*
-                * Check whether the page to write out is beyond or straddles
-                * i_size or not.
-                * -------------------------------------------------------
-                * |            file mapping                    | <EOF>  |
-                * -------------------------------------------------------
-                * | Page ... | Page N-2 | Page N-1 |  Page N   | Beyond |
-                * ^--------------------------------^-----------|---------
-                * |                                |      Straddles     |
-                * ---------------------------------^-----------|--------|
-                */
-               size_t poff = offset_in_folio(folio, isize);
-               pgoff_t end_index = isize >> PAGE_SHIFT;
-
-               /*
-                * Skip the page if it's fully outside i_size, e.g.
-                * due to a truncate operation that's in progress.  We've
-                * cleaned this page and truncate will finish things off for
-                * us.
-                *
-                * Note that the end_index is unsigned long.  If the given
-                * offset is greater than 16TB on a 32-bit system then if we
-                * checked if the page is fully outside i_size with
-                * "if (page->index >= end_index + 1)", "end_index + 1" would
-                * overflow and evaluate to 0.  Hence this page would be
-                * redirtied and written out repeatedly, which would result in
-                * an infinite loop; the user program performing this operation
-                * would hang.  Instead, we can detect this situation by
-                * checking if the page is totally beyond i_size or if its
-                * offset is just equal to the EOF.
-                */
-               if (folio->index > end_index ||
-                   (folio->index == end_index && poff == 0))
-                       goto unlock;
-
-               /*
-                * The page straddles i_size.  It must be zeroed out on each
-                * and every writepage invocation because it may be mmapped.
-                * "A file is mapped in multiples of the page size.  For a file
-                * that is not a multiple of the page size, the remaining
-                * memory is zeroed when mapped, and writes to that region are
-                * not written out to the file."
-                */
-               folio_zero_segment(folio, poff, folio_size(folio));
-               end_pos = isize;
+       if (!iomap_writepage_handle_eof(folio, inode, &end_pos)) {
+               folio_unlock(folio);
+               return 0;
        }
 
        return iomap_writepage_map(wpc, wbc, inode, folio, end_pos);
-
-unlock:
-       folio_unlock(folio);
-       return 0;
 }
 
 int