* [PATCH v3 1/2] mpage: Provide variant of mpage_writepages() with own optional folio handler
2026-03-26 14:06 [PATCH v4 0/2] udf: Fix race between file type conversion and writeback Jan Kara
@ 2026-03-26 14:06 ` Jan Kara
2026-03-26 14:22 ` Christoph Hellwig
2026-03-26 14:06 ` [PATCH v3 2/2] udf: Fix race between file type conversion and writeback Jan Kara
1 sibling, 1 reply; 5+ messages in thread
From: Jan Kara @ 2026-03-26 14:06 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Christoph Hellwig, Jan Kara
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.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/mpage.c | 21 +++++++++++++++++----
include/linux/mpage.h | 11 +++++++++--
2 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/fs/mpage.c b/fs/mpage.c
index 7dae5afc2b9e..9095f3f983a8 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -655,8 +655,10 @@ static int mpage_write_folio(struct writeback_control *wbc, struct folio *folio,
* address_space_operation.
*/
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 +668,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);
diff --git a/include/linux/mpage.h b/include/linux/mpage.h
index 1bdc39daac0a..358946990bfa 100644
--- a/include/linux/mpage.h
+++ b/include/linux/mpage.h
@@ -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
--
2.51.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v3 2/2] udf: Fix race between file type conversion and writeback
2026-03-26 14:06 [PATCH v4 0/2] udf: Fix race between file type conversion and writeback Jan Kara
2026-03-26 14:06 ` [PATCH v3 1/2] mpage: Provide variant of mpage_writepages() with own optional folio handler Jan Kara
@ 2026-03-26 14:06 ` Jan Kara
2026-03-26 14:22 ` Christoph Hellwig
1 sibling, 1 reply; 5+ messages in thread
From: Jan Kara @ 2026-03-26 14:06 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Christoph Hellwig, Jan Kara, Jianzhou Zhao
udf_setsize() can race with udf_writepages() as follows:
udf_setsize() udf_writepages()
if (iinfo->i_alloc_type ==
ICBTAG_FLAG_AD_IN_ICB)
err = udf_expand_file_adinicb(inode);
err = udf_extend_file(inode, newsize);
udf_adinicb_writepages()
memcpy_from_file_folio() - crash
because inode size is too big.
Fix the problem by rechecking file type under folio lock in
udf_writepages() which properly serializes with
udf_expand_file_adinicb(). Since it is quite difficult to implement this
locking with current writeback_iter() logic, let's just opencode the
logic necessary to prepare (the only) folio the inode can have for
writeback.
Reported-by: Jianzhou Zhao <luckd0g@163.com>
Link: https://lore.kernel.org/all/f622c01.67ac.19cdbdd777d.Coremail.luckd0g@163.com
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/udf/inode.c | 33 +++++++++++++++------------------
1 file changed, 15 insertions(+), 18 deletions(-)
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 7fae8002344a..23e894092dab 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -181,22 +181,23 @@ static void udf_write_failed(struct address_space *mapping, loff_t to)
}
}
-static int udf_adinicb_writepages(struct address_space *mapping,
- struct writeback_control *wbc)
+static int udf_handle_page_wb(struct folio *folio,
+ struct writeback_control *wbc)
{
- struct inode *inode = mapping->host;
+ struct inode *inode = folio->mapping->host;
struct udf_inode_info *iinfo = UDF_I(inode);
- struct folio *folio = NULL;
- int error = 0;
- while ((folio = writeback_iter(mapping, wbc, folio, &error))) {
- BUG_ON(!folio_test_locked(folio));
- BUG_ON(folio->index != 0);
- memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio,
- 0, i_size_read(inode));
- folio_unlock(folio);
- }
+ /*
+ * Inodes in the normal format are handled by the generic code. This
+ * check is race-free as the folio lock protects us from inode type
+ * conversion.
+ */
+ if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB)
+ return 1;
+ memcpy_from_file_folio(iinfo->i_data + iinfo->i_lenEAttr, folio,
+ 0, i_size_read(inode));
+ folio_unlock(folio);
mark_inode_dirty(inode);
return 0;
}
@@ -204,12 +205,8 @@ static int udf_adinicb_writepages(struct address_space *mapping,
static int udf_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
- struct inode *inode = mapping->host;
- struct udf_inode_info *iinfo = UDF_I(inode);
-
- if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
- return udf_adinicb_writepages(mapping, wbc);
- return mpage_writepages(mapping, wbc, udf_get_block_wb);
+ return __mpage_writepages(mapping, wbc, udf_get_block_wb,
+ udf_handle_page_wb);
}
static void udf_adinicb_read_folio(struct folio *folio)
--
2.51.0
^ permalink raw reply related [flat|nested] 5+ messages in thread