Skip to content

Commit dbf8932

Browse files
committed
Merge tag 'sched_ext-for-6.19-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext
Pull sched_ext fixes from Tejun Heo: - Fix memory leak when destroying helper kthread workers during scheduler disable - Fix bypass depth accounting on scx_enable() failure which could leave the system permanently in bypass mode - Fix missing preemption handling when moving tasks to local DSQs via scx_bpf_dsq_move() - Misc fixes including NULL check for put_prev_task(), flushing stdout in selftests, and removing unused code * tag 'sched_ext-for-6.19-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext: sched_ext: Remove unused code in the do_pick_task_scx() selftests/sched_ext: flush stdout before test to avoid log spam sched_ext: Fix missing post-enqueue handling in move_local_task_to_local_dsq() sched_ext: Factor out local_dsq_post_enq() from dispatch_enqueue() sched_ext: Fix bypass depth leak on scx_enable() failure sched/ext: Avoid null ptr traversal when ->put_prev_task() is called with NULL next sched_ext: Fix the memleak for sch->helper objects
2 parents 6b63f90 + bb27226 commit dbf8932

2 files changed

Lines changed: 56 additions & 24 deletions

File tree

kernel/sched/ext.c

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ static bool scx_init_task_enabled;
4141
static bool scx_switching_all;
4242
DEFINE_STATIC_KEY_FALSE(__scx_switched_all);
4343

44+
/*
45+
* Tracks whether scx_enable() called scx_bypass(true). Used to balance bypass
46+
* depth on enable failure. Will be removed when bypass depth is moved into the
47+
* sched instance.
48+
*/
49+
static bool scx_bypassed_for_enable;
50+
4451
static atomic_long_t scx_nr_rejected = ATOMIC_LONG_INIT(0);
4552
static atomic_long_t scx_hotplug_seq = ATOMIC_LONG_INIT(0);
4653

@@ -975,6 +982,30 @@ static void refill_task_slice_dfl(struct scx_sched *sch, struct task_struct *p)
975982
__scx_add_event(sch, SCX_EV_REFILL_SLICE_DFL, 1);
976983
}
977984

