From 76cbb73c857726c8cce6b3dc8962997a9136d9b0 Mon Sep 17 00:00:00 2001 From: Horst Birthelmer Date: Fri, 16 Jan 2026 13:31:07 +0100 Subject: [PATCH] fuse: fix inode initialization race Fix a race between fuse_iget() and fuse_reverse_inval_inode() where invalidation can arrive while an inode is being initialized, causing the invalidation to be lost. Add a waitqueue to make fuse_reverse_inval_inode() wait when it encounters an inode with attr_version == 0 (still initializing). When fuse_change_attributes_common() completes initialization, it wakes waiting threads. This ensures invalidations are properly serialized with inode initialization, maintaining cache coherency. Signed-off-by: Horst Birthelmer --- fs/fuse/fuse_i.h | 3 +++ fs/fuse/inode.c | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4f0474e2e31def..4a52a3bc443ca7 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -896,6 +896,9 @@ struct fuse_conn { /** Version counter for attribute changes */ atomic64_t attr_version; + /** Waitqueue for attr_version initialization */ + wait_queue_head_t attr_version_waitq; + /** Version counter for evict inode */ atomic64_t evict_ctr; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 61962fd615857a..67612fde6ad84e 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -238,6 +238,7 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, set_mask_bits(&fi->inval_mask, STATX_BASIC_STATS, 0); fi->attr_version = atomic64_inc_return(&fc->attr_version); + wake_up_all(&fc->attr_version_waitq); fi->i_time = attr_valid; inode->i_ino = fuse_squash_ino(attr->ino); @@ -597,10 +598,17 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, return -ENOENT; fi = get_fuse_inode(inode); + spin_lock(&fi->lock); + while (fi->attr_version == 0) { + spin_unlock(&fi->lock); + wait_event(fc->attr_version_waitq, READ_ONCE(fi->attr_version) != 0); + spin_lock(&fi->lock); + } + fi->attr_version = atomic64_inc_return(&fc->attr_version); spin_unlock(&fi->lock); - + if (fc->inval_inode_entries) fuse_invalidate_inode_entry(inode); else if (fc->expire_inode_entries) @@ -1017,6 +1025,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm, refcount_set(&fc->count, 1); atomic_set(&fc->dev_count, 1); init_waitqueue_head(&fc->blocked_waitq); + init_waitqueue_head(&fc->attr_version_waitq); fuse_iqueue_init(&fc->iq, fiq_ops, fiq_priv); INIT_LIST_HEAD(&fc->bg_queue); INIT_LIST_HEAD(&fc->entry);