[Devel,vz7,24/46] fuse: allow interrupt queuing without fc->lock

Submitted by Maxim Patlasov on March 25, 2017, 2:22 a.m.

Details

Message ID 149040852359.25341.1781680287021533311.stgit@maxim-thinkpad
State New
Series "fuse: add multi-threading support"
Headers show

Commit Message

Maxim Patlasov March 25, 2017, 2:22 a.m.
Backport from ml:

commit 8f7bb368dbdda76f5e98e05ee49ae2dc138fd42f
Author: Miklos Szeredi <mszeredi@suse.cz>
Date:   Wed Jul 1 16:26:03 2015 +0200

    fuse: allow interrupt queuing without fc->lock

    Interrupt is only queued after the request has been sent to userspace.
    This is either done in request_wait_answer() or fuse_dev_do_read()
    depending on which state the request is in at the time of the interrupt.
    If it's not yet sent, then queuing the interrupt is postponed until the
    request is read.  Otherwise (the request has already been read and is
    waiting for an answer) the interrupt is queued immedidately.

    We want to call queue_interrupt() without fc->lock protection, in which
    case there can be a race between the two functions:

     - neither of them queue the interrupt (thinking the other one has already
       done it).

     - both of them queue the interrupt

    The first one is prevented by adding memory barriers, the second is
    prevented by checking (under fiq->waitq.lock) if the interrupt has already
    been queued.

    Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>

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

Patch hide | download patch | download mbox

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index de28d86..5a65afa 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -406,8 +406,10 @@  __releases(fc->lock)
 static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
 {
 	spin_lock(&fiq->waitq.lock);
-	list_add_tail(&req->intr_entry, &fiq->interrupts);
-	wake_up_locked(&fiq->waitq);
+	if (list_empty(&req->intr_entry)) {
+		list_add_tail(&req->intr_entry, &fiq->interrupts);
+		wake_up_locked(&fiq->waitq);
+	}
 	spin_unlock(&fiq->waitq.lock);
 	kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
 }
@@ -426,6 +428,8 @@  static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
 
 		spin_lock(&fc->lock);
 		set_bit(FR_INTERRUPTED, &req->flags);
+		/* matches barrier in fuse_dev_do_read() */
+		smp_mb__after_atomic();
 		if (test_bit(FR_SENT, &req->flags))
 			queue_interrupt(fiq, req);
 		spin_unlock(&fc->lock);
@@ -1323,8 +1327,10 @@  static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,
 	if (!test_bit(FR_ISREPLY, &req->flags)) {
 		request_end(fc, req);
 	} else {
-		set_bit(FR_SENT, &req->flags);
 		list_move_tail(&req->list, &fc->processing);
+		set_bit(FR_SENT, &req->flags);
+		/* matches barrier in request_wait_answer() */
+		smp_mb__after_atomic();
 		if (test_bit(FR_INTERRUPTED, &req->flags))
 			queue_interrupt(fiq, req);
 		spin_unlock(&fc->lock);