-
Notifications
You must be signed in to change notification settings - Fork 7
fuse: fix race between inode creation and invalidation #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: redfs-ubuntu-noble-6.8.0-58.60
Are you sure you want to change the base?
fuse: fix race between inode creation and invalidation #80
Conversation
There is a race condition between fuse_iget() and fuse_reverse_inval_inode() where an invalidation request can arrive while an inode is still being initialized. This can lead to the invalidation being lost, causing stale inode attributes to persist. The race occurs when: 1. fuse_iget() is called to create/initialize an inode 2. Before fuse_change_attributes_i() completes, fuse_reverse_inval_inode() is called for the same inode 3. The invalidation updates attr_version, but the subsequent fuse_change_attributes_i() overwrites it, losing the invalidation Fix this by introducing a delayed invalidation mechanism using attr_version as a state indicator: - attr_version == 0: inode initialization in progress (fuse_iget not done) - attr_version == 1: delayed invalidation marker - attr_version > 1: normal operation When fuse_reverse_inval_inode() encounters an inode with attr_version == 0, it marks it with attr_version = 1 instead of performing immediate invalidation. After fuse_iget() completes fuse_change_attributes_i(), it checks for the delayed invalidation marker (attr_version == 1) and performs the invalidation at that point. This ensures invalidation requests are not lost during inode initialization, maintaining cache coherency. Signed-off-by: Guang Yuan Wu <gwu@ddn.com> Applied-by: Horst Birthelmer <hbirthelmer@ddn.com>
| fi = get_fuse_inode(inode); | ||
| spin_lock(&fi->lock); | ||
|
|
||
| if (fi->attr_version == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just return -EAGAIN and let userspace handle it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alternative would be too add a waitq
| evict_ctr); | ||
|
|
||
| spin_lock(&fi->lock); | ||
| if (fi->attr_version == 1) /* delayed inode invalidation */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must be missing something, somehow I think this is always true? fuse_change_attributes_common() increases to 1?
| * evict_ctr == fuse_get_evict_ctr() -> no evicts while during request | ||
| */ | ||
| if (!evict_ctr || fi->attr_version || evict_ctr == fuse_get_evict_ctr(fc)) | ||
| if (!evict_ctr || fi->attr_version > 1 || evict_ctr == fuse_get_evict_ctr(fc)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why these changes >1, same as >0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah not >0
|
|
||
| fi->attr_version = atomic64_inc_return(&fc->attr_version); | ||
| if (fi->attr_version != 1) /* fuse_iget() handle if attr_version == 1 */ | ||
| fi->attr_version = atomic64_inc_return(&fc->attr_version); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is if this is later on called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's assume there is no invalidate, and just two times fuse_change_attributes_common() from two different calls.
Note that for properly testing this we need multiple clients and mount points