]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
mpage: Provide variant of mpage_writepages() with own optional folio handler
authorJan Kara <jack@suse.cz>
Thu, 26 Mar 2026 14:06:31 +0000 (15:06 +0100)
committerJan Kara <jack@suse.cz>
Fri, 27 Mar 2026 16:01:36 +0000 (17:01 +0100)
Some filesystems need to treat some folios specially (for example for
inodes with inline data). Doing the handling in their .writepages method
in a race-free manner results in duplicating some of the writeback
internals. So provide generalized version of mpage_writepages() that
allows filesystem to provide a handler called for each folio which can
handle the folio in a special way.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://patch.msgid.link/20260326140635.15895-3-jack@suse.cz
Signed-off-by: Jan Kara <jack@suse.cz>
fs/mpage.c
include/linux/mpage.h

index 7dae5afc2b9ee240dad298805f5370a3876ddf3e..b3d9f231a04aa8dc5f08764f35e2ec1f252b35f0 100644 (file)
@@ -646,17 +646,24 @@ out:
 }
 
 /**
- * mpage_writepages - walk the list of dirty pages of the given address space & writepage() all of them
+ * __mpage_writepages - walk the list of dirty pages of the given address space
+ *                     & writepage() all of them
  * @mapping: address space structure to write
  * @wbc: subtract the number of written pages from *@wbc->nr_to_write
  * @get_block: the filesystem's block mapper function.
+ * @write_folio: handler to call for each folio before calling
+ *              mpage_write_folio()
  *
  * This is a library function, which implements the writepages()
- * address_space_operation.
+ * address_space_operation. It calls @write_folio handler for each folio. If
+ * the handler returns value > 0, it calls mpage_write_folio() to do the
+ * folio writeback.
  */
 int
-mpage_writepages(struct address_space *mapping,
-               struct writeback_control *wbc, get_block_t get_block)
+__mpage_writepages(struct address_space *mapping,
+                  struct writeback_control *wbc, get_block_t get_block,
+                  int (*write_folio)(struct folio *folio,
+                                     struct writeback_control *wbc))
 {
        struct mpage_data mpd = {
                .get_block      = get_block,
@@ -666,11 +673,22 @@ mpage_writepages(struct address_space *mapping,
        int error;
 
        blk_start_plug(&plug);
-       while ((folio = writeback_iter(mapping, wbc, folio, &error)))
+       while ((folio = writeback_iter(mapping, wbc, folio, &error))) {
+               if (write_folio) {
+                       error = write_folio(folio, wbc);
+                       /*
+                        * == 0 means folio is handled, < 0 means error. In
+                        * both cases hand back control to writeback_iter()
+                        */
+                       if (error <= 0)
+                               continue;
+                       /* Let mpage_write_folio() handle the folio. */
+               }
                error = mpage_write_folio(wbc, folio, &mpd);
+       }
        if (mpd.bio)
                mpage_bio_submit_write(mpd.bio);
        blk_finish_plug(&plug);
        return error;
 }
-EXPORT_SYMBOL(mpage_writepages);
+EXPORT_SYMBOL(__mpage_writepages);
index 1bdc39daac0a3eab8ccd8c4986a16e9d5f2347f3..358946990bfa5523894f5ec1024f5c1160c7d3b0 100644 (file)
@@ -17,7 +17,14 @@ struct readahead_control;
 
 void mpage_readahead(struct readahead_control *, get_block_t get_block);
 int mpage_read_folio(struct folio *folio, get_block_t get_block);
-int mpage_writepages(struct address_space *mapping,
-               struct writeback_control *wbc, get_block_t get_block);
+int __mpage_writepages(struct address_space *mapping,
+               struct writeback_control *wbc, get_block_t get_block,
+               int (*write_folio)(struct folio *folio,
+                                  struct writeback_control *wbc));
+static inline int mpage_writepages(struct address_space *mapping,
+               struct writeback_control *wbc, get_block_t get_block)
+{
+       return __mpage_writepages(mapping, wbc, get_block, NULL);
+}
 
 #endif