diff --git a/fs/fuse/file.c b/fs/fuse/file.c index e583a93c10467c..a93b0c1224aaf7 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3435,6 +3435,8 @@ static int fuse_get_page_mkwrite_lock(struct file *file, loff_t offset, size_t l fuse_abort_conn(fc); err = -EINVAL; } + if (!err && fc->dlm) + fuse_dlm_attr_timeout(get_fuse_inode(inode)); return err; } diff --git a/fs/fuse/fuse_dlm_cache.c b/fs/fuse/fuse_dlm_cache.c index e6080f312cc047..c641f1cb8ddbcf 100644 --- a/fs/fuse/fuse_dlm_cache.c +++ b/fs/fuse/fuse_dlm_cache.c @@ -827,6 +827,8 @@ int fuse_get_dlm_lock(struct file *file, loff_t offset, if (err) return err; + fuse_dlm_attr_timeout(fi); + if (inarg.start < outarg.start || inarg.end > outarg.end) { /* fuse server is seriously broken */ pr_warn("fuse: dlm lock request for %llu:%llu returned %llu:%llu bytes\n", diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 43fef648e0f5a5..e136765039c05c 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -321,6 +321,12 @@ enum { * mapping in the page cache. */ FUSE_I_SHARED_WRITE_MMAP, + /* + * A DLM lock was taken on this inode and no attribute reply has + * arrived since: i_time is bounded to dlm_attr_timeout seconds from + * the lock, see fuse_dlm_attr_timeout(). + */ + FUSE_I_DLM_LOCKED, }; struct fuse_conn; @@ -1580,6 +1586,9 @@ struct inode *fuse_ilookup(struct fuse_conn *fc, u64 nodeid, int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, loff_t offset, loff_t len); +/* A DLM lock was taken on @fi: bound its attribute timeout until the next reply */ +void fuse_dlm_attr_timeout(struct fuse_inode *fi); + /** * File-system tells the kernel to invalidate parent attributes and * the dentry matching parent/name. diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 28674d4dd3b46a..a5c3e00f782e99 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -49,6 +49,39 @@ module_param(enable_notify_dio, bool, 0644); MODULE_PARM_DESC(enable_notify_dio, "Latch a contended inode to direct IO on an invalidation notify storm"); +/* + * Attribute timeout of an inode once a DLM lock was taken on it, in + * seconds: i_time is bounded to it until the next attribute reply from + * the server, so a GETATTR goes out at most this long after the lock. + * 0 leaves the server's timeout alone. + */ +static unsigned int __read_mostly dlm_attr_timeout = 30; +module_param(dlm_attr_timeout, uint, 0644); +MODULE_PARM_DESC(dlm_attr_timeout, + "Seconds until the attributes of an inode a DLM lock was taken on are fetched again (0 = server's timeout)"); + +void fuse_dlm_attr_timeout(struct fuse_inode *fi) +{ + struct fuse_conn *fc = get_fuse_conn(&fi->inode); + unsigned int timeout = READ_ONCE(dlm_attr_timeout); + u64 valid; + + if (!timeout) + return; + valid = get_jiffies_64() + (u64)timeout * HZ; + + spin_lock(&fi->lock); + /* + * Only a reply to a request sent after the lock may end the bound: + * one to an earlier request is discarded by fuse_change_attributes_i(). + */ + if (!test_and_set_bit(FUSE_I_DLM_LOCKED, &fi->state)) + fi->attr_version = atomic64_inc_return(&fc->attr_version); + if (time_after64(fi->i_time, valid)) + fi->i_time = valid; + spin_unlock(&fi->lock); +} + static struct kmem_cache *fuse_inode_cachep; struct list_head fuse_conn_list; DEFINE_MUTEX(fuse_mutex); @@ -390,6 +423,9 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, lockdep_assert_held(&fi->lock); + /* The first attribute reply after a DLM lock ends the bounded timeout */ + clear_bit(FUSE_I_DLM_LOCKED, &fi->state); + if (sx) { return fuse_change_attributes_common_sx(inode, attr, sx, attr_valid, cache_mask,