History

sched_ext: Authorize remote-move inserts against the placing scheduler

scx_local_or_reject_dsq() authorizes a local-DSQ insert against the caps of the scheduler doing the insert. On the consume/dispatch paths that is the scheduler running balance_one(), passed down through scx_consume_dispatch_q() and move_local_task_to_local_dsq(), so the check is correct. The remote-move path loses it. move_remote_task_to_local_dsq() re-activates @p on the destination rq through enqueue_task_scx(), which reconstructs the scheduler from the task, i.e. @p's owner. When an ancestor places a descendant's task - e.g. draining a bypassed sub-scheduler - the owner is a sub-scheduler of the placer, so authorizing against the owner checks a narrower cap set and can spuriously reject a task the placer is entitled to run. Carry the placing scheduler across the activate_task() boundary the same way enq_flags already are, via a per-rq field set only for the duration of the re-activation, and have scx_local_or_reject_dsq() authorize against it. The placer's caps are a superset of the owner's, so this admits what the placer may run and keeps rejecting what it may not. v2: Document @sch in move_remote_task_to_local_dsq()'s kerneldoc. (Andrea)

Tejun Heo committed 4 months ago

Commit f2c9f511 parent 6ea3be33 files · +25 −15Browse files

Unified · Split

3 files
  1. kernel/sched/ext/ext.c +16 −12
  2. kernel/sched/ext/sub.c +7 −2
  3. kernel/sched/sched.h +2 −1
