* [PATCH] f2fs: zero post-EOF data regardless of fsync_mode when extending file size
@ 2026-10-19 12:45 Jan Prusakowski
0 siblings, 0 replies; only message in thread
From: Jan Prusakowski @ 2026-10-19 12:45 UTC (permalink / raw)
To: Jaegeuk Kim, Chao Yu; +Cc: linux-f2fs-devel, linux-kernel, Jan Prusakowski
Commit 5eced87b7d19 ("f2fs: fix to zero post-EOF data when extending
file size") gated post-EOF page zeroing and writeback behind
fsync_mode=strict. In the default fsync_mode=posix, extending a file
across an unaligned EOF boundary via truncate(), fallocate(), or write()
past EOF leaves stale on-disk data in [old_size, PAGE_ALIGN(old_size))
unzeroed when the page is not cached or after crash recovery, exposing
stale data and failing xfstest generic/794.
Zero and write back the partial post-EOF page on file size extension
regardless of fsync_mode, while avoiding unnecessary block allocations
and synchronous writeback:
1. Use f2fs_get_lock_data_folio() instead of fill_zero() in
do_zero_post_eof_page() so that sparse holes (-ENOENT) are skipped
without allocating physical blocks, and avoid calling
f2fs_balance_fs() while f2fs_setattr() holds i_gc_rwsem[WRITE].
2. Skip post-EOF disk zeroing for inline files (whose post-EOF bytes are
already zeroed in the inode page and upon inline conversion) and
compressed clusters.
3. Only request synchronous writeback in f2fs_write_checks() when
iocb->ki_pos > i_size_read(inode) (when the write starts past EOF,
leaving an unwritten gap) and in f2fs_expand_inode_data() when
!(mode & FALLOC_FL_KEEP_SIZE), avoiding synchronous writeback on
normal sequential append writes.
Fixes: 5eced87b7d19 ("f2fs: fix to zero post-EOF data when extending file size")
Assisted-by: LLM
Signed-off-by: Jan Prusakowski <jprusakowski@google.com>
---
fs/f2fs/file.c | 74 ++++++++++++++++++++++++++------------------------
1 file changed, 38 insertions(+), 36 deletions(-)
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 3b5ad281b518..d8fb72aa7a6d 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -36,36 +36,39 @@
#include <trace/events/f2fs.h>
#include <uapi/linux/f2fs.h>
-static int fill_zero(struct inode *inode, pgoff_t index,
- loff_t start, loff_t len);
-
static int do_zero_post_eof_page(struct inode *inode, loff_t new_size)
{
loff_t old_size = i_size_read(inode);
- unsigned int offset, len;
- pgoff_t index;
+ unsigned int offset = old_size & (PAGE_SIZE - 1);
+ pgoff_t index = old_size >> PAGE_SHIFT;
+ struct folio *folio;
+ unsigned int len;
int err;
- offset = old_size & (PAGE_SIZE - 1);
-
if (!offset)
return 0;
- len = min_t(loff_t, PAGE_SIZE - offset, new_size - old_size);
- index = old_size >> PAGE_SHIFT;
+ if (f2fs_has_inline_data(inode))
+ return 0;
- if (f2fs_has_inline_data(inode)) {
- /* data post eof should be always zero */
- if (new_size <= MAX_INLINE_DATA(inode))
- return 0;
- err = f2fs_convert_inline_inode(inode);
- if (err)
+ if (f2fs_compressed_file(inode)) {
+ err = f2fs_is_compressed_cluster(inode, index);
+ if (err < 0)
return err;
+ if (err)
+ return 0;
}
- err = fill_zero(inode, index, offset, len);
- if (err)
- return err;
+ folio = f2fs_get_lock_data_folio(inode, index, true);
+ if (IS_ERR(folio))
+ return PTR_ERR(folio) == -ENOENT ? 0 : PTR_ERR(folio);
+
+ len = min_t(loff_t, PAGE_SIZE - offset, new_size - old_size);
+ f2fs_folio_wait_writeback(folio, true, true);
+ folio_zero_range(folio, offset, len);
+ folio_mark_dirty(folio);
+ f2fs_folio_put(folio, true);
+
return filemap_write_and_wait_range(inode->i_mapping,
old_size, old_size + len - 1);
}
@@ -74,29 +77,26 @@ static int f2fs_zero_post_eof_page(struct inode *inode,
loff_t new_size, bool lock, bool writeback)
{
loff_t old_size = i_size_read(inode);
- bool strict =
- F2FS_OPTION(F2FS_I_SB(inode)).fsync_mode == FSYNC_MODE_STRICT;
if (old_size >= new_size)
return 0;
- if (!strict && mapping_empty(inode->i_mapping))
- return 0;
-
- if (lock)
- filemap_invalidate_lock(inode->i_mapping);
- /* zero or drop pages only in range of [old_size, new_size] */
- truncate_inode_pages_range(inode->i_mapping, old_size, new_size - 1);
- if (lock)
- filemap_invalidate_unlock(inode->i_mapping);
+ if (!mapping_empty(inode->i_mapping)) {
+ if (lock)
+ filemap_invalidate_lock(inode->i_mapping);
+ /* zero or drop pages only in range of [old_size, new_size] */
+ truncate_inode_pages_range(inode->i_mapping, old_size,
+ new_size - 1);
+ if (lock)
+ filemap_invalidate_unlock(inode->i_mapping);
+ }
- if (!writeback || !strict)
+ if (!writeback)
return 0;
/*
- * In fsync_mode=strict, when we expand an unaligned EOF size, we
- * should zero post EOF data and writeback the data immediately,
- * so that it can avoid exposing stale data after metadata flush
- * and POR.
+ * When we expand an unaligned EOF size, we should zero post-EOF
+ * data and writeback the data immediately, so that it can avoid
+ * exposing stale data after metadata flush and POR.
*/
return do_zero_post_eof_page(inode, new_size);
}
@@ -1984,7 +1984,8 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
if (err)
return err;
- err = f2fs_zero_post_eof_page(inode, offset + len, true, true);
+ err = f2fs_zero_post_eof_page(inode, offset + len, true,
+ !(mode & FALLOC_FL_KEEP_SIZE));
if (err)
return err;
@@ -5367,7 +5368,8 @@ static ssize_t f2fs_write_checks(struct kiocb *iocb, struct iov_iter *from)
return err;
err = f2fs_zero_post_eof_page(inode,
- iocb->ki_pos + iov_iter_count(from), true, true);
+ iocb->ki_pos + iov_iter_count(from), true,
+ iocb->ki_pos > i_size_read(inode));
if (err)
return err;
return count;
--
2.56.0.176.g3575e65682-goog
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-19 12:45 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-19 12:45 [PATCH] f2fs: zero post-EOF data regardless of fsync_mode when extending file size Jan Prusakowski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®