ext4: add support for writepages calls that cannot map blocks
authorJan Kara <jack@suse.cz>
Wed, 7 Dec 2022 11:27:08 +0000 (12:27 +0100)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 9 Dec 2022 02:49:25 +0000 (21:49 -0500)
Add support for calls to ext4_writepages() than cannot map blocks. These
will be issued from jbd2 transaction commit code.

Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20221207112722.22220-5-jack@suse.cz
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/inode.c

index 4c70cc5..186635c 100644 (file)
@@ -1564,6 +1564,7 @@ struct mpage_da_data {
        struct ext4_map_blocks map;
        struct ext4_io_submit io_submit;        /* IO submission data */
        unsigned int do_map:1;
+       unsigned int can_map:1; /* Can writepages call map blocks? */
        unsigned int scanned_until_end:1;
 };
 
@@ -2556,18 +2557,33 @@ static int ext4_da_writepages_trans_blocks(struct inode *inode)
                                MAX_WRITEPAGES_EXTENT_LEN + bpp - 1, bpp);
 }
 
+/* Return true if the page needs to be written as part of transaction commit */
+static bool ext4_page_nomap_can_writeout(struct page *page)
+{
+       struct buffer_head *bh, *head;
+
+       bh = head = page_buffers(page);
+       do {
+               if (buffer_dirty(bh) && buffer_mapped(bh) && !buffer_delay(bh))
+                       return true;
+       } while ((bh = bh->b_this_page) != head);
+       return false;
+}
+
 /*
  * mpage_prepare_extent_to_map - find & lock contiguous range of dirty pages
- *                              and underlying extent to map
+ *                              needing mapping, submit mapped pages
  *
  * @mpd - where to look for pages
  *
  * Walk dirty pages in the mapping. If they are fully mapped, submit them for
- * IO immediately. When we find a page which isn't mapped we start accumulating
- * extent of buffers underlying these pages that needs mapping (formed by
- * either delayed or unwritten buffers). We also lock the pages containing
- * these buffers. The extent found is returned in @mpd structure (starting at
- * mpd->lblk with length mpd->len blocks).
+ * IO immediately. If we cannot map blocks, we submit just already mapped
+ * buffers in the page for IO and keep page dirty. When we can map blocks and
+ * we find a page which isn't mapped we start accumulating extent of buffers
+ * underlying these pages that needs mapping (formed by either delayed or
+ * unwritten buffers). We also lock the pages containing these buffers. The
+ * extent found is returned in @mpd structure (starting at mpd->lblk with
+ * length mpd->len blocks).
  *
  * Note that this function can attach bios to one io_end structure which are
  * neither logically nor physically contiguous. Although it may seem as an
@@ -2658,14 +2674,30 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd)
                        if (mpd->map.m_len == 0)
                                mpd->first_page = page->index;
                        mpd->next_page = page->index + 1;
-                       /* Add all dirty buffers to mpd */
-                       lblk = ((ext4_lblk_t)page->index) <<
-                               (PAGE_SHIFT - blkbits);
-                       head = page_buffers(page);
-                       err = mpage_process_page_bufs(mpd, head, head, lblk);
-                       if (err <= 0)
-                               goto out;
-                       err = 0;
+                       /*
+                        * Writeout for transaction commit where we cannot
+                        * modify metadata is simple. Just submit the page.
+                        */
+                       if (!mpd->can_map) {
+                               if (ext4_page_nomap_can_writeout(page)) {
+                                       err = mpage_submit_page(mpd, page);
+                                       if (err < 0)
+                                               goto out;
+                               } else {
+                                       unlock_page(page);
+                                       mpd->first_page++;
+                               }
+                       } else {
+                               /* Add all dirty buffers to mpd */
+                               lblk = ((ext4_lblk_t)page->index) <<
+                                       (PAGE_SHIFT - blkbits);
+                               head = page_buffers(page);
+                               err = mpage_process_page_bufs(mpd, head, head,
+                                                             lblk);
+                               if (err <= 0)
+                                       goto out;
+                               err = 0;
+                       }
                        left--;
                }
                pagevec_release(&pvec);
@@ -2785,6 +2817,7 @@ retry:
         */
        mpd.do_map = 0;
        mpd.scanned_until_end = 0;
+       mpd.can_map = 1;
        mpd.io_submit.io_end = ext4_init_io_end(inode, GFP_KERNEL);
        if (!mpd.io_submit.io_end) {
                ret = -ENOMEM;
@@ -2808,6 +2841,7 @@ retry:
                        break;
                }
 
+               WARN_ON_ONCE(!mpd->can_map);
                /*
                 * We have two constraints: We find one extent to map and we
                 * must always write out whole page (makes a difference when