* [PATCH v2] fuse: permit freezing while waiting for request answer
@ 2026-08-19 2:35 Sergey Senozhatsky
2026-08-19 10:03 ` Miklos Szeredi
0 siblings, 1 reply; 15+ messages in thread
From: Sergey Senozhatsky @ 2026-08-19 2:35 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: fuse-devel, linux-kernel, Sergey Senozhatsky
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.)
Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
---
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
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-19 2:35 [PATCH v2] fuse: permit freezing while waiting for request answer Sergey Senozhatsky
@ 2026-08-19 10:03 ` Miklos Szeredi
2026-08-20 4:57 ` Sergey Senozhatsky
2026-08-20 8:49 ` Peter Zijlstra
0 siblings, 2 replies; 15+ messages in thread
From: Miklos Szeredi @ 2026-08-19 10:03 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: fuse-devel, linux-kernel, Rafael J. Wysocki, Peter Zijlstra,
linux-fsdevel
[Cc: fsdevel, Peter Z., Rafael]
On Wed, 19 Aug 2026 at 04:35, Sergey Senozhatsky
<senozhatsky@chromium.org> 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@chromium.org>
> ---
>
> 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
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-19 10:03 ` Miklos Szeredi
@ 2026-08-20 4:57 ` Sergey Senozhatsky
2026-08-20 8:49 ` Peter Zijlstra
1 sibling, 0 replies; 15+ messages in thread
From: Sergey Senozhatsky @ 2026-08-20 4:57 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Sergey Senozhatsky, fuse-devel, linux-kernel, Rafael J. Wysocki,
Peter Zijlstra, linux-fsdevel
On (26/08/19 12:03), Miklos Szeredi wrote:
> On Wed, 19 Aug 2026 at 04:35, Sergey Senozhatsky
> <senozhatsky@chromium.org> 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.
Sure. In RFC patch I had TASK_FREEZABLE_UNSAFE but eventually
"don't add new users" won.
> 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.
We also use "safe" variant in fuse_get_req().
> Why is this unsafe exactly? Does that unsafeness apply to
> filesystems? If so why do we allow freezing while blocked on
> sb_start_write()?
Right, I don't have much to add to the point that maybe file-systems
can get a waiver. If we suspend under un-contended VFS lock then it
doesn't look like unsafe here (we similarly can sleep indefinitely
under the same lock waiting for server reply); if the lock is contended
then suspend will fail.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-19 10:03 ` Miklos Szeredi
2026-08-20 4:57 ` Sergey Senozhatsky
@ 2026-08-20 8:49 ` Peter Zijlstra
2026-08-20 9:07 ` Sergey Senozhatsky
1 sibling, 1 reply; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-20 8:49 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Sergey Senozhatsky, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Wed, Aug 19, 2026 at 12:03:32PM +0200, Miklos Szeredi wrote:
> Why is this unsafe exactly? Does that unsafeness apply to
> filesystems? If so why do we allow freezing while blocked on
> sb_start_write()?
Getting frozen with lock A held, while another task is blocked on A in
an unfreezable state results in the system not being freezable.
IOW you deadlock the freeze.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-20 8:49 ` Peter Zijlstra
@ 2026-08-20 9:07 ` Sergey Senozhatsky
2026-08-20 9:10 ` Peter Zijlstra
0 siblings, 1 reply; 15+ messages in thread
From: Sergey Senozhatsky @ 2026-08-20 9:07 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Miklos Szeredi, Sergey Senozhatsky, fuse-devel, linux-kernel,
Rafael J. Wysocki, linux-fsdevel
On (26/08/20 10:49), Peter Zijlstra wrote:
> Message-ID: <20260820084933.GA4036497@noisy.programming.kicks-ass.net>
>
> On Wed, Aug 19, 2026 at 12:03:32PM +0200, Miklos Szeredi wrote:
> > Why is this unsafe exactly? Does that unsafeness apply to
> > filesystems? If so why do we allow freezing while blocked on
> > sb_start_write()?
>
> Getting frozen with lock A held, while another task is blocked on A in
> an unfreezable state results in the system not being freezable.
Right, but then the system says "suspend failed" (tasks refuse to freeze
after 20sec) and just thaws everything back in?
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-20 9:07 ` Sergey Senozhatsky
@ 2026-08-20 9:10 ` Peter Zijlstra
2026-08-20 9:24 ` Sergey Senozhatsky
0 siblings, 1 reply; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-20 9:10 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: Miklos Szeredi, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Thu, Aug 20, 2026 at 06:07:18PM +0900, Sergey Senozhatsky wrote:
> On (26/08/20 10:49), Peter Zijlstra wrote:
> > Message-ID: <20260820084933.GA4036497@noisy.programming.kicks-ass.net>
> >
> > On Wed, Aug 19, 2026 at 12:03:32PM +0200, Miklos Szeredi wrote:
> > > Why is this unsafe exactly? Does that unsafeness apply to
> > > filesystems? If so why do we allow freezing while blocked on
> > > sb_start_write()?
> >
> > Getting frozen with lock A held, while another task is blocked on A in
> > an unfreezable state results in the system not being freezable.
>
> Right, but then the system says "suspend failed" (tasks refuse to freeze
> after 20sec) and just thaws everything back in?
People don't like suspend failing. People like to close their lid, throw
laptop in bag, and expect laptop to not cook itself to death.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-20 9:10 ` Peter Zijlstra
@ 2026-08-20 9:24 ` Sergey Senozhatsky
2026-08-20 9:34 ` Peter Zijlstra
0 siblings, 1 reply; 15+ messages in thread
From: Sergey Senozhatsky @ 2026-08-20 9:24 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Sergey Senozhatsky, Miklos Szeredi, fuse-devel, linux-kernel,
Rafael J. Wysocki, linux-fsdevel
On (26/08/20 11:10), Peter Zijlstra wrote:
> On Thu, Aug 20, 2026 at 06:07:18PM +0900, Sergey Senozhatsky wrote:
> > On (26/08/20 10:49), Peter Zijlstra wrote:
> > > Message-ID: <20260820084933.GA4036497@noisy.programming.kicks-ass.net>
> > >
> > > On Wed, Aug 19, 2026 at 12:03:32PM +0200, Miklos Szeredi wrote:
> > > > Why is this unsafe exactly? Does that unsafeness apply to
> > > > filesystems? If so why do we allow freezing while blocked on
> > > > sb_start_write()?
> > >
> > > Getting frozen with lock A held, while another task is blocked on A in
> > > an unfreezable state results in the system not being freezable.
> >
> > Right, but then the system says "suspend failed" (tasks refuse to freeze
> > after 20sec) and just thaws everything back in?
>
> People don't like suspend failing. People like to close their lid, throw
> laptop in bag, and expect laptop to not cook itself to death.
Sure, that's exactly the problem I'm looking at. Throwing TASK_FREEZABLE
addresses some of the cases. Failing laptop suspend because of uncontended
VFS lock is not an uncommon scenario for us. Ideally, however, we need
some sort of server/client aware suspend, maybe moving clients to cgroup C
and server to cgroup S, and freezing those in strict order. Or teaching PM
that some tasks cannot be frozen in random order during suspend (e.g. a
special flag PM_FREEZE_ME_LAST).
fuse is not the only subsystem that doesn't fit current random order suspend.
Another troublemaker for us is notify, which basically has the same server/client
architecture (where both sides are user-space processes).
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-20 9:24 ` Sergey Senozhatsky
@ 2026-08-20 9:34 ` Peter Zijlstra
2026-08-20 9:47 ` Sergey Senozhatsky
0 siblings, 1 reply; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-20 9:34 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: Miklos Szeredi, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Thu, Aug 20, 2026 at 06:24:29PM +0900, Sergey Senozhatsky wrote:
> On (26/08/20 11:10), Peter Zijlstra wrote:
> > On Thu, Aug 20, 2026 at 06:07:18PM +0900, Sergey Senozhatsky wrote:
> > > On (26/08/20 10:49), Peter Zijlstra wrote:
> > > > Message-ID: <20260820084933.GA4036497@noisy.programming.kicks-ass.net>
> > > >
> > > > On Wed, Aug 19, 2026 at 12:03:32PM +0200, Miklos Szeredi wrote:
> > > > > Why is this unsafe exactly? Does that unsafeness apply to
> > > > > filesystems? If so why do we allow freezing while blocked on
> > > > > sb_start_write()?
> > > >
> > > > Getting frozen with lock A held, while another task is blocked on A in
> > > > an unfreezable state results in the system not being freezable.
> > >
> > > Right, but then the system says "suspend failed" (tasks refuse to freeze
> > > after 20sec) and just thaws everything back in?
> >
> > People don't like suspend failing. People like to close their lid, throw
> > laptop in bag, and expect laptop to not cook itself to death.
>
> Sure, that's exactly the problem I'm looking at. Throwing TASK_FREEZABLE
> addresses some of the cases. Failing laptop suspend because of uncontended
> VFS lock is not an uncommon scenario for us. Ideally, however, we need
> some sort of server/client aware suspend, maybe moving clients to cgroup C
> and server to cgroup S, and freezing those in strict order. Or teaching PM
> that some tasks cannot be frozen in random order during suspend (e.g. a
> special flag PM_FREEZE_ME_LAST).
>
> fuse is not the only subsystem that doesn't fit current random order suspend.
> Another troublemaker for us is notify, which basically has the same server/client
> architecture (where both sides are user-space processes).
Then propose patches creating freeze order.
PM_FREEZE_ME_LAST is going to be trouble I think, before long you'll
need PM_FREEZE_ME_REALLY_LAST or somesuch nonsense.
Using cgroups for this also doesn't sound right.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-20 9:34 ` Peter Zijlstra
@ 2026-08-20 9:47 ` Sergey Senozhatsky
2026-08-20 10:41 ` Peter Zijlstra
0 siblings, 1 reply; 15+ messages in thread
From: Sergey Senozhatsky @ 2026-08-20 9:47 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Sergey Senozhatsky, Miklos Szeredi, fuse-devel, linux-kernel,
Rafael J. Wysocki, linux-fsdevel
On (26/08/20 11:34), Peter Zijlstra wrote:
> On Thu, Aug 20, 2026 at 06:24:29PM +0900, Sergey Senozhatsky wrote:
> > On (26/08/20 11:10), Peter Zijlstra wrote:
> > > On Thu, Aug 20, 2026 at 06:07:18PM +0900, Sergey Senozhatsky wrote:
> > > > On (26/08/20 10:49), Peter Zijlstra wrote:
> > > > > Message-ID: <20260820084933.GA4036497@noisy.programming.kicks-ass.net>
> > > > >
> > > > > On Wed, Aug 19, 2026 at 12:03:32PM +0200, Miklos Szeredi wrote:
> > > > > > Why is this unsafe exactly? Does that unsafeness apply to
> > > > > > filesystems? If so why do we allow freezing while blocked on
> > > > > > sb_start_write()?
> > > > >
> > > > > Getting frozen with lock A held, while another task is blocked on A in
> > > > > an unfreezable state results in the system not being freezable.
> > > >
> > > > Right, but then the system says "suspend failed" (tasks refuse to freeze
> > > > after 20sec) and just thaws everything back in?
> > >
> > > People don't like suspend failing. People like to close their lid, throw
> > > laptop in bag, and expect laptop to not cook itself to death.
> >
> > Sure, that's exactly the problem I'm looking at. Throwing TASK_FREEZABLE
> > addresses some of the cases. Failing laptop suspend because of uncontended
> > VFS lock is not an uncommon scenario for us. Ideally, however, we need
> > some sort of server/client aware suspend, maybe moving clients to cgroup C
> > and server to cgroup S, and freezing those in strict order. Or teaching PM
> > that some tasks cannot be frozen in random order during suspend (e.g. a
> > special flag PM_FREEZE_ME_LAST).
> >
> > fuse is not the only subsystem that doesn't fit current random order suspend.
> > Another troublemaker for us is notify, which basically has the same server/client
> > architecture (where both sides are user-space processes).
>
> Then propose patches creating freeze order.
>
> PM_FREEZE_ME_LAST is going to be trouble I think, before long you'll
> need PM_FREEZE_ME_REALLY_LAST or somesuch nonsense.
We probably need more than one, yeah. This doesn't take into consideration
relations between tasks within a priority group. If client A holds a lock
and enters freezer, and client B sleeps on that lock, then this group
cannot be suspended. We need clients to reach some freezer checkpoint,
instead of doing in-place freezing.
> Using cgroups for this also doesn't sound right.
This begins sounding pessimistic.
cgroup-s sounded solid to me, because this moves all the clients to that
save freezer checkpoint when they don't hold any locks. The only
problem is that if a task never reaches "return from syscall" then
we fail suspend. (I'm only talking about user-space tasks here, in the
context of fuse or inotify.) Why don't cgroups sound right to you?
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-20 9:47 ` Sergey Senozhatsky
@ 2026-08-20 10:41 ` Peter Zijlstra
2026-08-20 16:11 ` Miklos Szeredi
0 siblings, 1 reply; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-20 10:41 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: Miklos Szeredi, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Thu, Aug 20, 2026 at 06:47:03PM +0900, Sergey Senozhatsky wrote:
> > PM_FREEZE_ME_LAST is going to be trouble I think, before long you'll
> > need PM_FREEZE_ME_REALLY_LAST or somesuch nonsense.
>
> We probably need more than one, yeah. This doesn't take into consideration
> relations between tasks within a priority group. If client A holds a lock
> and enters freezer, and client B sleeps on that lock, then this group
> cannot be suspended. We need clients to reach some freezer checkpoint,
> instead of doing in-place freezing.
>
> > Using cgroups for this also doesn't sound right.
>
> This begins sounding pessimistic.
>
> cgroup-s sounded solid to me, because this moves all the clients to that
> save freezer checkpoint when they don't hold any locks. The only
> problem is that if a task never reaches "return from syscall" then
> we fail suspend. (I'm only talking about user-space tasks here, in the
> context of fuse or inotify.) Why don't cgroups sound right to you?
The cgroup hierarchy is already a mess, and the more different things
you want to stuff in there, the worse it gets.
Also, I still have machines with CGROUP=n.
Also, you'd be putting suspend success in the hands of userspace, that
sounds like a mighty fail right there.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-20 10:41 ` Peter Zijlstra
@ 2026-08-20 16:11 ` Miklos Szeredi
2026-08-21 8:30 ` Peter Zijlstra
0 siblings, 1 reply; 15+ messages in thread
From: Miklos Szeredi @ 2026-08-20 16:11 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Sergey Senozhatsky, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Thu, 20 Aug 2026 at 12:41, Peter Zijlstra <peterz@infradead.org> wrote:
> Also, you'd be putting suspend success in the hands of userspace, that
> sounds like a mighty fail right there.
Freeze ordering is also a hard/impossible problem without the help of
userspace. One obvious heuristic is if task has a /dev/fuse fd open,
it is a server. This simple ordering between server and non-server
tasks fails if
- task X is a server of A, while also doing ops on B
- task X is server, blocked on task Y which is not a server
What about adding a flag to mutex/rwsem that allows making them
freezable at setup time? Then fuse fs could set that flag for its
locks (including VFS locks).
Thanks,
Miklos
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-20 16:11 ` Miklos Szeredi
@ 2026-08-21 8:30 ` Peter Zijlstra
2026-08-21 11:03 ` Peter Zijlstra
2026-08-21 14:33 ` Miklos Szeredi
0 siblings, 2 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-21 8:30 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Sergey Senozhatsky, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Thu, Aug 20, 2026 at 06:11:40PM +0200, Miklos Szeredi wrote:
> On Thu, 20 Aug 2026 at 12:41, Peter Zijlstra <peterz@infradead.org> wrote:
>
> > Also, you'd be putting suspend success in the hands of userspace, that
> > sounds like a mighty fail right there.
>
> Freeze ordering is also a hard/impossible problem without the help of
> userspace.
In case of fuse, perhaps.
> One obvious heuristic is if task has a /dev/fuse fd open,
> it is a server. This simple ordering between server and non-server
> tasks fails if
>
> - task X is a server of A, while also doing ops on B
>
> - task X is server, blocked on task Y which is not a server
>
> What about adding a flag to mutex/rwsem that allows making them
> freezable at setup time? Then fuse fs could set that flag for its
> locks (including VFS locks).
That would be adding conditionals to the mutex, something we all pay
for, always.
I was thinking it shouldn't be too hard to add a graph to the tasklist,
where each task has a list of other tasks that should be frozen before
it.
That way the task iteration in try_to_freeze_tasks() can be modified to
iterate this list (recursively; depth-first etc.) before it goes and
freezes the initial task.
Obviously adding a link to the graph needs to ensure the whole thing
stays free of cycles, but that is 'details' :-)
And while this is fairly straight forward to make work, I fear it is the
wrong shape for the problem, since the server is more of an 'after'
rather than a 'before' relation. You would have to add all clients to
the before of the server.
Another approach might be to have each task have a list of tasks that
needs to be done later, and have this relation be counted in the
destination task.
Then, on freeze, create a list of all tasks and start iteration, for
each task that has a non-zero count of prior tasks, move it to the tail.
This relies on freeze points being a location where a task has no
relations. Such that when a task is frozen, it has no dependencies.
Then, again assuming it was a non-cyclic graph, it will always finish
the freeze in an order that resolves the dependencies.
This might be a little more tricky to implement, but should be doable.
In both scenarios the tracking of the actual dependencies is of course
going to be key.
One way would be for each file op to be wrapped like 'link-$op-unlink'
such that the client (the task doing the file op) gets linked to the
server (the task responsible for satisfying the request) before it can
block, and unlinked once its done.
Link/unlink could be a simple as:
struct task_struct *client, *server;
link;
atomic_inc(&server->freezer_count);
client->freezer_link = server;
unlink:
atomic_dec(&client->freezer_link->freezer_count);
client->freezer_link = NULL;
And the freezer should assert: !task->freezer_link, to ensure you
cannot be frozen while still having a link out, since this would keep
the link target's count elevated and inhibit freezer forever more.
This also deals with the server being a client of yet another fuse
filesystem. Creating cycles in fuse mounts would already be a recipe
for disaster today, without all this, so I'm assuming this all just
'works'.
Does any of that make sense?
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-21 8:30 ` Peter Zijlstra
@ 2026-08-21 11:03 ` Peter Zijlstra
2026-08-21 14:33 ` Miklos Szeredi
1 sibling, 0 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-21 11:03 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Sergey Senozhatsky, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Fri, Aug 21, 2026 at 10:30:00AM +0200, Peter Zijlstra wrote:
> Another approach might be to have each task have a list of tasks that
> needs to be done later, and have this relation be counted in the
> destination task.
>
> Then, on freeze, create a list of all tasks and start iteration, for
> each task that has a non-zero count of prior tasks, move it to the tail.
> This relies on freeze points being a location where a task has no
> relations. Such that when a task is frozen, it has no dependencies.
>
> Then, again assuming it was a non-cyclic graph, it will always finish
> the freeze in an order that resolves the dependencies.
>
> This might be a little more tricky to implement, but should be doable.
>
> In both scenarios the tracking of the actual dependencies is of course
> going to be key.
>
> One way would be for each file op to be wrapped like 'link-$op-unlink'
> such that the client (the task doing the file op) gets linked to the
> server (the task responsible for satisfying the request) before it can
> block, and unlinked once its done.
>
> Link/unlink could be a simple as:
>
> struct task_struct *client, *server;
>
> link;
> atomic_inc(&server->freezer_count);
> client->freezer_link = server;
>
> unlink:
>
> atomic_dec(&client->freezer_link->freezer_count);
> client->freezer_link = NULL;
>
> And the freezer should assert: !task->freezer_link, to ensure you
> cannot be frozen while still having a link out, since this would keep
> the link target's count elevated and inhibit freezer forever more.
>
> This also deals with the server being a client of yet another fuse
> filesystem. Creating cycles in fuse mounts would already be a recipe
> for disaster today, without all this, so I'm assuming this all just
> 'works'.
Something like so; this is a very rough draft and only supports a single
dependent task -- if you need more (eg. threaded fuse server where there
isn't a convenient 1:1 relation) it needs a little more.
But I'm hoping the idea is clear.
---
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 193a4a4dcc27..5c0cf42b4827 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1670,6 +1670,12 @@ struct task_struct {
struct unwind_task_info unwind_info;
#endif
+#ifdef CONFIG_FREEZER
+ struct list_head freezer_node;
+ struct task_struct *freezer_link;
+ atomic_t freezer_count;
+#endif
+
/* CPU-specific state of this task: */
struct thread_struct thread;
@@ -1693,6 +1699,35 @@ static inline bool sched_proxy_exec(void)
}
#endif
+#ifdef CONFIG_FREEZER
+extern struct task_struct init_task;
+static inline void init_task_freezer_link(struct task_struct *p)
+{
+ list_add_tail(&p->freezer_node, &init_task.freezer_node);
+}
+static inline void init_task_freezer_unlink(struct task_struct *p)
+{
+ list_del(&p->freezer_node);
+}
+static inline void freezer_link(struct task_struct *server)
+{
+ WARN_ON_ONCE(current->freezer_link);
+ current->freezer_link = server;
+ atomic_inc(&server->freezer_count);
+}
+static inline void freezer_unlink(struct task_struct *server)
+{
+ WARN_ON_ONCE(current->freezer_link != server);
+ current->freezer_link = NULL;
+ atomic_dec(&server->freezer_count);
+}
+#else
+static inline void init_task_freezer_link(struct task_struct *p) { }
+static inline void init_task_freezer_unlink(struct task_struct *p) { }
+static inline void freezer_link(struct task_struct *server) { }
+static inline void freezer_unlink(struct task_struct *server) { }
+#endif
+
#define TASK_REPORT_IDLE (TASK_REPORT + 1)
#define TASK_REPORT_MAX (TASK_REPORT_IDLE << 1)
diff --git a/init/init_task.c b/init/init_task.c
index ba5c2523f7e0..643598217335 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -266,6 +266,9 @@ struct task_struct init_task __aligned(L1_CACHE_BYTES) = {
#ifdef CONFIG_SCHED_MM_CID
.mm_cid = { .cid = MM_CID_UNSET, },
#endif
+#ifdef CONFIG_FREEZER
+ .freezer_node = LIST_HEAD_INIT(init_task.freezer_node),
+#endif
};
EXPORT_SYMBOL(init_task);
diff --git a/kernel/exit.c b/kernel/exit.c
index e9f902be4ea6..22d9800656f1 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -291,6 +291,8 @@ void release_task(struct task_struct *p)
leader->exit_state = EXIT_DEAD;
}
+ init_task_freezer_unlink(p);
+
write_unlock_irq(&tasklist_lock);
/* @thread_pid can't go away until free_pids() below */
proc_flush_pid(thread_pid);
diff --git a/kernel/fork.c b/kernel/fork.c
index e645675dd727..86b4f30c9370 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2506,6 +2506,7 @@ __latent_entropy struct task_struct *copy_process(
task_set_no_new_privs(p);
init_task_pid_links(p);
+ init_task_freezer_link(p);
if (likely(p->pid)) {
ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);
diff --git a/kernel/freezer.c b/kernel/freezer.c
index a76bf957fb32..46838be512f1 100644
--- a/kernel/freezer.c
+++ b/kernel/freezer.c
@@ -135,8 +135,10 @@ static int __set_task_frozen(struct task_struct *p, void *arg)
/*
* It's dangerous to freeze with locks held; there be dragons there.
*/
- if (!(state & __TASK_FREEZABLE_UNSAFE))
+ if (!(state & __TASK_FREEZABLE_UNSAFE)) {
WARN_ON_ONCE(debug_locks && p->lockdep_depth);
+ WARN_ON_ONCE(p->freezer_link);
+ }
#endif
p->saved_state = p->__state;
diff --git a/kernel/power/process.c b/kernel/power/process.c
index dc0dfc349f22..454b02f00ec8 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -29,7 +29,7 @@ static int try_to_freeze_tasks(bool user_only)
{
const char *what = user_only ? "user space processes" :
"remaining freezable tasks";
- struct task_struct *g, *p;
+ struct task_struct *g, *p, *stop;
unsigned long end_time;
unsigned int todo;
bool wq_busy = false;
@@ -49,9 +49,52 @@ static int try_to_freeze_tasks(bool user_only)
while (true) {
todo = 0;
+ stop = NULL;
read_lock(&tasklist_lock);
- for_each_process_thread(g, p) {
- if (p == current || !freeze_task(p))
+ list_for_each_entry_safe(p, g, &init_task.freezer_node, freezer_node) {
+ /*
+ * The head of the list will be the already frozen
+ * tasks, skip those.
+ */
+ if (p == current || frozen(p))
+ continue;
+
+ /*
+ * If the task has dependencies; move it to the tail.
+ * This is safe under read-tasklist_lock, because that
+ * excludes clone/exit and is otherwise serialized by
+ * the freezer -- you can't have multiple freezer
+ * instances.
+ */
+ if (atomic_read(&p->freezer_count)) {
+ /*
+ * @stop tracks the 'first' task with
+ * dependencies that is moved to the tail; if
+ * we encounter it again while also having
+ * failed to freeze previous tasks, we must
+ * stop.
+ */
+ if (p == stop && todo)
+ break;
+
+ if (!stop)
+ stop = p;
+
+ list_move_tail(&p->freezer_node, &init_task.freezer_node);
+ continue;
+ }
+
+ /*
+ * The @stop task was previously observed to have
+ * non-zero freezer_count, however all its
+ * dependencies went away (got frozen) and it can be
+ * frozen now. Clear it as a stop marker such that a
+ * new marker can be picked.
+ */
+ if (p == stop)
+ stop = NULL;
+
+ if (!freeze_task(p))
continue;
todo++;
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-21 8:30 ` Peter Zijlstra
2026-08-21 11:03 ` Peter Zijlstra
@ 2026-08-21 14:33 ` Miklos Szeredi
2026-08-21 14:57 ` Peter Zijlstra
1 sibling, 1 reply; 15+ messages in thread
From: Miklos Szeredi @ 2026-08-21 14:33 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Sergey Senozhatsky, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Fri, 21 Aug 2026 at 10:30, Peter Zijlstra <peterz@infradead.org> wrote:
> That would be adding conditionals to the mutex, something we all pay
> for, always.
It's just one bit (could be stashed into LSB of ->first_waiter),
Condition is checked before going to sleep, i.e. adds one branch in
the slow path. Don't think this could be measurable.
> One way would be for each file op to be wrapped like 'link-$op-unlink'
> such that the client (the task doing the file op) gets linked to the
> server (the task responsible for satisfying the request) before it can
> block, and unlinked once its done.
Okay, how do you know which task or tasks are responsible for
satisfying the request? That's not a generally calculable
information.
sshfs is a simple example: the transport layer process is ssh, but it
is in no way involved in fuse transactions, there's just a pipe
between the two. How would the kernel know it has to link the ssh
process when the pipe is written to?
There are probably many such examples where this fails.
Thanks,
Miklos
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] fuse: permit freezing while waiting for request answer
2026-08-21 14:33 ` Miklos Szeredi
@ 2026-08-21 14:57 ` Peter Zijlstra
0 siblings, 0 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-21 14:57 UTC (permalink / raw)
To: Miklos Szeredi
Cc: Sergey Senozhatsky, fuse-devel, linux-kernel, Rafael J. Wysocki,
linux-fsdevel
On Fri, Aug 21, 2026 at 04:33:57PM +0200, Miklos Szeredi wrote:
> On Fri, 21 Aug 2026 at 10:30, Peter Zijlstra <peterz@infradead.org> wrote:
>
> > That would be adding conditionals to the mutex, something we all pay
> > for, always.
>
> It's just one bit (could be stashed into LSB of ->first_waiter),
> Condition is checked before going to sleep, i.e. adds one branch in
> the slow path. Don't think this could be measurable.
I suppose I'm not at all sure how it would differ from having
mutex_lock_freezable(), or how it would help in this situation.
> > One way would be for each file op to be wrapped like 'link-$op-unlink'
> > such that the client (the task doing the file op) gets linked to the
> > server (the task responsible for satisfying the request) before it can
> > block, and unlinked once its done.
>
> Okay, how do you know which task or tasks are responsible for
> satisfying the request? That's not a generally calculable
> information.
>
> sshfs is a simple example: the transport layer process is ssh, but it
> is in no way involved in fuse transactions, there's just a pipe
> between the two. How would the kernel know it has to link the ssh
> process when the pipe is written to?
>
> There are probably many such examples where this fails.
Well, it was 'your' (as in not me) suggestion to have a freeze priority,
which sorta implied you knew what tasks to apply it to :/
Anyway, I suppose that in this case the fuse server is responsible to
communicate this dependency to the kernel -- or the sshfs fuse server
should use libssh and embed the ssh client inside itself, instead of
using pipes, dunno.
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-08-21 15:19 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-19 2:35 [PATCH v2] fuse: permit freezing while waiting for request answer Sergey Senozhatsky
2026-08-19 10:03 ` Miklos Szeredi
2026-08-20 4:57 ` Sergey Senozhatsky
2026-08-20 8:49 ` Peter Zijlstra
2026-08-20 9:07 ` Sergey Senozhatsky
2026-08-20 9:10 ` Peter Zijlstra
2026-08-20 9:24 ` Sergey Senozhatsky
2026-08-20 9:34 ` Peter Zijlstra
2026-08-20 9:47 ` Sergey Senozhatsky
2026-08-20 10:41 ` Peter Zijlstra
2026-08-20 16:11 ` Miklos Szeredi
2026-08-21 8:30 ` Peter Zijlstra
2026-08-21 11:03 ` Peter Zijlstra
2026-08-21 14:33 ` Miklos Szeredi
2026-08-21 14:57 ` Peter Zijlstra
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.