From: Darrick J. Wong <djwong@xxxxxxxxxx> Cache the timestamps in the kernel so that the kernel sends FUSE_SETATTR calls to the fuse server after writes, because the iomap infrastructure won't do that for us. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- fs/fuse/dir.c | 3 ++- fs/fuse/file.c | 19 +++++++++++++------ fs/fuse/file_iomap.c | 6 ++++++ fs/fuse/inode.c | 13 +++++++------ 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 7a398e42e9818b..1e9d5bf1811c6a 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1964,7 +1964,8 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct fuse_setattr_in inarg; struct fuse_attr_out outarg; bool is_truncate = false; - bool is_wb = fc->writeback_cache && S_ISREG(inode->i_mode); + bool is_wb = S_ISREG(inode->i_mode) && + (fuse_has_iomap_fileio(inode) || fc->writeback_cache); loff_t oldsize; int err; bool trust_local_cmtime = is_wb; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 2dd4e5c2933c0f..207836e2e09cc4 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -238,7 +238,8 @@ static int fuse_open(struct inode *inode, struct file *file) struct fuse_file *ff; int err; bool is_truncate = (file->f_flags & O_TRUNC) && fc->atomic_o_trunc; - bool is_wb_truncate = is_truncate && fc->writeback_cache; + bool is_wb_truncate = is_truncate && (fuse_has_iomap_fileio(inode) || + fc->writeback_cache); bool dax_truncate = is_truncate && FUSE_IS_DAX(inode); if (fuse_is_bad(inode)) @@ -458,7 +459,9 @@ static int fuse_flush(struct file *file, fl_owner_t id) if (fuse_is_bad(inode)) return -EIO; - if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache) + if ((ff->open_flags & FOPEN_NOFLUSH) && + !fm->fc->writeback_cache && + !fuse_has_iomap_fileio(inode)) return 0; err = write_inode_now(inode, 1); @@ -494,7 +497,7 @@ static int fuse_flush(struct file *file, fl_owner_t id) * In memory i_blocks is not maintained by fuse, if writeback cache is * enabled, i_blocks from cached attr may not be accurate. */ - if (!err && fm->fc->writeback_cache) + if (!err && (fuse_has_iomap_fileio(inode) || fm->fc->writeback_cache)) fuse_invalidate_attr_mask(inode, STATX_BLOCKS); return err; } @@ -792,8 +795,10 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read, * If writeback_cache is enabled, a short read means there's a hole in * the file. Some data after the hole is in page cache, but has not * reached the client fs yet. So the hole is not present there. + * If iomap is enabled, a short read means we hit EOF so there's + * nothing to adjust. */ - if (!fc->writeback_cache) { + if (!fc->writeback_cache && !fuse_has_iomap_fileio(inode)) { loff_t pos = folio_pos(ap->folios[0]) + num_read; fuse_read_update_size(inode, pos, attr_ver); } @@ -1935,7 +1940,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args, * Do this only if writeback_cache is not enabled. If writeback_cache * is enabled, we trust local ctime/mtime. */ - if (!fc->writeback_cache) + if (!fc->writeback_cache && !fuse_has_iomap_fileio(inode)) fuse_invalidate_attr_mask(inode, FUSE_STATX_MODIFY); spin_lock(&fi->lock); fi->writectr--; @@ -2266,6 +2271,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, int err = -ENOMEM; WARN_ON(!fc->writeback_cache); + WARN_ON(fuse_has_iomap_fileio(mapping->host)); folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN, mapping_gfp_mask(mapping)); @@ -3108,7 +3114,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, ssize_t err; /* mark unstable when write-back is not used, and file_out gets * extended */ - bool is_unstable = (!fc->writeback_cache) && + bool is_unstable = (!fc->writeback_cache && + !fuse_has_iomap_fileio(inode_out)) && ((pos_out + len) > inode_out->i_size); if (fc->no_copy_file_range) diff --git a/fs/fuse/file_iomap.c b/fs/fuse/file_iomap.c index ab0dee6460a7dd..112cbb6cabb015 100644 --- a/fs/fuse/file_iomap.c +++ b/fs/fuse/file_iomap.c @@ -1342,6 +1342,12 @@ static inline void fuse_iomap_set_fileio(struct inode *inode) ASSERT(get_fuse_conn_c(inode)->iomap_fileio); + /* + * Manage timestamps ourselves, don't make the fuse server do it. This + * is critical for mtime updates to work correctly with page_mkwrite. + */ + inode->i_flags &= ~S_NOCMTIME; + inode->i_flags &= ~S_NOATIME; inode->i_data.a_ops = &fuse_iomap_aops; INIT_WORK(&fi->ioend_work, fuse_iomap_end_io); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 3e92a29d1030c9..d67cc635612cff 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -328,10 +328,11 @@ u32 fuse_get_cache_mask(struct inode *inode) { struct fuse_conn *fc = get_fuse_conn(inode); - if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) - return 0; + if (S_ISREG(inode->i_mode) && + (fuse_has_iomap_fileio(inode) || fc->writeback_cache)) + return STATX_MTIME | STATX_CTIME | STATX_SIZE; - return STATX_MTIME | STATX_CTIME | STATX_SIZE; + return 0; } static void fuse_change_attributes_i(struct inode *inode, struct fuse_attr *attr, @@ -346,9 +347,9 @@ static void fuse_change_attributes_i(struct inode *inode, struct fuse_attr *attr spin_lock(&fi->lock); /* - * In case of writeback_cache enabled, writes update mtime, ctime and - * may update i_size. In these cases trust the cached value in the - * inode. + * In case of writeback_cache or iomap enabled, writes update mtime, + * ctime and may update i_size. In these cases trust the cached value + * in the inode. */ cache_mask = fuse_get_cache_mask(inode); if (cache_mask & STATX_SIZE)