mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jan Prusakowski <jprusakowski@google.com>
To: Jaegeuk Kim <jaegeuk@kernel.org>, Chao Yu <chao@kernel.org>
Cc: linux-f2fs-devel@lists.sourceforge.net,
	linux-kernel@vger.kernel.org,
	 Jan Prusakowski <jprusakowski@google.com>
Subject: [PATCH] f2fs: zero post-EOF data regardless of fsync_mode when extending file size
Date: Mon, 19 Oct 2026 12:45:22 +0000	[thread overview]
Message-ID: <20261019124522.3702174-1-jprusakowski@google.com> (raw)

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


                 reply	other threads:[~2026-10-19 12:45 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261019124522.3702174-1-jprusakowski@google.com \
    --to=jprusakowski@google.com \
    --cc=chao@kernel.org \
    --cc=jaegeuk@kernel.org \
    --cc=linux-f2fs-devel@lists.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®