[Devel] [PATCH RHEL7 COMMIT] fs/fuse kio: forward fuse_file pointer to kpcs_req_send()

Konstantin Khorenko khorenko at virtuozzo.com
Tue May 21 19:02:17 MSK 2019


The commit is pushed to "branch-rh7-3.10.0-957.12.2.vz7.96.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-957.12.2.vz7.96.1
------>
commit a09812bac12b7c18f372f447f453c5338b2dc9ac
Author: Pavel Butsykin <pbutsykin at virtuozzo.com>
Date:   Tue May 21 19:02:15 2019 +0300

    fs/fuse kio: forward fuse_file pointer to kpcs_req_send()
    
    Unfortunately, synchronous requests don't reference ff, and at the same time use
    page cache. In such a situation, it's just not possible to check ff->ff_state
    inside pcs_fuse_submit(), that may lead to a race bug and as result deadlock
    with fuse_invalidate_files(). To fix this, let's forward fuse_file pointer to
    kpcs_req_send() for synchronous requests with pages.
    
    Signed-off-by: Pavel Butsykin <pbutsykin at virtuozzo.com>
    
    =====================
    Patchset description:
    
    fix deadlock between synchronous reqs and fuse_invalidate_files
    
    One more deadlock with fuse_invalidate_files():
    
    [<ffffffff92ba7cc4>] __lock_page+0x74/0x90
    [<ffffffff92bb9a75>] invalidate_inode_pages2_range+0x445/0x470
    [<ffffffff92bb9ab7>] invalidate_inode_pages2+0x17/0x20
    [<ffffffffc034cde5>] fuse_invalidate_files+0x235/0x270 [fuse]
    [<ffffffffc033d3fb>] fuse_dev_do_write+0x7fb/0xe20 [fuse]
    [<ffffffffc033ddc1>] fuse_dev_write+0x71/0xa0 [fuse]
    [<ffffffff92c3c2e6>] do_sync_write+0x96/0xe0
    [<ffffffff92c3cdc0>] vfs_write+0xc0/0x1f0
    [<ffffffff92c3dbef>] SyS_write+0x7f/0xf0
    [<ffffffff9315589b>] system_call_fastpath+0x22/0x27
    
    This happened because synchronous kio read request was not dropped by kill_requests:
    
    PID: 20684  TASK: ffff9543c2e71160  CPU: 5   COMMAND: "co_io"
    [ffff9547b0faf9f8] __schedule at ffffffff93148a9f
    [ffff9547b0fafa88] schedule at ffffffff93148fe9
    [ffff9547b0fafa98] kpcs_req_send at ffffffffc047dab5 [fuse_kio_pcs]
    [ffff9547b0fafb08] __fuse_request_send at ffffffffc033a987 [fuse]
    [ffff9547b0fafb40] fuse_request_check_and_send at ffffffffc033e097 [fuse]
    [ffff9547b0fafb50] fuse_send_read at ffffffffc03463ec [fuse]
    [ffff9547b0fafb90] __fuse_readpage at ffffffffc03475ad [fuse]
    [ffff9547b0fafc40] fuse_readpage at ffffffffc0347a3c [fuse]
    [ffff9547b0fafca0] generic_file_read_iter at ffffffff92baa186
    [ffff9547b0fafd58] generic_file_aio_read at ffffffff92baa5c5
    [ffff9547b0fafdc0] fuse_file_aio_read at ffffffffc0343788 [fuse]
    [ffff9547b0fafdf0] do_sync_read at ffffffff92c3c206
    [ffff9547b0fafed0] vfs_read at ffffffff92c3cc2f
    [ffff9547b0faff00] sys_pread64 at ffffffff92c3dcf2
    [ffff9547b0faff50] system_call_fastpath at ffffffff9315589b
    
     struct pcs_fuse_req {
     req = {
        list = {
          next = 0xffff95479374eb18,
          prev = 0xffff9547acf76e00
        },
        ...
        page_cache = 1,
        page_needs_release = 0,
        killed = 0,
        ...
        ff = 0x0,
        io_inode = 0xffff95445da0b740,
    
    The fuse_file pointer was not initialized in req->ff, that means we can't check
    ff->ff_state inside pcs_fuse_submit(), despite the fact that this request has
    locked pages. It's believed that requests with an empty req->ff don't have pages,
    therefore, such requests may not be synchronized with fuse_invalidate_files().
    However, synchronous requests don't reference ff, but pass it as a parameter to
    fuse_request_check_and_send().
    
    This patchset fixes it.
    
    https://pmc.acronis.com/browse/VSTOR-23034
    Reviewed-by: Kirill Tkhai <ktkhai at virtuozzo.com>
    
    Pavel Butsykin (4):
      fs/fuse kio: forward fuse_file pointer to kpcs_req_send()
      fs/fuse kio: add pending kio requests to kqueue
      fs/fuse kio: style fix in pcs_fuse_submit()
      fs/fuse kio: keep fuse_file for requests waiting for shrink
---
 fs/fuse/dev.c                      |  6 +++---
 fs/fuse/fuse_i.h                   |  4 ++--
 fs/fuse/kio/pcs/pcs_fuse_kdirect.c | 12 +++++++-----
 3 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index f9e33a504c70..a3e9e7aacafa 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -401,7 +401,7 @@ static void flush_bg_queue(struct fuse_conn *fc, struct fuse_iqueue *fiq)
 		list_del_init(&req->list);
 		fc->active_background++;
 
-		if (fc->kio.op && !fc->kio.op->req_send(fc, req, true, true))
+		if (fc->kio.op && !fc->kio.op->req_send(fc, req, NULL, true, true))
 			continue;
 
 		spin_lock(&fiq->waitq.lock);
@@ -555,7 +555,7 @@ static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req,
 
 	BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
 
-	if (fc->kio.op && !fc->kio.op->req_send(fc, req, false, false))
+	if (fc->kio.op && !fc->kio.op->req_send(fc, req, ff, false, false))
 		return;
 
 	spin_lock(&fiq->waitq.lock);
@@ -646,7 +646,7 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)
 {
 	WARN_ON(!req->end);
 
-	if (fc->kio.op && !fc->kio.op->req_send(fc, req, true, false))
+	if (fc->kio.op && !fc->kio.op->req_send(fc, req, NULL, true, false))
 		return;
 
 	if (!fuse_request_queue_background(fc, req)) {
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 0ea0340c6b70..ab81131585d1 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -527,8 +527,8 @@ struct fuse_kio_ops {
 	/* Request handling hooks */
 	struct fuse_req *(*req_alloc)(struct fuse_conn *fc, unsigned nrpages,
 				      gfp_t flags);
-	int (*req_send)(struct fuse_conn *fc, struct fuse_req *req, bool bg,
-			bool locked);
+	int (*req_send)(struct fuse_conn *fc, struct fuse_req *req,
+			struct fuse_file *ff, bool bg, bool locked);
 
 	/* Inode scope hooks */
 	int  (*file_open)(struct fuse_conn *fc, struct file *file,
diff --git a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
index 3ae6e4e9385a..a4ab56a1fd88 100644
--- a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
+++ b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
@@ -911,7 +911,8 @@ static int pcs_fuse_prep_rw(struct pcs_fuse_req *r)
 	return ret;
 }
 
-static void pcs_fuse_submit(struct pcs_fuse_cluster *pfc, struct fuse_req *req, bool async, bool lk)
+static void pcs_fuse_submit(struct pcs_fuse_cluster *pfc, struct fuse_req *req,
+			    struct fuse_file *ff, bool async, bool lk)
 {
 	struct pcs_fuse_req *r = pcs_req_from_fuse(req);
 	struct fuse_inode *fi = get_fuse_inode(req->io_inode);
@@ -1004,7 +1005,7 @@ static void pcs_fuse_submit(struct pcs_fuse_cluster *pfc, struct fuse_req *req,
 
 submit:
 	spin_lock(&di->kq_lock);
-	if (req->ff && test_bit(FUSE_S_FAIL_IMMEDIATELY, &req->ff->ff_state)) {
+	if (ff && test_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state)) {
 		spin_unlock(&di->kq_lock);
 		req->out.h.error = -EIO;
 		goto error;
@@ -1078,7 +1079,7 @@ static void _pcs_shrink_end(struct fuse_conn *fc, struct fuse_req *req)
 
 		TRACE("resubmit %p\n", &r->req);
 		list_del_init(&ireq->list);
-		pcs_fuse_submit(pfc, &r->req, true, false);
+		pcs_fuse_submit(pfc, &r->req, NULL, true, false);
 	}
 }
 
@@ -1181,7 +1182,8 @@ static int pcs_kio_classify_req(struct fuse_conn *fc, struct fuse_req *req, bool
 	return -EINVAL;
 }
 
-static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req, bool bg, bool lk)
+static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req,
+			 struct fuse_file *ff, bool bg, bool lk)
 {
 	struct pcs_fuse_cluster *pfc = (struct pcs_fuse_cluster*)fc->kio.ctx;
 	int ret;
@@ -1237,7 +1239,7 @@ static int kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req, bool bg, bo
 	}
 	__clear_bit(FR_PENDING, &req->flags);
 
-	pcs_fuse_submit(pfc, req, lk, lk);
+	pcs_fuse_submit(pfc, req, ff ? : req->ff, lk, lk);
 	if (!bg)
 		wait_event(req->waitq,
 			   test_bit(FR_FINISHED, &req->flags) && !req->end);



More information about the Devel mailing list