[PATCH] f2fs: compress: don't overwrite populated compress cache entry

From: Chao Yu

Date: Wed Oct 21 2026 - 07:08:52 EST


From: Chao Yu <chao@xxxxxxxxxx>

f2fs_cache_compressed_page() looks up the entry of blkaddr w/
f2fs_find_cache(), and then grabs it w/ f2fs_grab_cache() and
F2FS_CACHE_LOCK_CREATE. If another reader populates the entry in
between, f2fs_grab_cache() returns that uptodate entry, and we will
overwrite its ino and data.

Before the compress cache was converted to the generic cache,
add_to_page_cache_lru() failed w/ -EEXIST in this case, and the
existing page was left untouched.

Normally, the racer is a reader of the same block of the same inode,
so both copies are the same, let's just back off, as
f2fs_ra_meta_caches() does.

A block address can only be owned by one inode at a time, the block
can not be freed and reused by another inode during population, since
the reader holds the locks of the cluster's pagecache pages until
decompression completes, and truncation must lock these pages before
freeing blocks. So if the uptodate entry was cached by another inode,
it's a bug, and one of the copies must be stale, let's report the bug,
and drop the entry, then readers will load the block from disk.

Cc: stable@xxxxxxxxxx
Fixes: 6a78d0488116 ("f2fs: cache: use compress cache")
Signed-off-by: Chao Yu <chao@xxxxxxxxxx>
---
fs/f2fs/compress.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index 747141c79900..9cc77d4dc63f 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1928,6 +1928,24 @@ static void f2fs_cache_compressed_page(struct f2fs_sb_info *sbi,
if (IS_ERR(entry))
return;

+ /* someone else has populated the entry after f2fs_find_cache() */
+ if (f2fs_cache_test_uptodate(entry)) {
+ /*
+ * a block address can only be owned by one inode, if the
+ * entry was cached by another inode, one of copies must be
+ * stale, drop it and let readers load the block from disk.
+ */
+ if (unlikely(entry->ino != ino)) {
+ f2fs_bug_on(sbi, 1);
+ f2fs_warn_ratelimited(sbi,
+ "%s: blkaddr %u was cached by ino %u, current ino %u",
+ __func__, blkaddr, entry->ino, ino);
+ f2fs_truncate_locked_cache(entry, false);
+ }
+ f2fs_put_cache(entry, true);
+ return;
+ }
+
entry->ino = ino;
memcpy(cache_address(entry), folio_address(folio), sbi->blocksize);
f2fs_cache_set_uptodate(entry);
--
2.49.0