[PATCH] fuse: serialize resend state transitions with fiq->lock

Runli <[email protected]>
Newsgroups dev.linux.lists.fuse-devel
Message-ID <[email protected]>
fuse_chan_resend() moves processing requests to a private to_queue, then
sets FR_PENDING before taking fiq->lock. A fatal signal can consequently
remove and free a request while it is still being prepared for resend:

  CPU 0 (request holder)          CPU 1 (resend)
  ----------------------          ----------------
  request_wait_answer()
                                  req moved to private to_queue
                                  set_bit(FR_PENDING)
  fatal signal
  fuse_remove_pending_req()
    list_del(&req->list)
    __fuse_put_request(req)
  fuse_put_request(req)
    free req
                                  clear_bit(FR_SENT)
                                  update req->in.h.unique

Hold fiq->lock while updating the requests and moving them to
fiq->pending, serializing the transition with fuse_remove_pending_req().

Connected requests retain the same flags, unique IDs, and queue order. On
disconnection, fuse_dev_end_requests() still ends every request, while
requests that were never requeued are no longer marked pending or resent.

This also combines the two linear list walks into one, reducing the total
iteration count from 2n to n. The fiq->lock critical section remains O(n).

Fixes: 760eac73f9f6 ("fuse: Introduce a new notification type for resend pending requests")
Signed-off-by: Runli <[email protected]>
---
 fs/fuse/dev.c | 24 ++++++++++--------------
 1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 5763a7cd3b37..fc1f5b9f100c 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1781,26 +1781,22 @@ void fuse_chan_resend(struct fuse_chan *fch)
 	}
 	spin_unlock(&fch->lock);
 
-	list_for_each_entry_safe(req, next, &to_queue, list) {
-		set_bit(FR_PENDING, &req->flags);
-		clear_bit(FR_SENT, &req->flags);
-		/* mark the request as resend request */
-		req->in.h.unique |= FUSE_UNIQUE_RESEND;
-	}
-
 	spin_lock(&fiq->lock);
 	if (!fiq->connected) {
 		spin_unlock(&fiq->lock);
-		list_for_each_entry(req, &to_queue, list)
-			clear_bit(FR_PENDING, &req->flags);
 		fuse_dev_end_requests(&to_queue);
 		return;
 	}
-	/*
-	 * Remove interrupt entries for resent requests to prevent stale
-	 * intr_entry on fiq->interrupts after the request is re-queued.
-	 */
-	list_for_each_entry(req, &to_queue, list) {
+
+	list_for_each_entry_safe(req, next, &to_queue, list) {
+		set_bit(FR_PENDING, &req->flags);
+		clear_bit(FR_SENT, &req->flags);
+		/* mark the request as resend request */
+		req->in.h.unique |= FUSE_UNIQUE_RESEND;
+		/*
+		 * Remove interrupt entries for resent requests to prevent stale
+		 * intr_entry on fiq->interrupts after the request is re-queued.
+		 */
 		if (test_bit(FR_INTERRUPTED, &req->flags))
 			list_del_init(&req->intr_entry);
 	}
-- 
2.47.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.