mirror of
https://github.com/torvalds/linux.git
synced 2026-07-29 02:31:27 +02:00
sched_ext: Fix deadlock between scx_root_disable() and concurrent forks
scx_root_disable() enters SCX_DISABLING before it grabs scx_enable_mutex to
clear __scx_switched_all and scx_switching_all. task_should_scx() short-circuits on DISABLING,
so forks in that window land on fair while next_active_class() still skips
fair - the new tasks stall.
This can deadlock the disable path itself: scx_alloc_and_add_sched() runs
under scx_enable_mutex and creates a helper kthread; if that new kthread is
one of the stalled fair tasks, the mutex holder waits forever and
scx_root_disable() can never make progress. Only sub-sched support exposes
this, since sub-sched enables are the only path where
scx_alloc_and_add_sched() can race the root's disable.
Move the DISABLING check after @scx_switching_all. @scx_switching_all
serves as a proxy for __scx_switched_all, so while it's set, forks keep
going to scx. Once cleared, DISABLING applies normally.
v2: Reword in-source comment and description. (Andrea)
Fixes: 337ec00b1d ("sched_ext: Implement cgroup sub-sched enabling and disabling")
Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Andrea Righi <arighi@nvidia.com>
This commit is contained in:
parent
6ae315d379
commit
515e3996a4
|
|
@ -4946,10 +4946,30 @@ static const struct kset_uevent_ops scx_uevent_ops = {
|
||||||
*/
|
*/
|
||||||
bool task_should_scx(int policy)
|
bool task_should_scx(int policy)
|
||||||
{
|
{
|
||||||
if (!scx_enabled() || unlikely(scx_enable_state() == SCX_DISABLING))
|
/* if disabled, nothing should be on it */
|
||||||
|
if (!scx_enabled())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* scx is taking over all SCHED_OTHER and SCHED_EXT tasks */
|
||||||
if (READ_ONCE(scx_switching_all))
|
if (READ_ONCE(scx_switching_all))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scx is tearing down - keep new SCHED_EXT tasks out.
|
||||||
|
*
|
||||||
|
* Must come after scx_switching_all test, which serves as a proxy
|
||||||
|
* for __scx_switched_all. While __scx_switched_all is set, we must
|
||||||
|
* return true via the branch above: a fork routed to fair would
|
||||||
|
* stall because next_active_class() skips fair.
|
||||||
|
*
|
||||||
|
* This can develop into a deadlock - scx holds scx_enable_mutex across
|
||||||
|
* kthread_create() in scx_alloc_and_add_sched(); if the new kthread is
|
||||||
|
* the stalled task, the disable path can never grab the mutex to clear
|
||||||
|
* scx_switching_all.
|
||||||
|
*/
|
||||||
|
if (unlikely(scx_enable_state() == SCX_DISABLING))
|
||||||
|
return false;
|
||||||
|
|
||||||
return policy == SCHED_EXT;
|
return policy == SCHED_EXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user