From 389625e5d729aae318768f66cd315fd507861966 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 22 Oct 2021 17:03:01 +0200 Subject: [PATCH] UPSTREAM: fuse: make sure reclaim doesn't write the inode In writeback cache mode mtime/ctime updates are cached, and flushed to the server using the ->write_inode() callback. Closing the file will result in a dirty inode being immediately written, but in other cases the inode can remain dirty after all references are dropped. This result in the inode being written back from reclaim, which can deadlock on a regular allocation while the request is being served. The usual mechanisms (GFP_NOFS/PF_MEMALLOC*) don't work for FUSE, because serving a request involves unrelated userspace process(es). Instead do the same as for dirty pages: make sure the inode is written before the last reference is gone. - fallocate(2)/copy_file_range(2): these call file_update_time() or file_modified(), so flush the inode before returning from the call - unlink(2), link(2) and rename(2): these call fuse_update_ctime(), so flush the ctime directly from this helper fuse_flush_time_update(inode) was skipped to call in __fuse_copy_file_range() because of huge dependent changes. Change-Id: I102dab1992c9ed2b5e89606265b3d3aa9c1cdb8a Reported-by: chenguanyou Signed-off-by: Miklos Szeredi Git-commit: 5c791fe1e2a4f401f819065ea4fc0450849f1818 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Pradeep P V K --- fs/fuse/dir.c | 8 ++++++++ fs/fuse/file.c | 12 ++++++++++++ fs/fuse/fuse_i.h | 1 + fs/fuse/inode.c | 3 +++ 4 files changed, 24 insertions(+) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 02c00217b385..a55375ae2797 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1027,11 +1027,19 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, return create_new_entry(fm, &args, dir, entry, S_IFLNK); } +void fuse_flush_time_update(struct inode *inode) +{ + int err = sync_inode_metadata(inode, 1); + + mapping_set_error(inode->i_mapping, err); +} + void fuse_update_ctime(struct inode *inode) { if (!IS_NOCMTIME(inode)) { inode->i_ctime = current_time(inode); mark_inode_dirty_sync(inode); + fuse_flush_time_update(inode); } } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index cad73bad9c90..d96daeaa807b 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2022,6 +2022,16 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) */ if (!S_ISREG(inode->i_mode)) return 0; + /* + * Inode is always written before the last reference is dropped and + * hence this should not be reached from reclaim. + * + * Writing back the inode from reclaim can deadlock if the request + * processing itself needs an allocation. Allocations triggering + * reclaim while serving a request can't be prevented, because it can + * involve any number of unrelated userspace processes. + */ + WARN_ON(wbc->for_reclaim); ff = __fuse_write_file_get(fc, fi); err = fuse_flush_times(inode, ff); @@ -3559,6 +3569,8 @@ out: inode_unlock(inode); + fuse_flush_time_update(inode); + return err; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index a76019d9403f..97b44bbf5274 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1216,6 +1216,7 @@ int fuse_allow_current_process(struct fuse_conn *fc); u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id); +void fuse_flush_time_update(struct inode *inode); void fuse_update_ctime(struct inode *inode); int fuse_update_attributes(struct inode *inode, struct file *file); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 39d9d531c965..e37fba257c8b 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -125,6 +125,9 @@ static void fuse_evict_inode(struct inode *inode) { struct fuse_inode *fi = get_fuse_inode(inode); + /* Will write inode on close/munmap and in all other dirtiers */ + WARN_ON(inode->i_state & I_DIRTY_INODE); + truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (inode->i_sb->s_flags & SB_ACTIVE) {