Skip to content

Commit 5c791fe

Browse files
author
Miklos Szeredi
committed
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 <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 964d32e commit 5c791fe

File tree

4 files changed

+27
-0
lines changed

4 files changed

+27
-0
lines changed

fs/fuse/dir.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,11 +738,19 @@ static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir,
738738
return create_new_entry(fm, &args, dir, entry, S_IFLNK);
739739
}
740740

741+
void fuse_flush_time_update(struct inode *inode)
742+
{
743+
int err = sync_inode_metadata(inode, 1);
744+
745+
mapping_set_error(inode->i_mapping, err);
746+
}
747+
741748
void fuse_update_ctime(struct inode *inode)
742749
{
743750
if (!IS_NOCMTIME(inode)) {
744751
inode->i_ctime = current_time(inode);
745752
mark_inode_dirty_sync(inode);
753+
fuse_flush_time_update(inode);
746754
}
747755
}
748756

fs/fuse/file.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,6 +1848,17 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
18481848
struct fuse_file *ff;
18491849
int err;
18501850

1851+
/*
1852+
* Inode is always written before the last reference is dropped and
1853+
* hence this should not be reached from reclaim.
1854+
*
1855+
* Writing back the inode from reclaim can deadlock if the request
1856+
* processing itself needs an allocation. Allocations triggering
1857+
* reclaim while serving a request can't be prevented, because it can
1858+
* involve any number of unrelated userspace processes.
1859+
*/
1860+
WARN_ON(wbc->for_reclaim);
1861+
18511862
ff = __fuse_write_file_get(fi);
18521863
err = fuse_flush_times(inode, ff);
18531864
if (ff)
@@ -3002,6 +3013,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
30023013
if (lock_inode)
30033014
inode_unlock(inode);
30043015

3016+
fuse_flush_time_update(inode);
3017+
30053018
return err;
30063019
}
30073020

@@ -3111,6 +3124,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
31113124
inode_unlock(inode_out);
31123125
file_accessed(file_in);
31133126

3127+
fuse_flush_time_update(inode_out);
3128+
31143129
return err;
31153130
}
31163131

fs/fuse/fuse_i.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,7 @@ int fuse_allow_current_process(struct fuse_conn *fc);
11481148

11491149
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
11501150

1151+
void fuse_flush_time_update(struct inode *inode);
11511152
void fuse_update_ctime(struct inode *inode);
11521153

11531154
int fuse_update_attributes(struct inode *inode, struct file *file);

fs/fuse/inode.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ static void fuse_evict_inode(struct inode *inode)
118118
{
119119
struct fuse_inode *fi = get_fuse_inode(inode);
120120

121+
/* Will write inode on close/munmap and in all other dirtiers */
122+
WARN_ON(inode->i_state & I_DIRTY_INODE);
123+
121124
truncate_inode_pages_final(&inode->i_data);
122125
clear_inode(inode);
123126
if (inode->i_sb->s_flags & SB_ACTIVE) {

0 commit comments

Comments
 (0)