Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7499e44
fuse: refuse a writeback connection whose block is not a page
hbirth Aug 26, 2026
8c2ff0e
fuse: take a DLM read lock for the readahead window
hbirth Aug 21, 2026
b774e9f
fuse: give DLM ranges a lifecycle state
hbirth Aug 25, 2026
7444383
fuse: make the DLM lock mode part of the range state
hbirth Aug 25, 2026
83e334c
fuse: record what the page cache under a DLM range holds
hbirth Aug 25, 2026
a4ab6d8
fuse: record the exact byte range a cached write dirties
hbirth Aug 25, 2026
a259b45
fuse: write back only the bytes a DLM range records as written
hbirth Aug 25, 2026
ef3f058
fuse: let a NOTIFY invalidate ask what it has to do
hbirth Aug 25, 2026
1ef5278
fuse: keep a revoked DLM range instead of deleting it
hbirth Aug 25, 2026
02a9e2a
fuse: drop the per-inode coherency gate
hbirth Aug 25, 2026
544bbad
fuse: read part of a folio without zeroing the rest
hbirth Aug 25, 2026
3fca830
fuse: bound the run a DLM classification covers, including no record
hbirth Aug 25, 2026
f88ac89
fuse: leave a partly written folio unfilled instead of reading it
hbirth Aug 25, 2026
bdc02ee
fuse: stop naming bytes the record has already handed over
hbirth Aug 25, 2026
1243863
fuse: bound the re-request of a grant a revoke killed in flight
hbirth Aug 25, 2026
c7d382e
fuse: free the DLM ranges a truncate emptied
hbirth Aug 25, 2026
c9fab6c
fuse: wait out the writeback a NOTIFY invalidate starts
hbirth Aug 25, 2026
53140d0
fuse: flush the page cache before re-routing to direct IO
hbirth Aug 25, 2026
2480565
fuse: say byte offset where the record means byte offset
hbirth Aug 25, 2026
d474c45
fuse: split DLM ranges at the bounds of a recorded grant
Aug 26, 2026
1bf503a
fuse: drop the DLM ranges a truncate's invalidate empties
Aug 26, 2026
01722bf
fuse: forget the DLM record an O_TRUNC open discards
Aug 26, 2026
94d198f
fuse: drop the DLM ranges a punch hole empties
Aug 26, 2026
66894f6
fuse: drop the DLM ranges copy_file_range invalidates
Aug 26, 2026
acec517
fuse: keep the local size while the tail is dirty
Aug 26, 2026
bb6f5f9
fuse: drop the DLM ranges an attribute change invalidates
Aug 26, 2026
6752d39
fuse: report an error from a DLM buffered write that wrote nothing
hbirth Aug 26, 2026
48908a6
fuse: drop the deferred fill of a partly written folio
hbirth Aug 26, 2026
d75a174
fuse: hold the grant for writeback instead of classifying the run
hbirth Aug 26, 2026
9bb6233
fuse: drop the content bound from the DLM record
hbirth Aug 26, 2026
b387e08
fuse: do not hand O_APPEND to the server under the writeback cache
Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 39 additions & 27 deletions fs/fuse/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,10 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
memset(&inarg, 0, sizeof(inarg));
memset(&outentry, 0, sizeof(outentry));
inarg.flags = flags;

/* The kernel owns append positioning; see fuse_send_open() */
if (fm->fc->writeback_cache)
inarg.flags &= ~O_APPEND;
inarg.mode = mode;
inarg.umask = current_umask();

Expand Down Expand Up @@ -2106,34 +2110,24 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
WARN_ON(!(attr->ia_valid & ATTR_SIZE));
WARN_ON(attr->ia_size != 0);
if (fc->atomic_o_trunc) {
struct percpu_rw_semaphore *wb_sem = fi->wb_inval_rwsem;

/*
* No need to send request to userspace, since actual
* truncation has already been done by OPEN. But still
* need to truncate page cache.
*
* Revoke and drop under the coherency gate write side,
* like the NOTIFY invalidate path: a gate reader that
* already re-validated its grant must not have the
* lock tree and the cache yanked mid-hold, or it
* would repopulate the truncated range trusting a
* grant that no longer exists. Waiting for gate
* readers here is safe: we hold i_rwsem exclusive, so
* no gate holder can be waiting on it (the write path
* takes i_rwsem before the gate, the read path never
* takes it).
* Dropping every grant here does not need a reader or
* writer fenced out: truncate_pagecache() discards the
* folios rather than writing them, and a write racing
* this is a write racing an O_TRUNC open, which has no
* order to preserve. i_rwsem is held exclusive
* anyway, so no cached write is in progress.
*/
if (wb_sem)
percpu_down_write(wb_sem);
if (fc->dlm && fc->writeback_cache)
fuse_dlm_cache_release_locks(fi);
spin_lock(&fi->lock);
i_size_write(inode, 0);
spin_unlock(&fi->lock);
truncate_pagecache(inode, 0);
if (wb_sem)
percpu_up_write(wb_sem);
goto out;
}
file = NULL;
Expand Down Expand Up @@ -2237,23 +2231,41 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
*/
if ((is_truncate || !is_wb) &&
S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
struct percpu_rw_semaphore *wb_sem = fi->wb_inval_rwsem;

/*
* Revoke and drop under the coherency gate write side; see
* the atomic-O_TRUNC branch above. i_rwsem is held
* exclusive here as well (setattr), so waiting out gate
* readers cannot deadlock.
* Revoke past the new size and drop what is beyond it; see
* the atomic-O_TRUNC branch above for why this needs nothing
* fenced out. i_rwsem is held exclusive here as well.
*/
if (wb_sem)
percpu_down_write(wb_sem);
if (fc->dlm && fc->writeback_cache)
fuse_dlm_unlock_range(fi, outarg.attr.size & PAGE_MASK, -1);
fuse_dlm_unlock_range(fi, outarg.attr.size & PAGE_MASK,
U64_MAX);

truncate_pagecache(inode, outarg.attr.size);
invalidate_inode_pages2(mapping);
if (wb_sem)
percpu_up_write(wb_sem);

/*
* The cache above the new size is gone, so the ranges
* describing it have nothing left to say. From the first
* whole page above it: the page holding the new end of the
* file survives the truncate, and so does its record.
*/
if (fc->dlm && fc->writeback_cache)
fuse_dlm_ranges_dropped(fi, PAGE_ALIGN(outarg.attr.size),
U64_MAX);

/*
* invalidate_inode_pages2() emptied the mapping below the new
* size too (laundering anything dirty first, so those bytes
* are on the server). Ranges left DIRTY there would make the
* next partial write to the page keep and flush folio bytes
* nobody wrote. Only when the drop really emptied it: a busy
* folio that survived still needs its record, and a fault
* populating after the check keeps its page visible to it.
*/
if (fc->dlm && fc->writeback_cache && outarg.attr.size &&
!filemap_range_has_page(mapping, 0, outarg.attr.size - 1))
fuse_dlm_ranges_dropped(fi, 0,
PAGE_ALIGN(outarg.attr.size) - 1);
}

clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
Expand Down
Loading