From ee17c0cd763b622012bc725253015b6a721b48f8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 21 Oct 2026 11:05:38 +0000 Subject: [PATCH] f2fs: compress: don't overwrite populated compress cache entry 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@kernel.org Fixes: 6a78d0488116 ("f2fs: cache: use compress cache") Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- 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);