985+
static void local_dsq_post_enq(struct scx_dispatch_q *dsq, struct task_struct *p,
986+
u64 enq_flags)
987+
{
988+
struct rq *rq = container_of(dsq, struct rq, scx.local_dsq);
989+
bool preempt = false;
990+
991+
/*
992+
* If @rq is in balance, the CPU is already vacant and looking for the
993+
* next task to run. No need to preempt or trigger resched after moving
994+
* @p into its local DSQ.
995+
*/
996+
if (rq->scx.flags & SCX_RQ_IN_BALANCE)
997+
return;
998+
999+
if ((enq_flags & SCX_ENQ_PREEMPT) && p != rq->curr &&
1000+
rq->curr->sched_class == &ext_sched_class) {
1001+
rq->curr->scx.slice = 0;
1002+
preempt = true;
1003+
}
1004+
1005+
if (preempt || sched_class_above(&ext_sched_class, rq->curr->sched_class))
1006+
resched_curr(rq);
1007+
}
1008+
9781009
static void dispatch_enqueue(struct scx_sched *sch, struct scx_dispatch_q *dsq,
9791010
struct task_struct *p, u64 enq_flags)
9801011
{
@@ -1086,22 +1117,10 @@ static void dispatch_enqueue(struct scx_sched *sch, struct scx_dispatch_q *dsq,
10861117
if (enq_flags & SCX_ENQ_CLEAR_OPSS)
10871118
atomic_long_set_release(&p->scx.ops_state, SCX_OPSS_NONE);
10881119

1089-
if (is_local) {
1090-
struct rq *rq = container_of(dsq, struct rq, scx.local_dsq);
1091-
bool preempt = false;
1092-
1093-
if ((enq_flags & SCX_ENQ_PREEMPT) && p != rq->curr &&
1094-
rq->curr->sched_class == &ext_sched_class) {
1095-
rq->curr->scx.slice = 0;
1096-
preempt = true;
1097-
}
1098-
1099-
if (preempt || sched_class_above(&ext_sched_class,
1100-
rq->curr->sched_class))
1101-
resched_curr(rq);
1102-
} else {
1120+
if (is_local)
1121+
local_dsq_post_enq(dsq, p, enq_flags);
1122+
else
11031123
raw_spin_unlock(&dsq->lock);
1104-
}
11051124
}
11061125

11071126
static void task_unlink_from_dsq(struct task_struct *p,
@@ -1625,6 +1644,8 @@ static void move_local_task_to_local_dsq(struct task_struct *p, u64 enq_flags,
16251644

16261645
dsq_mod_nr(dst_dsq, 1);
16271646
p->scx.dsq = dst_dsq;
1647+
1648+
local_dsq_post_enq(dst_dsq, p, enq_flags);
16281649
}
16291650

16301651
/**
@@ -2402,7 +2423,7 @@ static void put_prev_task_scx(struct rq *rq, struct task_struct *p,
24022423
* ops.enqueue() that @p is the only one available for this cpu,
24032424
* which should trigger an explicit follow-up scheduling event.
24042425
*/
2405-
if (sched_class_above(&ext_sched_class, next->sched_class)) {
2426+
if (next && sched_class_above(&ext_sched_class, next->sched_class)) {
24062427
WARN_ON_ONCE(!(sch->ops.flags & SCX_OPS_ENQ_LAST));
24072428
do_enqueue_task(rq, p, SCX_ENQ_LAST, -1);
24082429
} else {
@@ -2425,7 +2446,7 @@ static struct task_struct *
24252446
do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
24262447
{
24272448
struct task_struct *prev = rq->curr;
2428-
bool keep_prev, kick_idle = false;
2449+
bool keep_prev;
24292450
struct task_struct *p;
24302451

24312452
/* see kick_cpus_irq_workfn() */
@@ -2467,12 +2488,8 @@ do_pick_task_scx(struct rq *rq, struct rq_flags *rf, bool force_scx)
24672488
refill_task_slice_dfl(rcu_dereference_sched(scx_root), p);
24682489
} else {
24692490
p = first_local_task(rq);
2470-
if (!p) {
2471-
if (kick_idle)
2472-
scx_kick_cpu(rcu_dereference_sched(scx_root),
2473-
cpu_of(rq), SCX_KICK_IDLE);
2491+
if (!p)
24742492
return NULL;
2475-
}
24762493

24772494
if (unlikely(!p->scx.slice)) {
24782495
struct scx_sched *sch = rcu_dereference_sched(scx_root);
@@ -3575,7 +3592,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
35753592
int node;
35763593

35773594
irq_work_sync(&sch->error_irq_work);
3578-
kthread_stop(sch->helper->task);
3595+
kthread_destroy_worker(sch->helper);
35793596

35803597
free_percpu(sch->pcpu);
35813598

@@ -4318,6 +4335,11 @@ static void scx_disable_workfn(struct kthread_work *work)
43184335
scx_dsp_max_batch = 0;
43194336
free_kick_syncs();
43204337

4338+
if (scx_bypassed_for_enable) {
4339+
scx_bypassed_for_enable = false;
4340+
scx_bypass(false);
4341+
}
4342+
43214343
mutex_unlock(&scx_enable_mutex);
43224344

43234345
WARN_ON_ONCE(scx_set_enable_state(SCX_DISABLED) != SCX_DISABLING);
@@ -4786,7 +4808,7 @@ static struct scx_sched *scx_alloc_and_add_sched(struct sched_ext_ops *ops)
47864808
return sch;
47874809

47884810
err_stop_helper:
4789-
kthread_stop(sch->helper->task);
4811+
kthread_destroy_worker(sch->helper);
47904812
err_free_pcpu:
47914813
free_percpu(sch->pcpu);
47924814
err_free_gdsqs:
@@ -4970,6 +4992,7 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)
49704992
* Init in bypass mode to guarantee forward progress.
49714993
*/
49724994
scx_bypass(true);
4995+
scx_bypassed_for_enable = true;
49734996

49744997
for (i = SCX_OPI_NORMAL_BEGIN; i < SCX_OPI_NORMAL_END; i++)
49754998
if (((void (**)(void))ops)[i])
@@ -5067,6 +5090,7 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)
50675090
scx_task_iter_stop(&sti);
50685091
percpu_up_write(&scx_fork_rwsem);
50695092

5093+
scx_bypassed_for_enable = false;
50705094
scx_bypass(false);
50715095

50725096
if (!scx_tryset_enable_state(SCX_ENABLED, SCX_ENABLING)) {

tools/testing/selftests/sched_ext/runner.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ static void print_test_preamble(const struct scx_test *test, bool quiet)
4646
if (!quiet)
4747
printf("DESCRIPTION: %s\n", test->description);
4848
printf("OUTPUT:\n");
49+
50+
/*
51+
* The tests may fork with the preamble buffered
52+
* in the children's stdout. Flush before the test
53+
* to avoid printing the message multiple times.
54+
*/
55+
fflush(stdout);
56+
fflush(stderr);
4957
}
5058

5159
static const char *status_to_result(enum scx_test_status status)

0 commit comments

Comments
 (0)