[PATCH v3 19/22] ext4: submit zeroed post-EOF data immediately in the iomap buffered I/O path
From: Zhang Yi
Date: Tue Apr 21 2026 - 22:23:59 EST
From: Zhang Yi <yi.zhang@xxxxxxxxxx>
In the generic buffered_head I/O path, we rely on the data=order mode to
ensure that the zeroed EOF block data is written before updating
i_disksize, thus preventing stale data from being exposed.
However, the iomap buffered I/O path cannot use this mechanism. Instead,
we issue the I/O immediately after performing the zero operation
(without synchronous waiting). This can reduce the risk of exposing
stale data, but it does not guarantee that the zero data will be flushed
to disk before the metadata of i_disksize is updated. The subsequent
patches will wait for this I/O to complete before updating i_disksize.
Suggested-by: Jan Kara <jack@xxxxxxx>
Signed-off-by: Zhang Yi <yi.zhang@xxxxxxxxxx>
---
fs/ext4/inode.c | 58 +++++++++++++++++++++++++++++++++++++++----------
1 file changed, 47 insertions(+), 11 deletions(-)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index d2f7af7922d7..d55899c1ef4c 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4766,8 +4766,10 @@ int ext4_block_zero_eof(struct inode *inode, loff_t from, loff_t end)
if (IS_ENCRYPTED(inode) && !fscrypt_has_encryption_key(inode))
return 0;
- if (length > blocksize - offset)
+ if (length > blocksize - offset) {
length = blocksize - offset;
+ end = from + length;
+ }
err = ext4_block_zero_range(inode, from, length,
&did_zero, &zero_written);
@@ -4782,18 +4784,52 @@ int ext4_block_zero_eof(struct inode *inode, loff_t from, loff_t end)
* TODO: In the iomap path, handle this by updating i_disksize to
* i_size after the zeroed data has been written back.
*/
- if (ext4_should_order_data(inode) &&
- did_zero && zero_written && !IS_DAX(inode)) {
- handle_t *handle;
+ if (did_zero && zero_written && !IS_DAX(inode)) {
+ if (ext4_should_order_data(inode)) {
+ handle_t *handle;
- handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
- if (IS_ERR(handle))
- return PTR_ERR(handle);
+ handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
- err = ext4_jbd2_inode_add_write(handle, inode, from, length);
- ext4_journal_stop(handle);
- if (err)
- return err;
+ err = ext4_jbd2_inode_add_write(handle, inode, from,
+ length);
+ ext4_journal_stop(handle);
+ if (err)
+ return err;
+ /*
+ * inodes using the iomap buffered I/O path do not use the
+ * data=ordered mode. We submit zeroed range here.
+ *
+ * TODO: The end_io process needs to wait for I/O to completes
+ * before updating i_disksize.
+ */
+ } else if (ext4_inode_buffered_iomap(inode)) {
+ struct folio *folio;
+ bool do_submit = false;
+
+ folio = filemap_lock_folio(inode->i_mapping,
+ from >> PAGE_SHIFT);
+ if (IS_ERR(folio))
+ /* Already writeback and clear? */
+ return PTR_ERR(folio) == -ENOENT ? 0 :
+ PTR_ERR(folio);
+
+ folio_wait_writeback(folio);
+ WARN_ON_ONCE(folio_test_writeback(folio));
+
+ if (likely(folio_test_dirty(folio)))
+ do_submit = true;
+ folio_unlock(folio);
+ folio_put(folio);
+
+ if (do_submit) {
+ err = filemap_fdatawrite_range(inode->i_mapping,
+ from, end - 1);
+ if (err)
+ return err;
+ }
+ }
}
return 0;
--
2.52.0