Hi Michal, On Fri, Aug 21, 2026 at 06:59:26PM +0200, Michal Koutn?? wrote: > Hi Salvatore, > > thanks for the nice report and sorry for not so prompt response. > > On Sat, Aug 15, 2026 at 09:41:14PM +0200, Salvatore Bonaccorso > <[email protected]> wrote: > > With an additional reproducer provided by Noah, I could bisect the > > change down to > > Good job. > > > > > commit 260fbcb92bbeacfcd050410fdc2d24ab15044400 > > Author: Tejun Heo <[email protected]> > > Date: Tue Oct 28 20:19:16 2025 -1000 > > > > cgroup: Move dying_tasks cleanup from cgroup_task_release() to > > cgroup_task_free() > > > > Currently, cgroup_task_exit() adds thread group leaders with live > > member > > threads to their css_set's dying_tasks list (so cgroup.procs > > iteration can > > still see the leader), and cgroup_task_release() later removes them > > with > > list_del_init(&task->cg_list). > > > > An upcoming patch will defer the dying_tasks list addition, moving > > it from > > cgroup_task_exit() (called from do_exit()) to a new function called > > from > > finish_task_switch(). However, release_task() (which calls > > cgroup_task_release()) can run either before or after > > finish_task_switch(), > > creating a race where cgroup_task_release() might try to remove the > > task from > > dying_tasks before or while it's being added. > > > > Move the list_del_init() from cgroup_task_release() to > > cgroup_task_free() to > > fix this race. cgroup_task_free() runs from __put_task_struct(), > > which is > > always after both paths, making the cleanup safe. > > > > Cc: Dan Schatzberg <[email protected]> > > Cc: Peter Zijlstra <[email protected]> > > Signed-off-by: Tejun Heo <[email protected]> > > > > But there was the suspect that the matching commit might be > > d245698d727a ("cgroup: Defer task cgroup unlink until after the task > > is done switching out"). > > I see that after 260fbcb92bbea ("cgroup: Move dying_tasks cleanup from > cgroup_task_release() to cgroup_task_free()") it may be possible that > tasks on the dying_tasks list may drop their ->usage to zero (since the > actual unlinking only happens in __put_task_struct). > Most often those would be skipped due to PF_EXITING except for the case > of thread group leaders (which the reproducer stresses) whose refcount > apparently can drop to zero after task->signal->live > 0 made them > iterable :-/ > > A band-aid fix could be to use tryget_task_struct() in > css_task_iter_next() (I got that hint from a LLM) and "skip" zeroed > tasks. I see that commit fbe3fb103596b ("sched_ext: Replace > tryget_task_struct() with get_task_struct()"), assumes the iterator > always succeeds in obtaining the task reference (which was the > justification of tryget removal). I expect that sched_ext should still > be fine if dying_tasks with zero references are skipped. (What are they? > Tasks which literally no one should be interested in and they're only > waiting for __put_task_struct_rcu_cb() to be called [*]). > > (I'm calling that band-aid because it'd resurrect usage of > tryget_task_struct() and it keeps the dying_tasks list a weird place to > be. If anyone has a better idea?) > > The commit d245698d727a ("cgroup: Defer task cgroup unlink until after > the task is done switching out") seems a reasonable separation of the > stages to me.
Thanks a lot for looking into this problem. Is there something Noah or I could test? Regards, Salvatore

