[PATCHSET cgroup/for-7.4] cgroup: Make the offline drain interruptible

From: Tejun Heo

Date: Wed Sep 02 2026 - 19:29:30 EST


Hello,

syzbot has been reporting "task hung in cgroup_subtree_control_write".

The underlying problem is an indefinite wait in a place where indefinite
waits should not happen. A task past exit_signals() can block in its exit
path for as long as, say, a FUSE daemon takes to answer a flush.

That wait is interruptible in name only. Once a thread group is exiting,
prepare_signal() drops every signal sent to it, so nothing can wake the
task. It still shows as TASK_INTERRUPTIBLE, and the hung task detector only
looks at TASK_UNINTERRUPTIBLE, so the stuck task never appears in any
report.

Since 1dffd95575eb ("cgroup: Defer kill_css_finish() in
cgroup_apply_control_disable()"), a disabled css offlines only after every
such task pinning it has exited, and the re-enable path waits for that
offline in TASK_UNINTERRUPTIBLE. The writer inherits the unkillable wait,
sits in D state, and is the only thing the hung task report shows.

This patchset makes the drain interruptible so that the writer can be
killed. The wait in the exit path remains a separate problem.

Tao Yu posted a patch that rejects the re-enable with -EBUSY while csses are
dying:

https://lore.kernel.org/all/20260901012342.855056-1-tao1.yu@xxxxxxxxx/

That fails re-enables that would have succeeded and keys off a counter that
also covers removed cgroups and offlined-but-pinned csses, so this takes the
interruptible wait instead.

0001-cgroup-Return-ENODEV-consistently-for-a-dead-cgroup.patch
0002-cgroup-Return-ERR_PTR-from-cgroup_kn_lock_live.patch
0003-cgroup-Make-the-offline-drain-interruptible.patch

0001 unifies the errno the interface file writers return for a removed
cgroup. 0002 makes cgroup_kn_lock_live() return ERR_PTR so that it can
report the interrupted drain. 0003 is the actual change.

This is against v7.3-rc1 (cee9395acd80) and also available in the following
git branch:

https://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git cgroup-drain-interruptible

diffstat follows.

kernel/cgroup/cgroup-internal.h | 2 +-
kernel/cgroup/cgroup-v1.c | 16 ++++---
kernel/cgroup/cgroup.c | 98 ++++++++++++++++++++++++-----------------
kernel/cgroup/debug.c | 10 ++---
4 files changed, 74 insertions(+), 52 deletions(-)

--
tejun