xref: /linux/kernel/sched/ext/sub.h (revision 8946dbd3aa91acfb75b1633859290ba248e313b2)
1daf8e166STejun Heo /* SPDX-License-Identifier: GPL-2.0 */
2daf8e166STejun Heo /*
3daf8e166STejun Heo  * BPF extensible scheduler class: Documentation/scheduler/sched-ext.rst
4daf8e166STejun Heo  *
5daf8e166STejun Heo  * Sub-scheduler hierarchy support.
6daf8e166STejun Heo  *
7daf8e166STejun Heo  * Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
8daf8e166STejun Heo  * Copyright (c) 2026 Tejun Heo <tj@kernel.org>
9daf8e166STejun Heo  */
10daf8e166STejun Heo #ifndef _KERNEL_SCHED_EXT_SUB_H
11daf8e166STejun Heo #define _KERNEL_SCHED_EXT_SUB_H
12daf8e166STejun Heo 
13daf8e166STejun Heo #include "internal.h"
14daf8e166STejun Heo 
15daf8e166STejun Heo #ifdef CONFIG_EXT_SUB_SCHED
16daf8e166STejun Heo 
17bbda59d8STejun Heo struct scx_sched *scx_skip_subtree_pre(struct scx_sched *pos, struct scx_sched *root);
18daf8e166STejun Heo struct scx_sched *scx_next_descendant_pre(struct scx_sched *pos, struct scx_sched *root);
19daf8e166STejun Heo void scx_set_task_sched(struct task_struct *p, struct scx_sched *sch);
20daf8e166STejun Heo struct cgroup *sch_cgroup(struct scx_sched *sch);
21daf8e166STejun Heo void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch);
22daf8e166STejun Heo void scx_pstack_recursion_on_dispatch(struct bpf_prog *prog);
235f2a9a4cSTejun Heo void scx_pstack_recursion_on_caps_updated(struct bpf_prog *prog);
24daf8e166STejun Heo void drain_descendants(struct scx_sched *sch);
25daf8e166STejun Heo void scx_sub_disable(struct scx_sched *sch);
26daf8e166STejun Heo void scx_sub_enable_workfn(struct kthread_work *work);
27daf8e166STejun Heo bool scx_bpf_sub_dispatch(u64 cgroup_id, const struct bpf_prog_aux *aux);
288dba3bbdSTejun Heo void scx_free_pshards(struct scx_sched *sch);
298dba3bbdSTejun Heo s32 scx_alloc_pshards(struct scx_sched *sch);
3086094b95STejun Heo void scx_init_root_caps(struct scx_sched *sch);
31b81a6c01STejun Heo void scx_process_sync_ecaps(struct rq *rq, struct task_struct *prev);
3275c268edSTejun Heo void scx_unbypass_replay_ecaps(struct rq *rq, struct scx_sched *sch);
33b81a6c01STejun Heo void scx_online_ecaps(struct rq *rq);
34b81a6c01STejun Heo void scx_offline_ecaps(struct rq *rq);
3556fdc35bSTejun Heo void scx_discard_ecaps_to_sync(s32 cpu, struct scx_sched_pcpu *pcpu);
3656fdc35bSTejun Heo void scx_discard_stale_ecaps_syncs(void);
3775a8c820STejun Heo struct scx_dispatch_q *scx_local_or_reject_dsq(struct scx_sched *sch, struct rq *rq,
3875a8c820STejun Heo 					       struct task_struct *p, u64 *enq_flags);
3975a8c820STejun Heo bool scx_task_reenq_on_cap_revoke(struct rq *rq, struct task_struct *p);
4075a8c820STejun Heo void scx_reenq_reject(struct rq *rq);
41daf8e166STejun Heo 
42cbcda14bSPat Somaru static inline const char *sch_cgrp_path(struct scx_sched *sch)
43cbcda14bSPat Somaru {
44cbcda14bSPat Somaru 	return sch->cgrp_path;
45cbcda14bSPat Somaru }
46cbcda14bSPat Somaru 
47*8946dbd3STejun Heo /* a dying sub's hot-path influence ends in scx_sched_free_rcu_work() */
48*8946dbd3STejun Heo static inline void scx_dec_has_subs(struct scx_sched *sch)
49*8946dbd3STejun Heo {
50*8946dbd3STejun Heo 	if (sch->level)
51*8946dbd3STejun Heo 		static_branch_dec(&__scx_has_subs);
52*8946dbd3STejun Heo }
53*8946dbd3STejun Heo 
54daf8e166STejun Heo #else	/* CONFIG_EXT_SUB_SCHED */
55daf8e166STejun Heo 
56daf8e166STejun Heo static inline struct scx_sched *scx_next_descendant_pre(struct scx_sched *pos, struct scx_sched *root) { return pos ? NULL : root; }
57bbda59d8STejun Heo static inline struct scx_sched *scx_skip_subtree_pre(struct scx_sched *pos, struct scx_sched *root) { return NULL; }
58daf8e166STejun Heo static inline void scx_set_task_sched(struct task_struct *p, struct scx_sched *sch) {}
59daf8e166STejun Heo static inline struct cgroup *sch_cgroup(struct scx_sched *sch) { return NULL; }
60cbcda14bSPat Somaru static inline const char *sch_cgrp_path(struct scx_sched *sch) { return "/"; }
61daf8e166STejun Heo static inline void set_cgroup_sched(struct cgroup *cgrp, struct scx_sched *sch) {}
62daf8e166STejun Heo static inline void drain_descendants(struct scx_sched *sch) { }
63daf8e166STejun Heo static inline void scx_sub_disable(struct scx_sched *sch) { }
648dba3bbdSTejun Heo static inline void scx_free_pshards(struct scx_sched *sch) {}
658dba3bbdSTejun Heo static inline s32 scx_alloc_pshards(struct scx_sched *sch) { return 0; }
6686094b95STejun Heo static inline void scx_init_root_caps(struct scx_sched *sch) {}
67b81a6c01STejun Heo static inline void scx_process_sync_ecaps(struct rq *rq, struct task_struct *prev) {}
6875c268edSTejun Heo static inline void scx_unbypass_replay_ecaps(struct rq *rq, struct scx_sched *sch) {}
69b81a6c01STejun Heo static inline void scx_online_ecaps(struct rq *rq) {}
70b81a6c01STejun Heo static inline void scx_offline_ecaps(struct rq *rq) {}
7156fdc35bSTejun Heo static inline void scx_discard_ecaps_to_sync(s32 cpu, struct scx_sched_pcpu *pcpu) {}
7256fdc35bSTejun Heo static inline void scx_discard_stale_ecaps_syncs(void) {}
7375a8c820STejun Heo static inline struct scx_dispatch_q *scx_local_or_reject_dsq(struct scx_sched *sch, struct rq *rq, struct task_struct *p, u64 *enq_flags) { return &rq->scx.local_dsq; }
7475a8c820STejun Heo static inline bool scx_task_reenq_on_cap_revoke(struct rq *rq, struct task_struct *p) { return false; }
7575a8c820STejun Heo static inline void scx_reenq_reject(struct rq *rq) {}
76*8946dbd3STejun Heo static inline void scx_dec_has_subs(struct scx_sched *sch) {}
77daf8e166STejun Heo 
78daf8e166STejun Heo #endif	/* CONFIG_EXT_SUB_SCHED */
79daf8e166STejun Heo 
80daf8e166STejun Heo /**
81daf8e166STejun Heo  * scx_for_each_descendant_pre - pre-order walk of a sched's descendants
82daf8e166STejun Heo  * @pos: iteration cursor
83daf8e166STejun Heo  * @root: sched to walk the descendants of
84daf8e166STejun Heo  *
85daf8e166STejun Heo  * Walk @root's descendants. @root is included in the iteration and the first
8670f8b178STejun Heo  * node to be visited. Must be called with scx_enable_mutex, scx_sched_lock, or
8770f8b178STejun Heo  * RCU read lock.
88daf8e166STejun Heo  */
89daf8e166STejun Heo #define scx_for_each_descendant_pre(pos, root)					\
90daf8e166STejun Heo 	for ((pos) = scx_next_descendant_pre(NULL, (root)); (pos);		\
91daf8e166STejun Heo 	     (pos) = scx_next_descendant_pre((pos), (root)))
92daf8e166STejun Heo 
9356fdc35bSTejun Heo #ifdef CONFIG_EXT_SUB_SCHED
9456fdc35bSTejun Heo 
9575a8c820STejun Heo /**
9675a8c820STejun Heo  * scx_missing_caps - The caps in @needed that @sch lacks on @cpu
9775a8c820STejun Heo  * @sch: sched to test
9875a8c820STejun Heo  * @cpu: cpu to test on
9975a8c820STejun Heo  * @needed: bitmask of SCX_CAP_* values
10075a8c820STejun Heo  *
10175a8c820STejun Heo  * Return the caps in @needed that @sch lacks for @cpu, 0 if it holds them all.
10275a8c820STejun Heo  */
10375a8c820STejun Heo static inline u64 scx_missing_caps(struct scx_sched *sch, s32 cpu, u64 needed)
10475a8c820STejun Heo {
10575a8c820STejun Heo 	u64 ecaps;
10675a8c820STejun Heo 
107*8946dbd3STejun Heo 	/* no sub-scheds, no missing caps */
108*8946dbd3STejun Heo 	if (!scx_has_subs())
109*8946dbd3STejun Heo 		return 0;
110*8946dbd3STejun Heo 
11175a8c820STejun Heo 	/* root holds every cap on every cpu */
11275a8c820STejun Heo 	if (!sch->level)
11375a8c820STejun Heo 		return 0;
11475a8c820STejun Heo 
11575a8c820STejun Heo 	ecaps = READ_ONCE(per_cpu_ptr(sch->pcpu, cpu)->ecaps);
11675a8c820STejun Heo 
11775a8c820STejun Heo 	return needed & ~ecaps;
11875a8c820STejun Heo }
11975a8c820STejun Heo 
12075a8c820STejun Heo /*
12175a8c820STejun Heo  * Cap semantics: which caps an action requires, and which caps a cap implies.
12275a8c820STejun Heo  * Keep all such mappings collected here.
12375a8c820STejun Heo  */
12475a8c820STejun Heo 
12575a8c820STejun Heo /* map @enq_flags to the SCX_CAP_* bit required for the local-DSQ insert */
12675a8c820STejun Heo static inline u64 scx_caps_for_enq(u64 enq_flags)
12775a8c820STejun Heo {
1288b175234STejun Heo 	/* a restored task must be put into the local DSQ regardless of caps */
1298b175234STejun Heo 	if (enq_flags & SCX_ENQ_IGNORE_CAPS)
1308b175234STejun Heo 		return 0;
131701b7bcaSTejun Heo 	if (enq_flags & SCX_ENQ_IMMED)
132147d1885STejun Heo 		return SCX_CAP_ENQ_IMMED;
133701b7bcaSTejun Heo 	return SCX_CAP_ENQ;
13475a8c820STejun Heo }
13575a8c820STejun Heo 
13675a8c820STejun Heo /* map queued @p to the SCX_CAP_* bit required to stay on its local DSQ */
13775a8c820STejun Heo static inline u64 scx_caps_for_task(struct task_struct *p)
13875a8c820STejun Heo {
139701b7bcaSTejun Heo 	if (p->scx.flags & SCX_TASK_IMMED)
140147d1885STejun Heo 		return SCX_CAP_ENQ_IMMED;
141701b7bcaSTejun Heo 	return SCX_CAP_ENQ;
14275a8c820STejun Heo }
14375a8c820STejun Heo 
1446ea3be36STejun Heo /* the cap @sch needs to preempt @rq's current task, 0 if none */
1456ea3be36STejun Heo static inline u64 scx_caps_for_preempt(struct scx_sched *sch, struct rq *rq)
1466ea3be36STejun Heo {
1476ea3be36STejun Heo 	struct task_struct *curr = rq->curr;
1486ea3be36STejun Heo 
1496ea3be36STejun Heo 	/* a non-ext task can't be preempted by ext, own-subtree needs no cap */
1506ea3be36STejun Heo 	if (curr->sched_class != &ext_sched_class ||
1516ea3be36STejun Heo 	    scx_is_descendant(scx_task_sched(curr), sch))
1526ea3be36STejun Heo 		return 0;
1536ea3be36STejun Heo 	return SCX_CAP_PREEMPT;
1546ea3be36STejun Heo }
1556ea3be36STejun Heo 
15656fdc35bSTejun Heo /* caps implied by holding @cap */
15756fdc35bSTejun Heo static inline u64 scx_caps_implied(u64 cap)
15856fdc35bSTejun Heo {
159701b7bcaSTejun Heo 	switch (cap) {
1606ea3be36STejun Heo 	case SCX_CAP_PREEMPT:
1616ea3be36STejun Heo 		return SCX_CAP_ENQ | SCX_CAP_ENQ_IMMED;
162701b7bcaSTejun Heo 	case SCX_CAP_ENQ:
163701b7bcaSTejun Heo 		return SCX_CAP_ENQ_IMMED;
164701b7bcaSTejun Heo 	}
16556fdc35bSTejun Heo 	return 0;
16656fdc35bSTejun Heo }
16756fdc35bSTejun Heo 
16846a85ae6STejun Heo /* may @p keep running on @rq's cpu? requires baseline cpu access */
16946a85ae6STejun Heo static inline bool scx_task_can_stay_on_cpu(struct rq *rq, struct task_struct *p)
17046a85ae6STejun Heo {
171*8946dbd3STejun Heo 	if (!scx_has_subs())
172*8946dbd3STejun Heo 		return true;
173*8946dbd3STejun Heo 
17446a85ae6STejun Heo 	/* a migration-disabled task is let in without caps, keep it likewise */
17546a85ae6STejun Heo 	if (unlikely(is_migration_disabled(p)))
17646a85ae6STejun Heo 		return true;
17746a85ae6STejun Heo 
17846a85ae6STejun Heo 	return likely(!scx_missing_caps(scx_task_sched(p), cpu_of(rq), SCX_CAP_BASE));
17946a85ae6STejun Heo }
18046a85ae6STejun Heo 
18175a8c820STejun Heo #else	/* CONFIG_EXT_SUB_SCHED */
18275a8c820STejun Heo 
18375a8c820STejun Heo static inline u64 scx_missing_caps(struct scx_sched *sch, s32 cpu, u64 needed) { return 0; }
1846ea3be36STejun Heo static inline u64 scx_caps_for_preempt(struct scx_sched *sch, struct rq *rq) { return 0; }
18546a85ae6STejun Heo static inline bool scx_task_can_stay_on_cpu(struct rq *rq, struct task_struct *p) { return true; }
18675a8c820STejun Heo 
18756fdc35bSTejun Heo #endif	/* CONFIG_EXT_SUB_SCHED */
18856fdc35bSTejun Heo 
189daf8e166STejun Heo #endif /* _KERNEL_SCHED_EXT_SUB_H */
190