mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®