forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 8
fuse: fence cached reads against NOTIFY invalidate with a percpu gate #189
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
Merged
hbirth
merged 1 commit into
DDNStorage:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1
from
hbirth:redfs-ubuntu-hwe-6.17.0-16.16-24.04.1
Jul 21, 2026
+165
−72
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1224,11 +1224,16 @@ static void fuse_readahead(struct readahead_control *rac) | |
| } | ||
| } | ||
|
|
||
| static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to); | ||
|
|
||
| static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) | ||
| { | ||
| struct file *file = iocb->ki_filp; | ||
| struct inode *inode = file->f_mapping->host; | ||
| struct fuse_conn *fc = get_fuse_conn(inode); | ||
| struct fuse_inode *fi = get_fuse_inode(inode); | ||
| struct percpu_rw_semaphore *wb_sem = fi->wb_inval_rwsem; | ||
| ssize_t res; | ||
|
|
||
| /* | ||
| * In auto invalidate mode, always update attributes on read. | ||
|
|
@@ -1249,7 +1254,29 @@ static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) | |
| fuse_get_dlm_lock(file, iocb->ki_pos, | ||
| iov_iter_count(to), FUSE_PAGE_LOCK_READ); | ||
|
|
||
| return generic_file_read_iter(iocb, to); | ||
| /* | ||
| * Fence the cache-serving read against a NOTIFY invalidate so we never | ||
| * hand back a folio the server has just superseded. The gate read side | ||
| * is per-CPU cheap; the NOTIFY holds the write side with priority. | ||
| * Re-check the forced-DIO latch under it: if a storm latched us while we | ||
| * waited on a pending writer, reroute to direct like the buffered write | ||
| * path, so we do not repopulate the cache the latch just dropped. | ||
| * wb_sem is NULL on non-writeback+dlm mounts (gate inactive). | ||
| */ | ||
| if (wb_sem) { | ||
| percpu_down_read(wb_sem); | ||
| if (fuse_inode_force_dio(inode)) { | ||
| percpu_up_read(wb_sem); | ||
| return fuse_direct_read_iter(iocb, to); | ||
| } | ||
| } | ||
|
|
||
| res = generic_file_read_iter(iocb, to); | ||
|
|
||
| if (wb_sem) | ||
| percpu_up_read(wb_sem); | ||
|
|
||
| return res; | ||
| } | ||
|
|
||
| static void fuse_write_args_fill(struct fuse_io_args *ia, struct fuse_file *ff, | ||
|
|
@@ -1747,6 +1774,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) | |
| ssize_t err, count; | ||
| struct fuse_conn *fc = get_fuse_conn(inode); | ||
| struct fuse_inode *fi = get_fuse_inode(inode); | ||
| struct percpu_rw_semaphore *wb_sem = fi->wb_inval_rwsem; | ||
| bool writeback = false; | ||
| bool wb_guard = false; | ||
| bool exclusive = true; | ||
|
|
@@ -1806,20 +1834,21 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) | |
|
|
||
| /* | ||
| * The forced-direct-IO latch feature is active under writeback+dlm; | ||
| * hold wb_inval_rwsem for read across the page-cache dirtying so a | ||
| * concurrent NOTIFY_INVAL_INODE -- which latches the inode under the | ||
| * write side of this lock via a non-blocking trylock -- cannot strand | ||
| * the folios we are about to write. Re-check the latch under it (it may | ||
| * have been set while we blocked on the inode lock) and re-route to the | ||
| * direct path if set. Taken before task_io_account_write() so a | ||
| * re-route is not double-counted; the DLM write lock taken above is | ||
| * harmless as the direct path does its own server coordination. | ||
| * hold the coherency gate (wb_inval_rwsem) for read across the | ||
| * page-cache dirtying so a concurrent NOTIFY_INVAL_INODE -- which takes | ||
| * the write side (blocking, with priority) around its invalidate + latch | ||
| * set -- cannot strand the folios we are about to write. Re-check the | ||
| * latch under it (it may have been set while we blocked on the inode | ||
| * lock) and re-route to the direct path if set. Taken before | ||
| * task_io_account_write() so a re-route is not double-counted; the DLM | ||
| * write lock taken above is harmless as the direct path does its own | ||
| * server coordination. | ||
| */ | ||
| wb_guard = fc->writeback_cache && fc->dlm; | ||
| wb_guard = !!wb_sem; | ||
| if (wb_guard) { | ||
| down_read(&fi->wb_inval_rwsem); | ||
| percpu_down_read(wb_sem); | ||
|
hbirth marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, we're still missing check the DLM lock state after acquiring the wb_sem. This is dangerous as it could be revoked by an just done invalidation. |
||
| if (fuse_inode_force_dio(inode)) { | ||
| up_read(&fi->wb_inval_rwsem); | ||
| percpu_up_read(wb_sem); | ||
| fuse_cache_wr_unlock(inode, exclusive); | ||
| return fuse_direct_write_iter(iocb, from); | ||
| } | ||
|
|
@@ -1911,7 +1940,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) | |
| } | ||
| out: | ||
| if (wb_guard) | ||
| up_read(&fi->wb_inval_rwsem); | ||
| percpu_up_read(wb_sem); | ||
| fuse_cache_wr_unlock(inode, exclusive); | ||
| if (written > 0) | ||
| written = generic_write_sync(iocb, written); | ||
|
|
@@ -3705,7 +3734,23 @@ void fuse_init_file_inode(struct inode *inode, unsigned int flags) | |
| fi->iocachectr = 0; | ||
| init_waitqueue_head(&fi->page_waitq); | ||
| init_waitqueue_head(&fi->direct_io_waitq); | ||
| init_rwsem(&fi->wb_inval_rwsem); | ||
| /* | ||
| * Coherency gate for the forced-direct-IO feature; only writeback+dlm | ||
| * regular files need it. A percpu_rw_semaphore embeds per-CPU state, | ||
| * so allocate it out of line and only when the mount can use it rather | ||
| * than paying it on every inode. On failure leave it NULL: the gate | ||
| * stays inactive (best-effort invalidate) and the inode is still usable. | ||
| */ | ||
| fi->wb_inval_rwsem = NULL; | ||
| if (fc->writeback_cache && fc->dlm) { | ||
| struct percpu_rw_semaphore *sem = kmalloc(sizeof(*sem), GFP_KERNEL); | ||
|
|
||
| if (sem && percpu_init_rwsem(sem)) { | ||
| kfree(sem); | ||
| sem = NULL; | ||
| } | ||
| fi->wb_inval_rwsem = sem; | ||
| } | ||
| fi->notify_stamp = jiffies; | ||
| fi->notify_interval_ewma = FUSE_NOTIFY_EWMA_SEED << FUSE_NOTIFY_EWMA_SHIFT; | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
As my earlier comment, why still miss a check on DLM lock here? The invalidation could be just done before we acquire the wb_sem, then the DLM lock is revoked, then continue calling generic_file_read_iter is dangerous as it is running without DLM lock protection.