[RFC PATCH] freezer: Restrict unsafe freezable tasks to system sleep

From: Stanislav Kinsburskii

Date: Tue Jul 14 2026 - 12:41:44 EST


TASK_FREEZABLE_UNSAFE is used by network filesystems for sleeps that may
happen while a task holds locks or transport resources needed by other
tasks. Allowing the freezer to park such tasks for administrative freezer
requests can therefore block unrelated users of the same mount until the
task is thawed.

The original purpose of these unsafe freezable sleeps is to keep system
sleep from being blocked indefinitely by RPC or filesystem waits. Preserve
that behavior for suspend and hibernation, but do not freeze
TASK_FREEZABLE_UNSAFE tasks for non-PM freezer requests.

Make __TASK_FREEZABLE_UNSAFE independent of CONFIG_LOCKDEP so the freezer
can test the unsafe state in all builds. The lockdep check still uses the
same bit to suppress the locks-held warning for these known unsafe waits.

Document the PM-only freezer semantics for TASK_FREEZABLE_UNSAFE.

Signed-off-by: Stanislav Kinsburskii <skinsburskii@xxxxxxxxx>
---
Documentation/power/freezing-of-tasks.rst | 3 +++
include/linux/sched.h | 2 +-
kernel/freezer.c | 10 ++++++++++
3 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/Documentation/power/freezing-of-tasks.rst b/Documentation/power/freezing-of-tasks.rst
index df9755bfbd94..0ccf2ea041cf 100644
--- a/Documentation/power/freezing-of-tasks.rst
+++ b/Documentation/power/freezing-of-tasks.rst
@@ -16,6 +16,9 @@ II. How does it work?

There is one per-task flag (PF_NOFREEZE) and three per-task states
(TASK_FROZEN, TASK_FREEZABLE and __TASK_FREEZABLE_UNSAFE) used for that.
+Tasks sleeping in TASK_FREEZABLE_UNSAFE may hold locks or other resources
+that make them unsafe to freeze for administrative freezer requests, so the
+freezer only freezes them during system-wide suspend or hibernation.
The tasks that have PF_NOFREEZE unset (all user space tasks and some kernel
threads) are regarded as 'freezable' and treated in a special way before the
system enters a sleep state as well as before a hibernation image is created
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 908aff695ef8..67e052dee1c4 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -122,7 +122,7 @@ struct user_event_mm;
#define TASK_NEW 0x00000800
#define TASK_RTLOCK_WAIT 0x00001000
#define TASK_FREEZABLE 0x00002000
-#define __TASK_FREEZABLE_UNSAFE (0x00004000 * IS_ENABLED(CONFIG_LOCKDEP))
+#define __TASK_FREEZABLE_UNSAFE 0x00004000
#define TASK_FROZEN 0x00008000
#define TASK_STATE_MAX 0x00010000

diff --git a/kernel/freezer.c b/kernel/freezer.c
index a76bf957fb32..bf7bb15c144f 100644
--- a/kernel/freezer.c
+++ b/kernel/freezer.c
@@ -124,6 +124,16 @@ static int __set_task_frozen(struct task_struct *p, void *arg)
if (!(state & (TASK_FREEZABLE | __TASK_STOPPED | __TASK_TRACED)))
return 0;

+ /*
+ * TASK_FREEZABLE_UNSAFE waiters may hold locks or other resources that
+ * other tasks need in order to make forward progress. Only freeze
+ * them for system-wide suspend or hibernation, where failing to freeze
+ * them can prevent the sleep transition from making progress.
+ */
+ if ((state & __TASK_FREEZABLE_UNSAFE) &&
+ !pm_freezing && !pm_nosig_freezing)
+ return 0;
+
/*
* Only TASK_NORMAL can be augmented with TASK_FREEZABLE, since they
* can suffer spurious wakeups.