When all the references of an inode are dropped, and write of its dirty pages is serving in Daemon, reclaim may deadlock on a regular allocation in Daemon. Add fuse_dentry_iput and some FI_* flags to postponed the iput for the inodes be using in fuse_write_inode. Signed-off-by: Ed Tsai <ed.tsai@xxxxxxxxxxxx> --- Hi Miklos, I define d_iput for fuse to postpone the iput until the fuse_write_inode is done. This works fine at our platform. Please help to check this. fs/fuse/dir.c | 17 +++++++++++++++++ fs/fuse/file.c | 15 +++++++++++++++ fs/fuse/fuse_i.h | 4 ++++ 3 files changed, 36 insertions(+) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 74303d6e987b..37d768088c87 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -331,6 +331,22 @@ static struct vfsmount *fuse_dentry_automount(struct path *path) return mnt; } +static void fuse_dentry_iput(struct dentry *dentry, struct inode *inode) +{ + struct fuse_inode *fi = get_fuse_inode(inode); + int need_iput = true; + + spin_lock(&fi->lock); + if (test_bit(FUSE_I_WRITING, &fi->state)) { + set_bit(FUSE_I_NEED_IPUT, &fi->state); + need_iput = false; + } + spin_unlock(&fi->lock); + + if (need_iput) + iput(inode); +} + const struct dentry_operations fuse_dentry_operations = { .d_revalidate = fuse_dentry_revalidate, .d_delete = fuse_dentry_delete, @@ -339,6 +355,7 @@ const struct dentry_operations fuse_dentry_operations = { .d_release = fuse_dentry_release, #endif .d_automount = fuse_dentry_automount, + .d_iput = fuse_dentry_iput, }; const struct dentry_operations fuse_root_dentry_operations = { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 05caa2b9272e..a291784d42bc 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1848,6 +1848,7 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) { struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_file *ff; + int need_iput = false; int err; /* @@ -1862,10 +1863,24 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) WARN_ON(wbc->for_reclaim); ff = __fuse_write_file_get(fi); + + spin_lock(&fi->lock); + set_bit(FUSE_I_WRITING, &fi->state); + spin_unlock(&fi->lock); + err = fuse_flush_times(inode, ff); if (ff) fuse_file_put(ff, false, false); + spin_lock(&fi->lock); + clear_bit(FUSE_I_WRITING, &fi->state); + if (test_and_clear_bit(FUSE_I_NEED_IPUT, &fi->state)) + need_iput = true; + spin_unlock(&fi->lock); + + if (need_iput) + iput(inode); + return err; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 488b460e046f..f5851e5397ce 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -167,6 +167,10 @@ enum { FUSE_I_SIZE_UNSTABLE, /* Bad inode */ FUSE_I_BAD, + /* inode is writing */ + FUSE_I_WRITING, + /* inode need iput() after writing */ + FUSE_I_NEED_IPUT, }; struct fuse_conn; -- 2.18.0