[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