kernel/sched/ext/ext.c+16−12
@@ -2268,6 +2268,7 @@ static void move_local_task_to_local_dsq(struct scx_sched *sch, · 2267 unmodified lines
2268 2268
2269/**2269/**
2270 * move_remote_task_to_local_dsq - Move a task from a foreign rq to a local DSQ2270 * move_remote_task_to_local_dsq - Move a task from a foreign rq to a local DSQ
2271 * @sch: scheduler placing @p
2271 * @p: task to move2272 * @p: task to move
2272 * @enq_flags: %SCX_ENQ_*2273 * @enq_flags: %SCX_ENQ_*
2273 * @src_rq: rq to move the task from, locked on entry, released on return2274 * @src_rq: rq to move the task from, locked on entry, released on return
@@ -2275,7 +2276,8 @@ static void move_local_task_to_local_dsq(struct scx_sched *sch, · 1 unmodified line
2275 *2276 *
2276 * Move @p which is currently on @src_rq to @dst_rq's local DSQ.2277 * Move @p which is currently on @src_rq to @dst_rq's local DSQ.
2277 */2278 */
2278static void move_remote_task_to_local_dsq(struct task_struct *p, u64 enq_flags,2279static void move_remote_task_to_local_dsq(struct scx_sched *sch,
2280 struct task_struct *p, u64 enq_flags,
2279 struct rq *src_rq, struct rq *dst_rq)2281 struct rq *src_rq, struct rq *dst_rq)
2280{2282{
2281 lockdep_assert_rq_held(src_rq);2283 lockdep_assert_rq_held(src_rq);
@@ -2291,15 +2293,19 @@ static void move_remote_task_to_local_dsq(struct task_struct *p, u64 enq_flags, · 9 unmodified lines
2291 switch_rq_lock(src_rq, dst_rq);2293 switch_rq_lock(src_rq, dst_rq);
2292 2294
2293 /*2295 /*
2294 * We want to pass scx-specific enq_flags but activate_task() will2296 * activate_task() below truncates enq_flags to 32 bits and re-derives
2295 * truncate the upper 32 bit. As we own @rq, we can pass them through2297 * @p's owner, dropping our scx flags and the placing @sch. We own @rq,
2296 * @rq->scx.remote_activate_enq_flags instead.2298 * so stash both across the call. The enqueue reads them back, keeping
2299 * the scx flags and checking caps against the placer, not the owner.
2297 */2300 */
2298 WARN_ON_ONCE(!cpumask_test_cpu(cpu_of(dst_rq), p->cpus_ptr));2301 WARN_ON_ONCE(!cpumask_test_cpu(cpu_of(dst_rq), p->cpus_ptr));
2299 WARN_ON_ONCE(dst_rq->scx.remote_activate_enq_flags);2302 WARN_ON_ONCE(dst_rq->scx.remote_activate_enq_flags ||
2303 dst_rq->scx.remote_activate_sch);
2300 dst_rq->scx.remote_activate_enq_flags = enq_flags;2304 dst_rq->scx.remote_activate_enq_flags = enq_flags;
2305 dst_rq->scx.remote_activate_sch = sch;
2301 activate_task(dst_rq, p, 0);2306 activate_task(dst_rq, p, 0);
2302 dst_rq->scx.remote_activate_enq_flags = 0;2307 dst_rq->scx.remote_activate_enq_flags = 0;
2308 dst_rq->scx.remote_activate_sch = NULL;
2303}2309}
2304 2310
2305/*2311/*
@@ -2431,12 +2437,12 @@ static bool unlink_dsq_and_switch_rq_lock(struct task_struct *p, · 125 unmodified lines
2431 !WARN_ON_ONCE(src_rq != task_rq(p));2437 !WARN_ON_ONCE(src_rq != task_rq(p));
2432}2438}
2433 2439
2434static bool consume_remote_task(struct rq *this_rq,2440static bool consume_remote_task(struct scx_sched *sch, struct rq *this_rq,
2435 struct task_struct *p, u64 enq_flags,2441 struct task_struct *p, u64 enq_flags,
2436 struct scx_dispatch_q *dsq, struct rq *src_rq)2442 struct scx_dispatch_q *dsq, struct rq *src_rq)
2437{2443{
2438 if (unlink_dsq_and_switch_rq_lock(p, dsq, this_rq, src_rq)) {2444 if (unlink_dsq_and_switch_rq_lock(p, dsq, this_rq, src_rq)) {
2439 move_remote_task_to_local_dsq(p, enq_flags, src_rq, this_rq);2445 move_remote_task_to_local_dsq(sch, p, enq_flags, src_rq, this_rq);
2440 return true;2446 return true;
2441 } else {2447 } else {
2442 switch_rq_lock(src_rq, this_rq);2448 switch_rq_lock(src_rq, this_rq);
@@ -2497,8 +2503,7 @@ static struct rq *move_task_between_dsqs(struct scx_sched *sch, · 54 unmodified lines
2497 raw_spin_unlock(&src_dsq->lock);2503 raw_spin_unlock(&src_dsq->lock);
2498 } else {2504 } else {
2499 raw_spin_unlock(&src_dsq->lock);2505 raw_spin_unlock(&src_dsq->lock);
2500 move_remote_task_to_local_dsq(p, enq_flags,2506 move_remote_task_to_local_dsq(sch, p, enq_flags, src_rq, dst_rq);
2501 src_rq, dst_rq);
2502 }2507 }
2503 } else {2508 } else {
2504 /*2509 /*
@@ -2551,7 +2556,7 @@ retry: · 46 unmodified lines
2551 }2556 }
2552 2557
2553 if (task_can_run_on_remote_rq(sch, p, rq, false)) {2558 if (task_can_run_on_remote_rq(sch, p, rq, false)) {
2554 if (likely(consume_remote_task(rq, p, enq_flags, dsq, task_rq)))2559 if (likely(consume_remote_task(sch, rq, p, enq_flags, dsq, task_rq)))
2555 return true;2560 return true;
2556 goto retry;2561 goto retry;
2557 }2562 }
@@ -2644,8 +2649,7 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq, · 86 unmodified lines
2644 scx_dispatch_enqueue(sch, src_rq, find_global_dsq(sch, task_cpu(p)),2649 scx_dispatch_enqueue(sch, src_rq, find_global_dsq(sch, task_cpu(p)),
2645 p, enq_flags | SCX_ENQ_GDSQ_FALLBACK);2650 p, enq_flags | SCX_ENQ_GDSQ_FALLBACK);
2646 } else {2651 } else {
2647 move_remote_task_to_local_dsq(p, enq_flags,2652 move_remote_task_to_local_dsq(sch, p, enq_flags, src_rq, dst_rq);
2648 src_rq, dst_rq);
2649 /* task has been moved to dst_rq, which is now locked */2653 /* task has been moved to dst_rq, which is now locked */
2650 locked_rq = dst_rq;2654 locked_rq = dst_rq;
2651 }2655 }
kernel/sched/ext/sub.c+7−2
@@ -237,12 +237,17 @@ struct scx_dispatch_q *scx_local_or_reject_dsq(struct scx_sched *sch, struct rq · 236 unmodified lines
237 struct task_struct *p, u64 *enq_flags)237 struct task_struct *p, u64 *enq_flags)
238{238{
239 s32 cid = __scx_cpu_to_cid(cpu_of(rq));239 s32 cid = __scx_cpu_to_cid(cpu_of(rq));
240 struct scx_sched *asch = rq->scx.remote_activate_sch ?: sch;
240 u64 needed = scx_caps_for_enq(*enq_flags);241 u64 needed = scx_caps_for_enq(*enq_flags);
241 u64 missing;242 u64 missing;
242 243
244 /*
245 * On a remote activation the scheduling sched (@asch) differs from
246 * @p's owner (@sch). Check caps against the scheduling sched.
247 */
243 if (*enq_flags & SCX_ENQ_PREEMPT)248 if (*enq_flags & SCX_ENQ_PREEMPT)
244 needed |= scx_caps_for_preempt(sch, rq);249 needed |= scx_caps_for_preempt(asch, rq);
245 missing = scx_missing_caps(sch, cpu_of(rq), needed);250 missing = scx_missing_caps(asch, cpu_of(rq), needed);
246 251
247 /* requirements met */252 /* requirements met */
248 if (likely(!missing))253 if (likely(!missing))
kernel/sched/sched.h+2−1
@@ -800,8 +800,9 @@ struct scx_rq { · 799 unmodified lines
800 struct list_head runnable_list; /* runnable tasks on this rq */800 struct list_head runnable_list; /* runnable tasks on this rq */
801 struct list_head ddsp_deferred_locals; /* deferred ddsps from enq */801 struct list_head ddsp_deferred_locals; /* deferred ddsps from enq */
802 unsigned long ops_qseq;802 unsigned long ops_qseq;
803 /* see move_remote_task_to_local_dsq() */803 /* both stashed across the activate_task() in move_remote_task_to_local_dsq() */
804 u64 remote_activate_enq_flags;804 u64 remote_activate_enq_flags;
805 struct scx_sched *remote_activate_sch;
805 u32 nr_running;806 u32 nr_running;
806 u32 cpuperf_target; /* [0, SCHED_CAPACITY_SCALE] */807 u32 cpuperf_target; /* [0, SCHED_CAPACITY_SCALE] */
807 bool in_select_cpu;808 bool in_select_cpu;