All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jan Prusakowski via Linux-f2fs-devel <linux-f2fs-devel@lists.sourceforge.net>
To: Jaegeuk Kim <jaegeuk@kernel.org>, Chao Yu <chao@kernel.org>
Cc: linux-kernel@vger.kernel.org, linux-f2fs-devel@lists.sourceforge.net
Subject: [f2fs-dev] [PATCH] f2fs: fix stale extent cache in inode page after truncation
Date: Mon, 19 Oct 2026 13:02:41 +0000	[thread overview]
Message-ID: <20261019130241.3751829-1-jprusakowski@google.com> (raw)

During generic/475 (fsstress with dm-error), orphan inode recovery on
remount can fail in sanity_check_extent_cache() with a stale on-disk
extent (ri->i_ext) whose blocks have already been invalidated in the SIT
bitmap:

  F2FS-fs (dm-0): Inconsistent error blkaddr:212341, sit bitmap:0
  F2FS-fs (dm-0): sanity_check_extent_cache: inode (ino=14f2) extent info
                  [212341, 109, 1] is incorrect, run fsck to fix

This happens for two reasons:

1. In f2fs_drop_inode(), when an unlinked inode has I_SYNC set by a
   concurrent __writeback_single_inode(), f2fs_destroy_extent_node() only
   frees the rbtree nodes while leaving et->largest intact and without
   setting FI_NO_EXTENT. Concurrent writeback in f2fs_write_inode() ->
   f2fs_update_inode_page() can race with f2fs_truncate() and write a
   stale et->largest into ri->i_ext after blocks have been invalidated in
   the SIT bitmap. Call f2fs_drop_extent_tree() instead so FI_NO_EXTENT
   is set and et->largest.len is zeroed before truncation.

2. During truncation (including f2fs_evict_inode() -> f2fs_delete_inode()
   -> f2fs_truncate() where I_FREEING is set and f2fs_pre_evict_inode()
   has already destroyed the in-memory extent_tree),
   f2fs_truncate_data_blocks_range() clears ri->i_addr[] directly in the
   inode cached block (marking ientry dirty) and invalidates the blocks
   in the SIT bitmap, but does not call f2fs_update_inode() to
   synchronize ri->i_ext in ientry. Because igrab() returns NULL for an
   I_FREEING inode, both f2fs_sync_inode_meta() and flush_dirty_inode()
   skip the inode if a checkpoint or node writeback occurs before
   f2fs_remove_inode_page() completes, persisting ientry with truncated
   ri->i_addr[] and a stale ri->i_ext.

Fix this by:
- Calling f2fs_drop_extent_tree() instead of f2fs_destroy_extent_node()
  in f2fs_drop_inode().
- Updating the inode cached block via f2fs_update_inode() in
  f2fs_truncate_data_blocks_range() when truncating blocks in the inode
  cached block, as well as in f2fs_do_truncate_blocks() and
  f2fs_truncate_inode_blocks() when FI_DIRTY_INODE is set.

Fixes: 3e72f721390d ("f2fs: use extent_cache by default")
Assisted-by: LLM
Signed-off-by: Jan Prusakowski <jprusakowski@google.com>
---
 fs/f2fs/file.c  | 4 ++++
 fs/f2fs/node.c  | 5 +++++
 fs/f2fs/super.c | 2 +-
 3 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 3b5ad281b518..b7a404d0efc6 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -796,6 +796,8 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
 		f2fs_update_read_extent_cache_range(dn, fofs, 0, len);
 		f2fs_update_age_extent_cache_range(dn, fofs, len);
 		dec_valid_block_count(sbi, dn->inode, nr_free);
+		if (IS_INODE(sbi, dn->node_entry))
+			f2fs_update_inode(dn->inode, dn->node_entry);
 	}
 	dn->ofs_in_node = ofs;
 
@@ -885,6 +887,7 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
 		f2fs_update_time(sbi, REQ_TIME);
 
 		f2fs_drop_extent_tree(inode);
+		f2fs_update_inode(inode, ientry);
 
 		f2fs_put_cache(ientry, true);
 		goto out;
@@ -892,6 +895,7 @@ int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock)
 
 	if (f2fs_has_inline_data(inode)) {
 		f2fs_truncate_inline_inode(inode, ientry, from);
+		f2fs_update_inode(inode, ientry);
 		f2fs_put_cache(ientry, true);
 		truncate_page = true;
 		goto out;
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 424e95ca5878..27ccc175c282 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1315,6 +1315,11 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from)
 		nofs += err;
 	}
 fail:
+	if (is_inode_flag_set(inode, FI_DIRTY_INODE)) {
+		f2fs_lock_cache(entry);
+		f2fs_update_inode(inode, entry);
+		f2fs_unlock_cache(entry);
+	}
 	f2fs_put_cache(entry, false);
 	trace_f2fs_truncate_inode_blocks_exit(inode, err);
 	return err > 0 ? 0 : err;
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 773b12ea29c0..acefc1378a6a 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1888,7 +1888,7 @@ static int f2fs_drop_inode(struct inode *inode)
 			spin_unlock(&inode->i_lock);
 
 			/* should remain fi->extent_tree for writepage */
-			f2fs_destroy_extent_node(inode);
+			f2fs_drop_extent_tree(inode);
 
 			sb_start_intwrite(inode->i_sb);
 			f2fs_i_size_write(inode, 0);
-- 
2.56.0.176.g3575e65682-goog



_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

                 reply	other threads:[~2026-10-19 13:02 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=20261019130241.3751829-1-jprusakowski@google.com \
    --to=linux-f2fs-devel@lists.sourceforge.net \
    --cc=chao@kernel.org \
    --cc=jaegeuk@kernel.org \
    --cc=jprusakowski@google.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.