[Devel,vz7] fuse: fuse_send_writepage() must check FUSE_S_FAIL_IMMEDIATELY

Submitted by Maxim Patlasov on Dec. 7, 2016, 12:58 a.m.


Message ID 148107226639.10419.14840857137316566673.stgit@maxim-thinkpad
State New
Series "fuse: fuse_send_writepage() must check FUSE_S_FAIL_IMMEDIATELY"
Headers show

Commit Message

Maxim Patlasov Dec. 7, 2016, 12:58 a.m.
The patch fixes the following race (leading to deadlock):

1. Thread A. Enter fuse_prepare_write() checks for FUSE_S_FAIL_IMMEDIATELY,
but it was not set yet, so it doesn't return -EIO.

2. Thread B. Enter fuse_invalidate_files(). It sets FUSE_S_FAIL_IMMEDIATELY,
calls filemap_write_and_wait() and fuse_kill_requests(), then release

3. Thread A. fuse_commit_write() marks page as "Dirty", then
fuse_write_end() unlocks the page.

4. Thread B. fuse_invalidate_files() calls invalidate_inode_pages2(). The
page is dirty, so it ends up in fuse_launder_page() calling
fuse_writepage_locked(). The latter successfully proceeds queuing
fuse-write-back request, but then fuse_launder_page() calls
fuse_wait_on_page_writeback() that blocks forever because Thread A is still
blocked in fuse_invalidate_files().

Signed-off-by: Maxim Patlasov <mpatlasov@virtuozzo.com>
 fs/fuse/file.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Patch hide | download patch | download mbox

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 4fcf4f4..e21b8b7 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1827,7 +1827,8 @@  __acquires(fc->lock)
 	struct fuse_write_in *inarg = &req->misc.write.in;
 	__u64 data_size = req->num_pages * PAGE_CACHE_SIZE;
-	if (!fc->connected)
+	if (!fc->connected ||
+	    test_bit(FUSE_S_FAIL_IMMEDIATELY, &req->ff->ff_state))
 		goto out_free;
 	if (inarg->offset + data_size <= size) {