From c56cc864979813359be29c77a497d0d2e25a2e82 Mon Sep 17 00:00:00 2001 From: Bernd Schubert Date: Mon, 9 Mar 2026 21:39:09 +0100 Subject: [PATCH] fuse: Remove unlock_request/lock_request This is a DDN patch only, as unlock_request()/lock_request() solve a deadlock issue for specially designed file systems, see Documentation/filesystems/fuse.rst, in the section **Scenario 2 - Tricky deadlock** This one needs a carefully crafted filesystem. It's a variation on the above, only the call back to the filesystem is not explicit, but is caused by a pagefault. :: | Kamikaze filesystem thread 1 | Kamikaze filesystem thread 2 In redfsd we do our best to not cause any kind of user issues and just want to be as fast as possible. Hence, we do not need the per page unlock/lock checks. Given that fuse is a generic file system, this can be a DDN commit only for now, until we find a better generic solution. The unlock_request/lock_request functions have been replaced by check_req_aborted(), which is run once per copied argument. (cherry picked from commit dc7fa1cd35a0cf0caaca30c35ef09714f5fd3646) --- fs/fuse/dev.c | 45 +++++---------------------------------------- 1 file changed, 5 insertions(+), 40 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index b51decf401816e..d1d0b50806f808 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -669,38 +669,14 @@ static int fuse_simple_notify_reply(struct fuse_mount *fm, return 0; } -/* - * Lock the request. Up to the next unlock_request() there mustn't be - * anything that could cause a page-fault. If the request was already - * aborted bail out. - */ -static int lock_request(struct fuse_req *req) -{ - int err = 0; - if (req) { - spin_lock(&req->waitq.lock); - if (test_bit(FR_ABORTED, &req->flags)) - err = -ENOENT; - else - set_bit(FR_LOCKED, &req->flags); - spin_unlock(&req->waitq.lock); - } - return err; -} -/* - * Unlock request. If it was aborted while locked, caller is responsible - * for unlocking and ending the request. - */ -static int unlock_request(struct fuse_req *req) +static int check_req_aborted(struct fuse_req *req) { int err = 0; - if (req) { + if (req && test_bit(FR_ABORTED, &req->flags)) { spin_lock(&req->waitq.lock); if (test_bit(FR_ABORTED, &req->flags)) err = -ENOENT; - else - clear_bit(FR_LOCKED, &req->flags); spin_unlock(&req->waitq.lock); } return err; @@ -742,7 +718,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) struct page *page; int err; - err = unlock_request(cs->req); + err = check_req_aborted(cs->req); if (err) return err; @@ -801,7 +777,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) cs->pg = page; } - return lock_request(cs->req); + return 0; } /* Do as much copy to/from userspace buffer as we can */ @@ -856,9 +832,6 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) struct pipe_buffer *buf = cs->pipebufs; folio_get(oldfolio); - err = unlock_request(cs->req); - if (err) - goto out_put_old; fuse_copy_finish(cs); @@ -944,9 +917,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) cs->pg = buf->page; cs->offset = buf->offset; - err = lock_request(cs->req); - if (!err) - err = 1; + err = 1; goto out_put_old; } @@ -955,17 +926,11 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page, unsigned offset, unsigned count) { struct pipe_buffer *buf; - int err; if (cs->nr_segs >= cs->pipe->max_usage) return -EIO; get_page(page); - err = unlock_request(cs->req); - if (err) { - put_page(page); - return err; - } fuse_copy_finish(cs);