Re: [PATCH v2] fuse: permit freezing while waiting for request answer

From: Miklos Szeredi

Date: Wed Aug 19 2026 - 06:06:31 EST


[Cc: fsdevel, Peter Z., Rafael]

On Wed, 19 Aug 2026 at 04:35, Sergey Senozhatsky
<senozhatsky@xxxxxxxxxxxx> wrote:
>
> Suspend freezes tasks in random order and doesn't take into
> consideration producer-consumer dependency that may exist
> between tasks. One example where this can cause issues is:
> fuse server getting frozen ahead of clients, which then get
> stuck waiting for req answers that never come (the server
> is already frozen).
>
> Make all wait-event calls in request_wait_answer() freezer-friendly.
>
> This, however, doesn't address all cases. E.g. in-place
> PM-freeze of a request_wait_answer() task holding a contended
> VFS lock still will block suspend.
>
> Note: this uses TASK_FREEZABLE, not TASK_FREEZABLE_UNSAFE,
> which may trigger debug_locks warning during suspend (if
> request_wait_answer() task holds some locks at the time
> of freeze.)

This is not okay. I see the "no new users" warning on
TASK_FREEZABLE_UNSAFE, but this needs further discussion.

Existing users of the _UNSAFE variant are NFS and samba. I haven't
checked the context where these are called.

Apparently __sb_start_write() uses the safe variant, yet I'm quite
sure it will be called in various locking contexts.

Why is this unsafe exactly? Does that unsafeness apply to
filesystems? If so why do we allow freezing while blocked on
sb_start_write()?

Thanks,
Miklos

>
> Signed-off-by: Sergey Senozhatsky <senozhatsky@xxxxxxxxxxxx>
> ---
>
> fs/fuse/dev.c | 13 ++++++++-----
> 1 file changed, 8 insertions(+), 5 deletions(-)
>
> diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
> index 34106f6e66a0..42098ddc2587 100644
> --- a/fs/fuse/dev.c
> +++ b/fs/fuse/dev.c
> @@ -701,8 +701,9 @@ static void request_wait_answer(struct fuse_req *req)
>
> if (!fch->no_interrupt) {
> /* Any signal may interrupt this */
> - err = wait_event_interruptible(req->waitq,
> - test_bit(FR_FINISHED, &req->flags));
> + err = wait_event_state(req->waitq,
> + test_bit(FR_FINISHED, &req->flags),
> + (TASK_INTERRUPTIBLE | TASK_FREEZABLE));
> if (!err)
> return;
>
> @@ -717,8 +718,9 @@ static void request_wait_answer(struct fuse_req *req)
> bool removed;
>
> /* Only fatal signals may interrupt this */
> - err = wait_event_killable(req->waitq,
> - test_bit(FR_FINISHED, &req->flags));
> + err = wait_event_state(req->waitq,
> + test_bit(FR_FINISHED, &req->flags),
> + (TASK_KILLABLE | TASK_FREEZABLE));
> if (!err)
> return;
>
> @@ -740,7 +742,8 @@ static void request_wait_answer(struct fuse_req *req)
> * Either request is already in userspace, or it was forced.
> * Wait it out.
> */
> - wait_event(req->waitq, test_bit(FR_FINISHED, &req->flags));
> + wait_event_state(req->waitq, test_bit(FR_FINISHED, &req->flags),
> + (TASK_UNINTERRUPTIBLE | TASK_FREEZABLE));
> }
>
> static void __fuse_request_send(struct fuse_req *req)
> --
> 2.55.0.737.g08866a6d13-goog
>