diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2021-10-22 17:03:01 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2021-10-22 17:03:01 +0200 |
commit | 5c791fe1e2a4f401f819065ea4fc0450849f1818 (patch) | |
tree | 152b7cd2888395c394e1ed6eb82e94b238421574 /fs/fuse/dir.c | |
parent | 964d32e512670c7b87870e30cfed2303da86d614 (diff) |
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
Reported-by: chenguanyou <chenguanyou@xiaomi.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index d9b977c0f38d..2798fbe8d001 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -738,11 +738,19 @@ static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir, 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); } } |