diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a475288f76c6130d8062b5294e5877357354db8e..ea7e6503d7d49b35b90dd725cd0c2cdb7be52e5a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1055,7 +1055,7 @@ static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) * Otherwise, only update if we attempt to read past EOF (to ensure * i_size is up to date). */ - if (fc->auto_inval_data || + if (fc->auto_inval_data || fc->explicit_lazy_inval_data || (iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) { int err; err = fuse_update_attributes(inode, iocb->ki_filp); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index eb4e23f194199bc0d1779c85638a9209e580fb32..a2c371246de4f099c9f9a155233094ec47b743fa 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -209,6 +209,8 @@ enum { FUSE_I_INIT_RDPLUS, /** An operation changing file size is in progress */ FUSE_I_SIZE_UNSTABLE, + /** Hint that page cache may be stale */ + FUSE_I_DATA_STALE, /* Bad inode */ FUSE_I_BAD, }; @@ -848,6 +850,9 @@ struct fuse_conn { /** Filesystem is fully responsible for page cache invalidation. */ unsigned explicit_inval_data:1; + /** Like the previous one, but the invalidation will be delayed */ + unsigned explicit_lazy_inval_data:1; + /** Does the filesystem support readdirplus? */ unsigned do_readdirplus:1; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 4f2298b752eb230b83f49156dc46bb73cd5aa795..0f47efc26f26fe472b97fc1448a911edf68fb622 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -318,7 +318,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, if (oldsize != attr->size) { truncate_pagecache(inode, attr->size); - if (!fc->explicit_inval_data) + if (!fc->explicit_inval_data && !fc->explicit_lazy_inval_data) inval = true; } else if (fc->auto_inval_data) { struct timespec64 new_mtime = { @@ -342,6 +342,11 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, } } + if (test_and_clear_bit(FUSE_I_DATA_STALE, &fi->state)) { + WARN_ON(is_wb); + inval = true; + } + if (inval) invalidate_inode_pages2(inode->i_mapping); } @@ -487,6 +492,7 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, struct inode *inode; pgoff_t pg_start; pgoff_t pg_end; + bool lazy_inval = false; inode = fuse_ilookup(fc, nodeid, NULL); if (!inode) @@ -497,6 +503,12 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, fi->attr_version = atomic64_inc_return(&fc->attr_version); spin_unlock(&fi->lock); + if (fc->explicit_lazy_inval_data && !fc->writeback_cache && + (offset == 0 && len <= 0)) { + set_bit(FUSE_I_DATA_STALE, &fi->state); + lazy_inval = true; + } + fuse_invalidate_attr(inode); forget_all_cached_acls(inode); fuse_invalidate_inval_version(inode); @@ -506,8 +518,10 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, pg_end = -1; else pg_end = (offset + len - 1) >> PAGE_SHIFT; - invalidate_inode_pages2_range(inode->i_mapping, - pg_start, pg_end); + + if (!lazy_inval) + invalidate_inode_pages2_range(inode->i_mapping, + pg_start, pg_end); } iput(inode); return 0; @@ -1199,6 +1213,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args, fc->auto_inval_data = 1; else if (flags & FUSE_EXPLICIT_INVAL_DATA) fc->explicit_inval_data = 1; + else if (flags & FUSE_EXPLICIT_LAZY_INVAL_DATA) + fc->explicit_lazy_inval_data = 1; if (flags & FUSE_DO_READDIRPLUS) { fc->do_readdirplus = 1; if (flags & FUSE_READDIRPLUS_AUTO) @@ -1329,7 +1345,7 @@ static void fuse_prepare_send_init(struct fuse_mount *fm, FUSE_INVALDIR_ALLENTRY | FUSE_DELETE_STALE | FUSE_DIRECT_IO_ALLOW_MMAP | FUSE_NO_EXPORT_SUPPORT | FUSE_HAS_RESEND | FUSE_SEPARATE_BACKGROUND | FUSE_HAS_RECOVERY | - FUSE_WRITE_ALIGNMENT; + FUSE_WRITE_ALIGNMENT | FUSE_EXPLICIT_LAZY_INVAL_DATA; #ifdef CONFIG_FUSE_DAX if (fm->fc->dax) flags |= FUSE_MAP_ALIGNMENT; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index f3f3631389a0dcede417de6acc212dfd22d5ead8..a76e154180c5393974ea66c5057fc213a67801de 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -390,6 +390,7 @@ struct fuse_file_lock { #define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36) #define FUSE_NO_EXPORT_SUPPORT (1ULL << 38) #define FUSE_HAS_RESEND (1ULL << 39) +#define FUSE_EXPLICIT_LAZY_INVAL_DATA (1ULL << 54) #define FUSE_WRITE_ALIGNMENT (1ULL << 55) #define FUSE_SEPARATE_BACKGROUND (1ULL << 56) #define FUSE_HAS_RECOVERY (1ULL << 57)