Commit 5c791fe1 authored by Miklos Szeredi's avatar Miklos Szeredi

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: default avatarchenguanyou <chenguanyou@xiaomi.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 964d32e5
...@@ -738,11 +738,19 @@ static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir, ...@@ -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); 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) void fuse_update_ctime(struct inode *inode)
{ {
if (!IS_NOCMTIME(inode)) { if (!IS_NOCMTIME(inode)) {
inode->i_ctime = current_time(inode); inode->i_ctime = current_time(inode);
mark_inode_dirty_sync(inode); mark_inode_dirty_sync(inode);
fuse_flush_time_update(inode);
} }
} }
......
...@@ -1848,6 +1848,17 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) ...@@ -1848,6 +1848,17 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
struct fuse_file *ff; struct fuse_file *ff;
int err; int err;
/*
* 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(fi); ff = __fuse_write_file_get(fi);
err = fuse_flush_times(inode, ff); err = fuse_flush_times(inode, ff);
if (ff) if (ff)
...@@ -3002,6 +3013,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, ...@@ -3002,6 +3013,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
if (lock_inode) if (lock_inode)
inode_unlock(inode); inode_unlock(inode);
fuse_flush_time_update(inode);
return err; return err;
} }
...@@ -3111,6 +3124,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -3111,6 +3124,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
inode_unlock(inode_out); inode_unlock(inode_out);
file_accessed(file_in); file_accessed(file_in);
fuse_flush_time_update(inode_out);
return err; return err;
} }
......
...@@ -1148,6 +1148,7 @@ int fuse_allow_current_process(struct fuse_conn *fc); ...@@ -1148,6 +1148,7 @@ int fuse_allow_current_process(struct fuse_conn *fc);
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id); 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); void fuse_update_ctime(struct inode *inode);
int fuse_update_attributes(struct inode *inode, struct file *file); int fuse_update_attributes(struct inode *inode, struct file *file);
......
...@@ -118,6 +118,9 @@ static void fuse_evict_inode(struct inode *inode) ...@@ -118,6 +118,9 @@ static void fuse_evict_inode(struct inode *inode)
{ {
struct fuse_inode *fi = get_fuse_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); truncate_inode_pages_final(&inode->i_data);
clear_inode(inode); clear_inode(inode);
if (inode->i_sb->s_flags & SB_ACTIVE) { if (inode->i_sb->s_flags & SB_ACTIVE) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment