Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 0 additions & 6 deletions Documentation/admin-guide/sysctl/fs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,6 @@ greater than max_request_timeout, the system will use max_request_timeout as the
timeout. 0 indicates no max request timeout. The maximum value that can be set
is 65535.

If the server did not specify a timeout at mount and both of the above are set
to 0, the timeout is derived from ``/proc/sys/kernel/hung_task_timeout_secs``
instead, so that a server that stops answering aborts the connection rather than
leaving the waiters around for the hung task detector to report on. Setting
hung_task_timeout_secs to 0 disables this fallback as well.

For timeouts, if the server does not respond to the request by the time
the set timeout elapses, then the connection to the fuse server will be aborted.
Please note that the timeouts are not 100% precise (eg you may set 60 seconds but
Expand Down
8 changes: 0 additions & 8 deletions fs/fuse/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -2129,7 +2129,6 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (fc->dlm && fc->writeback_cache)
fuse_dlm_cache_release_locks(fi);
spin_lock(&fi->lock);
fi->server_size = 0;
i_size_write(inode, 0);
spin_unlock(&fi->lock);
truncate_pagecache(inode, 0);
Expand Down Expand Up @@ -2225,13 +2224,6 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
/* see the comment in fuse_change_attributes() */
if (!is_wb || is_truncate)
i_size_write(inode, outarg.attr.size);
/*
* A truncate settles the size on the server; only shrink the
* server-materialized bound: growing just exposes zeros, which the
* bound need not cover (see fuse_iomap_read_folio_range()).
*/
if (is_truncate && (loff_t) outarg.attr.size < fi->server_size)
fi->server_size = outarg.attr.size;

if (is_truncate) {
/* NOTE: this may release/reacquire fi->lock */
Expand Down
188 changes: 124 additions & 64 deletions fs/fuse/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,6 @@ static void fuse_truncate_update_attr(struct inode *inode, struct file *file)

spin_lock(&fi->lock);
fi->attr_version = atomic64_inc_return(&fc->attr_version);
fi->server_size = 0;
i_size_write(inode, 0);
spin_unlock(&fi->lock);
file_update_time(file);
Expand Down Expand Up @@ -1034,41 +1033,9 @@ static int fuse_iomap_read_folio_range(const struct iomap_iter *iter,
struct file *file = iter->private;
struct inode *inode = file_inode(file);
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
size_t off = offset_in_folio(folio, pos);
bool hole;
int ret;

/*
* Expanding writes claim their new i_size up front (see
* fuse_cache_write_iter()), which keeps iomap's own beyond-EOF
* zeroing in iomap_block_needs_zeroing() from ever firing for the
* write's own range: every block of a file expansion would be read
* from the server although it cannot contain data. Zero-fill
* locally instead when the server is known to hold no data in the
* range and we hold the DLM write lock covering it:
*
* - fi->server_size bounds the data materialized on the server
* (writeback and direct write acknowledgements, server
* attributes),
* - local data not yet acknowledged sits in uptodate blocks, which
* iomap never passes to this callback,
* - the page-granular DLM write lock excludes data written by
* other nodes, re-checked against the live lock tree so a
* revoked lock falls back to reading.
*/
if (fc->dlm) {
spin_lock(&fi->lock);
hole = pos >= fi->server_size;
spin_unlock(&fi->lock);

if (hole && fuse_dlm_range_is_locked(fi, pos, pos + len - 1,
FUSE_PAGE_LOCK_WRITE)) {
folio_zero_range(folio, off, len);
return 0;
}
}

ret = fuse_do_readfolio(file, folio, off, len);

/*
Expand Down Expand Up @@ -1423,15 +1390,6 @@ bool fuse_write_update_attr(struct inode *inode, loff_t pos, ssize_t written)

spin_lock(&fi->lock);
fi->attr_version = atomic64_inc_return(&fc->attr_version);
if (written > 0 && S_ISREG(inode->i_mode)) {
/*
* The server acknowledged data up to @pos, keep the
* server-materialized bound in sync for the expansion
* zero-fill in fuse_iomap_read_folio_range().
*/
if (pos > fi->server_size)
fi->server_size = pos;
}
if (written > 0 && pos > inode->i_size) {
i_size_write(inode, pos);
ret = true;
Expand Down Expand Up @@ -1587,7 +1545,8 @@ static inline unsigned int fuse_wr_pages(loff_t pos, size_t len,
max_pages);
}

static ssize_t fuse_perform_write(struct kiocb *iocb, struct iov_iter *ii)
static ssize_t fuse_perform_write(struct kiocb *iocb, struct iov_iter *ii,
bool cache)
{
struct address_space *mapping = iocb->ki_filp->f_mapping;
struct inode *inode = mapping->host;
Expand Down Expand Up @@ -1617,6 +1576,14 @@ static ssize_t fuse_perform_write(struct kiocb *iocb, struct iov_iter *ii)
if (count <= 0) {
err = count;
} else {
/*
* On behalf of a buffered write whose bytes bypass
* the page cache (DLM unaligned edges): the server
* must classify them like the writeback they
* replace.
*/
if (cache)
ia.write.in.write_flags |= FUSE_WRITE_CACHE;
err = fuse_send_write_pages(&ia, iocb, inode,
pos, count);
if (!err) {
Expand Down Expand Up @@ -1807,6 +1774,88 @@ static ssize_t fuse_writeback_write_iter(struct kiocb *iocb,
return written < 0 ? written : total_written;
}

/*
* Write @len bytes of @from at the current iocb position, either straight
* through to the server (@through, for an unaligned edge) or through the
* iomap page cache path (@through == false, for the aligned interior).
* Both primitives consume @len bytes from @from and advance iocb->ki_pos;
* the iterator is temporarily capped to @len so the unconsumed tail stays
* available for the next chunk. Returns bytes written (< @len means a
* short write, the caller stops) or a negative error.
*/
static ssize_t fuse_dlm_write_chunk(struct kiocb *iocb, struct iov_iter *from,
struct file *file, size_t len, bool through)
{
size_t hidden;
ssize_t res;

if (!len)
return 0;

/* Cap the iterator to this chunk, keeping the tail for later chunks. */
hidden = iov_iter_count(from) - len;
iov_iter_truncate(from, len);
res = through ? fuse_perform_write(iocb, from, true)
: fuse_writeback_write_iter(iocb, from, file);
/* Restore from the iterator's own residue, so short writes/errors
* (which leave it partly advanced) reexpand to the exact remainder. */
iov_iter_reexpand(from, iov_iter_count(from) + hidden);

return res;
}

/*
* Buffered write under DLM. A partial page dirtied for writeback would
* have to be completed by reading the untouched remainder back from the
* server, and for a write past the server EOF that READ can only return
* zero bytes: a wasted round trip per unaligned edge. So cache only the
* page-aligned interior, whole pages need no read-modify-write, and
* route the unaligned head and tail straight through to the server. The
* writethrough path writes just those bytes and leaves the page
* non-uptodate, doing no read, and each edge lands as an independent
* FUSE_WRITE carrying FUSE_WRITE_CACHE like the writeback it replaces,
* so writers sharing a boundary page accumulate their bytes on the
* server. Aligned writes take the interior path whole; a
* sub-page write with no aligned interior goes fully through.
*/
static ssize_t fuse_dlm_buffered_write(struct kiocb *iocb,
struct iov_iter *from,
struct file *file)
{
loff_t pos = iocb->ki_pos;
loff_t end = pos + iov_iter_count(from);
loff_t mid_start = round_up(pos, PAGE_SIZE);
loff_t mid_end = round_down(end, PAGE_SIZE);
ssize_t res, total = 0;

/* No whole page inside the write: nothing cacheable, all through. */
if (mid_end <= mid_start)
return fuse_perform_write(iocb, from, true);

/* Unaligned head [pos, mid_start): through. */
res = fuse_dlm_write_chunk(iocb, from, file, mid_start - pos, true);
if (res < 0)
return res;
total += res;
if (res < mid_start - pos)
return total;

/* Aligned interior [mid_start, mid_end): cached whole pages. */
res = fuse_dlm_write_chunk(iocb, from, file, mid_end - mid_start, false);
if (res < 0)
return total;
total += res;
if (res < mid_end - mid_start)
return total;

/* Unaligned tail [mid_end, end): through. */
res = fuse_dlm_write_chunk(iocb, from, file, end - mid_end, true);
if (res > 0)
total += res;

return total;
}

static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from);

/*
Expand Down Expand Up @@ -1975,9 +2024,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
/*
* An append write lands at the current EOF no matter what
* ki_pos holds: generic_write_checks() rewrites ki_pos to
* i_size for IOCB_APPEND, and i_size is stable here because
* append writes hold the inode lock exclusive. Lock where
* the data will land.
* i_size for IOCB_APPEND. Lock where the data will land.
*/
dlm_pos = i_size_read(inode);
dlm_len = iov_iter_count(from);
Expand All @@ -1992,6 +2039,24 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (err <= 0)
goto out;

/*
* The exclusive inode lock does not pin i_size for the append:
* attribute replies move it under fi->lock alone, so
* generic_write_checks() may have put ki_pos past the granted
* range. Re-lock where the write really lands; dlm_pos tracks it
* so the in-gate re-validation below guards the same range.
*/
if (writeback && fc->dlm && (iocb->ki_flags & IOCB_APPEND) &&
iocb->ki_pos != dlm_pos) {
dlm_pos = iocb->ki_pos;
dlm_len = count;

err = fuse_cache_wr_dlm_lock(file, dlm_pos, dlm_len,
&dlm_unrecorded);
if (err)
goto out;
}

/*
* Kill suid/sgid and stamp the timestamps here, before the gate,
* instead of leaving them next to the write itself. kiocb_modified()
Expand Down Expand Up @@ -2044,7 +2109,8 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (written < 0 || !iov_iter_count(from))
goto out;
written = direct_write_fallback(iocb, from, written,
fuse_perform_write(iocb, from));
fuse_perform_write(iocb, from,
false));
} else if (writeback) {
loff_t pos = iocb->ki_pos;
loff_t end = pos + count;
Expand Down Expand Up @@ -2085,7 +2151,16 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
pagecache_isize_extended(inode, orig_size, pos);
}

written = fuse_writeback_write_iter(iocb, from, file);
/*
* Under DLM the unaligned edges go through to the server
* instead of being completed by a read-modify-write READ
* (see fuse_dlm_buffered_write()); only whole pages are
* cached for writeback.
*/
if (fc->dlm)
written = fuse_dlm_buffered_write(iocb, from, file);
else
written = fuse_writeback_write_iter(iocb, from, file);

/*
* Reconcile the speculative extension with what was actually
Expand All @@ -2111,7 +2186,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
goto out;
}
} else {
written = fuse_perform_write(iocb, from);
written = fuse_perform_write(iocb, from, false);
}
out:
if (wb_guard)
Expand Down Expand Up @@ -2598,20 +2673,6 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
if (!fc->writeback_cache)
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODIFY);
spin_lock(&fi->lock);
if (!error) {
struct fuse_write_in *inarg = &wpa->ia.write.in;

/*
* The server acknowledged this writeback, so data up to the
* end of the request is materialized on the server. Advance
* the bound before the folios end writeback below, i.e.
* before they can go clean and be reclaimed, so that
* fuse_iomap_read_folio_range() can never zero-fill a
* reclaimed range the server holds data in.
*/
if ((loff_t) (inarg->offset + inarg->size) > fi->server_size)
fi->server_size = inarg->offset + inarg->size;
}
fi->writectr--;
fuse_writepage_finish(wpa);
spin_unlock(&fi->lock);
Expand Down Expand Up @@ -3921,7 +3982,6 @@ void fuse_init_file_inode(struct inode *inode, unsigned int flags)
fuse_dlm_cache_init(fi);
fi->writectr = 0;
fi->iocachectr = 0;
fi->server_size = 0;
init_waitqueue_head(&fi->page_waitq);
init_waitqueue_head(&fi->direct_io_waitq);
/*
Expand Down
20 changes: 0 additions & 20 deletions fs/fuse/fuse_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@
/* Frequency (in seconds) of request timeout checks, if opted into */
#define FUSE_TIMEOUT_TIMER_FREQ 15

/*
* Upper bound (in seconds) for request timeouts. fuse_init_out request
* timeouts are u16, and this goes up to ~18 hours, which is plenty.
*/
#define FUSE_REQ_TIMEOUT_LIMIT 65535

/** Frequency (in jiffies) of request timeout checks, if opted into */
extern const unsigned long fuse_timeout_timer_freq;

Expand Down Expand Up @@ -208,20 +202,6 @@ struct fuse_inode {
/* dlm locked areas we have sent lock requests for */
struct fuse_dlm_cache dlm_locked_areas;

/*
* Server-materialized size: an upper bound for how far
* the server holds file data. Seeded from
* server-reported attributes, advanced when the server
* acknowledges data (writeback completion,
* fuse_write_update_attr()), lowered again on
* truncate. A read-modify-write of a block starting
* at or past this bound needs no READ request under a
* held DLM write lock: the server has no data there
* (see fuse_iomap_read_folio_range()). Protected by
* fi->lock.
*/
loff_t server_size;

/*
* Serializes buffered-write page-cache dirtying against
* the forced-direct-IO latch transition driven by
Expand Down
Loading
Loading