The commit is pushed to "branch-rh7-3.10.0-514.vz7.27.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git after rh7-3.10.0-514.vz7.27.12 ------> commit c11cada4b4303f8eb8987f080617a3a6f761c8b3 Author: Maxim Patlasov <mpatla...@virtuozzo.com> Date: Tue Jan 17 16:10:57 2017 +0400
fuse: fuse_writepages_fill must check for FAIL_IMMEDIATELY The patch fixes a deadlock: if a page is stuck under fuse writeback for a while, and an user re-dirty the page, next writeback will start to wait for fuse writeback inside fuse_writepages_fill: > if (fuse_page_is_writeback(inode, page->index)) { > if (wbc->sync_mode != WB_SYNC_ALL) { > redirty_page_for_writepage(wbc, page); > unlock_page(page); > return 0; > } > fuse_wait_on_page_writeback(inode, page->index); > } That's correct, but if fuse_invalidate_files steps in now, it will block in filemap_write_and_wait because fuse_writepages_fill holds page locked. And even if fused eventually completes initial writeback, it won't be able to send ACK to the kernel, because /dev/fuse is currently busy by fuse_invalidate_files. Hence deadlock. The patch fixes deadlock by waking up fuse_writepages_fill after marking files with FAIL_IMMEDIATELY flag. Signed-off-by: Maxim Patlasov <mpatla...@virtuozzo.com> --- fs/fuse/file.c | 37 ++++++++++++++++++++++++++++++++----- fs/fuse/inode.c | 3 +++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 58313a1..680a437 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -567,17 +567,24 @@ static void fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index) /* * Can be woken up by FUSE_NOTIFY_INVAL_FILES */ -static void fuse_wait_on_page_writeback_or_invalidate(struct inode *inode, - struct file *file, - pgoff_t index) +static void __fuse_wait_on_page_writeback_or_invalidate(struct inode *inode, + struct fuse_file *ff, + pgoff_t index) { struct fuse_inode *fi = get_fuse_inode(inode); - struct fuse_file *ff = file->private_data; wait_event(fi->page_waitq, !fuse_page_is_writeback(inode, index) || test_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state)); } +static void fuse_wait_on_page_writeback_or_invalidate(struct inode *inode, + struct file *file, + pgoff_t index) +{ + return __fuse_wait_on_page_writeback_or_invalidate(inode, + file->private_data, index); +} + static void fuse_wait_on_writeback(struct inode *inode, pgoff_t start, size_t bytes) { @@ -2166,12 +2173,32 @@ static int fuse_writepages_fill(struct page *page, int check_for_blocked = 0; if (fuse_page_is_writeback(inode, page->index)) { + struct fuse_file *ff; + if (wbc->sync_mode != WB_SYNC_ALL) { redirty_page_for_writepage(wbc, page); unlock_page(page); return 0; } - fuse_wait_on_page_writeback(inode, page->index); + + /* we can acquire ff here because we do have locked pages here! */ + ff = fuse_write_file(fc, get_fuse_inode(inode)); + if (!ff) { + printk("FUSE: dirty page on dead file\n"); + unlock_page(page); + return -EIO; + } + + /* FUSE_NOTIFY_INVAL_FILES must be able to wake us up */ + __fuse_wait_on_page_writeback_or_invalidate(inode, ff, page->index); + + if (test_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state)) { + unlock_page(page); + fuse_release_ff(inode, ff); + return 0; + } + + fuse_release_ff(inode, ff); } if (req->num_pages && diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index f606deb..b63aae2 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -413,6 +413,9 @@ int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid) /* let them see FUSE_S_FAIL_IMMEDIATELY */ wake_up_all(&fc->blocked_waitq); + /* see how fuse_writepages_fill() waits for fuse writeback */ + wake_up(&fi->page_waitq); + err = filemap_write_and_wait(inode->i_mapping); if (!err || err == -EIO) { /* AS_EIO might trigger -EIO */ spin_lock(&fc->lock); _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel