History

sched_ext: Close sub-sched init race with post-init DEAD recheck

scx_sub_enable_workfn()'s init pass and scx_sub_disable() migration both drop the rq lock to call __scx_init_task() against the other sched. A TASK_DEAD @p can fall through sched_ext_dead() in that window. sched_ext_dead() runs ops.exit_task() on the sched @p was attached to, not on the sched whose init just completed, so the new allocation leaks. Reuse the DEAD signal set by sched_ext_dead(). After __scx_init_task() returns, take task_rq_lock(p) and check for DEAD; on hit, call scx_sub_init_cancel_task() against the sub sched the init ran for and drop @p; on miss, proceed as before.

Tejun Heo committed 6 months ago

Commit cd6aab71 parent c941d731 file · +31 −1Browse files

Showing the changes to kernel (1 of 1 file). Show all

Unified · Split

kernel/sched/ext.c+31−1
@@ -5777,6 +5777,21 @@ static void scx_sub_disable(struct scx_sched *sch) · 5776 unmodified lines
5777 }5777 }
5778 5778
5779 rq = task_rq_lock(p, &rf);5779 rq = task_rq_lock(p, &rf);
5780
5781 if (scx_get_task_state(p) == SCX_TASK_DEAD) {
5782 /*
5783 * sched_ext_dead() raced us between __scx_init_task()
5784 * and this rq lock and ran exit_task() on @sch (the
5785 * sched @p was on at that point), not on $parent.
5786 * $parent's just-completed init is owed an exit_task()
5787 * and we issue it here.
5788 */
5789 scx_sub_init_cancel_task(parent, p);
5790 task_rq_unlock(rq, p, &rf);
5791 put_task_struct(p);
5792 continue;
5793 }
5794
5780 scoped_guard (sched_change, p, DEQUEUE_SAVE | DEQUEUE_MOVE) {5795 scoped_guard (sched_change, p, DEQUEUE_SAVE | DEQUEUE_MOVE) {
5781 /*5796 /*
5782 * $p is initialized for $parent and still attached to5797 * $p is initialized for $parent and still attached to
@@ -5791,8 +5806,8 @@ static void scx_sub_disable(struct scx_sched *sch) · 8 unmodified lines
5791 scx_set_task_state(p, SCX_TASK_READY);5806 scx_set_task_state(p, SCX_TASK_READY);
5792 scx_enable_task(parent, p);5807 scx_enable_task(parent, p);
5793 }5808 }
5794 task_rq_unlock(rq, p, &rf);
5795 5809
5810 task_rq_unlock(rq, p, &rf);
5796 put_task_struct(p);5811 put_task_struct(p);
5797 }5812 }
5798 scx_task_iter_stop(&sti);5813 scx_task_iter_stop(&sti);
@@ -7212,6 +7227,21 @@ static void scx_sub_enable_workfn(struct kthread_work *work) · 1413 unmodified lines
7212 goto abort;7227 goto abort;
7213 7228
7214 rq = task_rq_lock(p, &rf);7229 rq = task_rq_lock(p, &rf);
7230
7231 if (scx_get_task_state(p) == SCX_TASK_DEAD) {
7232 /*
7233 * sched_ext_dead() raced us between __scx_init_task()
7234 * and this rq lock and ran exit_task() on $parent (the
7235 * sched @p was on at that point), not on @sch. @sch's
7236 * just-completed init is owed an exit_task() and we
7237 * issue it here.
7238 */
7239 scx_sub_init_cancel_task(sch, p);
7240 task_rq_unlock(rq, p, &rf);
7241 put_task_struct(p);
7242 continue;
7243 }
7244
7215 p->scx.flags |= SCX_TASK_SUB_INIT;7245 p->scx.flags |= SCX_TASK_SUB_INIT;
7216 task_rq_unlock(rq, p, &rf);7246 task_rq_unlock(rq, p, &rf);
7217 7247