xref: /linux/kernel/sched/ext/cid.h (revision 12da4723b679374ed914dc79f1c33faae212811a)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Topological CPU IDs (cids)
4  * --------------------------
5  *
6  * Raw cpu numbers are clumsy for sharding work and communication across
7  * topology units, especially from BPF: the space can be sparse, numerical
8  * closeness doesn't imply topological closeness (x86 hyperthreading often puts
9  * SMT siblings far apart), and a range of cpu ids doesn't mean anything.
10  * Sub-scheds make this acute - cpu allocation, revocation and other state are
11  * constantly communicated across sub-scheds, and passing whole cpumasks scales
12  * poorly with cpu count. cpumasks are also awkward in BPF: a variable-length
13  * kernel type sized for the maximum NR_CPUS (4k), with verbose helper sequences
14  * for every op.
15  *
16  * cids give every cpu a dense, topology-ordered id. CPUs sharing a core, LLC or
17  * NUMA node get contiguous cid ranges, so a topology unit becomes a (start,
18  * length) slice of cid space. Communication can pass a slice instead of a
19  * cpumask, and BPF code can process, for example, a u64 word's worth of cids at
20  * a time.
21  *
22  * The mapping is built once at root scheduler enable time by walking the
23  * topology of online cpus only. Going by online cpus is out of necessity:
24  * depending on the arch, topology info isn't reliably available for offline
25  * cpus. The expected usage model is restarting the scheduler on hotplug events
26  * so the mapping is rebuilt against the new online set. A scheduler that wants
27  * to handle hotplug without a restart can provide its own cid and shard mapping
28  * through the override interface.
29  *
30  * Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
31  * Copyright (c) 2026 Tejun Heo <tj@kernel.org>
32  */
33 #ifndef _KERNEL_SCHED_EXT_CID_H
34 #define _KERNEL_SCHED_EXT_CID_H
35 
36 #include "internal.h"
37 
38 struct scx_sched;
39 
40 /*
41  * Cid space (total is always num_possible_cpus()) is laid out with
42  * topology-annotated cids first, then no-topo cids at the tail. The
43  * topology-annotated block covers the cpus that were online when scx_cid_init()
44  * ran and remains valid even after those cpus go offline. The tail block covers
45  * possible-but-not-online cpus and carries all-(-1) topo info (see
46  * scx_cid_topo); callers detect it via the -1 sentinels.
47  *
48  * See the comment above the table definitions in cid.c for the
49  * memory-ordering and visibility contract.
50  */
51 struct scx_cid_tables {
52 	u32			nr_shards;
53 	s16			*cid_to_cpu;	/* [num_possible_cpus()] */
54 	s16			*cpu_to_cid;	/* [nr_cpu_ids] */
55 	s32			*cid_to_shard;	/* [num_possible_cpus()] */
56 	s32			*shard_node;	/* [num_possible_cpus()] */
57 	struct scx_cid_shard	*shard_ranges;	/* [num_possible_cpus()] */
58 	struct scx_cid_topo	*topo;		/* [num_possible_cpus()] */
59 	struct rcu_head		rcu;
60 };
61 
62 extern u32 scx_nr_cid_shards;
63 extern s16 __rcu *scx_cid_to_cpu_tbl;
64 extern s16 __rcu *scx_cpu_to_cid_tbl;
65 extern s32 __rcu *scx_cid_to_shard;
66 extern s32 __rcu *scx_shard_node;
67 extern struct scx_cid_shard __rcu *scx_cid_shard_ranges;
68 extern struct scx_cid_topo __rcu *scx_cid_topo;
69 extern struct btf_id_set8 scx_kfunc_ids_init_cids;
70 
71 void scx_cmask_clear(struct scx_cmask *m);
72 void scx_cmask_fill(struct scx_cmask *m);
73 void scx_cmask_and(struct scx_cmask *dst, const struct scx_cmask *src);
74 void scx_cmask_or(struct scx_cmask *dst, const struct scx_cmask *src);
75 void scx_cmask_copy(struct scx_cmask *dst, const struct scx_cmask *src);
76 void scx_cmask_andnot(struct scx_cmask *dst, const struct scx_cmask *src);
77 bool scx_cmask_subset(const struct scx_cmask *sub, const struct scx_cmask *super);
78 bool scx_cmask_intersects(const struct scx_cmask *a, const struct scx_cmask *b);
79 bool scx_cmask_empty(const struct scx_cmask *m);
80 s32 scx_cid_init(struct scx_sched *sch);
81 void scx_cid_publish_tables(void);
82 void scx_cid_retire_tables(void);
83 int scx_cid_kfunc_init(void);
84 
85 /**
86  * cid_valid - Verify a cid value, to be used on ops input args
87  * @sch: scx_sched to abort on error
88  * @cid: cid which came from a BPF ops
89  *
90  * Return true if @cid is in [0, num_possible_cpus()). On failure, trigger
91  * scx_error() and return false.
92  */
93 static inline bool cid_valid(struct scx_sched *sch, s32 cid)
94 {
95 	if (likely(cid >= 0 && cid < num_possible_cpus()))
96 		return true;
97 	scx_error(sch, "invalid cid %d", cid);
98 	return false;
99 }
100 
101 /**
102  * __scx_cid_to_cpu - Unchecked cid->cpu table lookup
103  * @cid: cid to look up. Must be in [0, num_possible_cpus()).
104  *
105  * Intended for callsites that have already validated @cid and that run on a
106  * live scheduler, which guarantees the tables are published and stable.
107  */
108 static inline s32 __scx_cid_to_cpu(s32 cid)
109 {
110 	return rcu_dereference_all(scx_cid_to_cpu_tbl)[cid];
111 }
112 
113 /**
114  * __scx_cpu_to_cid - Unchecked cpu->cid table lookup
115  * @cpu: cpu to look up. Must be a valid possible cpu id.
116  *
117  * Same usage constraints as __scx_cid_to_cpu().
118  */
119 static inline s32 __scx_cpu_to_cid(s32 cpu)
120 {
121 	return rcu_dereference_all(scx_cpu_to_cid_tbl)[cpu];
122 }
123 
124 /**
125  * scx_cid_to_cpu - Translate @cid to its cpu
126  * @sch: scx_sched for error reporting
127  * @cid: cid to look up
128  *
129  * Return the cpu for @cid or a negative errno on failure. Invalid cid triggers
130  * scx_error() on @sch. The mapping is stable while the scheduler is live.
131  *
132  * Return -EINVAL without triggering scx_error() if no tables have been
133  * published yet, which a prog-facing kfunc can observe while racing the root
134  * scheduler enable.
135  */
136 static inline s32 scx_cid_to_cpu(struct scx_sched *sch, s32 cid)
137 {
138 	s16 *tbl = rcu_dereference_all(scx_cid_to_cpu_tbl);
139 
140 	if (!cid_valid(sch, cid) || unlikely(!tbl))
141 		return -EINVAL;
142 	return tbl[cid];
143 }
144 
145 /**
146  * scx_cpu_to_cid - Translate @cpu to its cid
147  * @sch: scx_sched for error reporting
148  * @cpu: cpu to look up
149  *
150  * Return the cid for @cpu or a negative errno on failure. Invalid cpu triggers
151  * scx_error() on @sch. Same usage rules as scx_cid_to_cpu().
152  */
153 static inline s32 scx_cpu_to_cid(struct scx_sched *sch, s32 cpu)
154 {
155 	s16 *tbl = rcu_dereference_all(scx_cpu_to_cid_tbl);
156 
157 	if (!scx_cpu_valid(sch, cpu, NULL) || unlikely(!tbl))
158 		return -EINVAL;
159 	return tbl[cpu];
160 }
161 
162 /**
163  * scx_is_cid_type - Test whether the active scheduler hierarchy is cid-form
164  */
165 static inline bool scx_is_cid_type(void)
166 {
167 	return static_branch_unlikely(&__scx_is_cid_type);
168 }
169 
170 static inline bool __scx_cmask_contains(u32 cid, const struct scx_cmask *m)
171 {
172 	return likely(cid >= m->base && cid < m->base + m->nr_cids);
173 }
174 
175 /* Word in bits[] covering @cid. @cid must satisfy __scx_cmask_contains(). */
176 static inline u64 *__scx_cmask_word(u32 cid, const struct scx_cmask *m)
177 {
178 	return (u64 *)&m->bits[cid / 64 - m->base / 64];
179 }
180 
181 /**
182  * __scx_cmask_init - Initialize @m with explicit storage capacity
183  * @m: cmask to initialize
184  * @base: first cid of the active range
185  * @nr_cids: number of cids in the active range
186  * @alloc_cids: storage capacity in cids, at least @nr_cids
187  *
188  * Use when storage is sized larger than the initial active range. All of
189  * bits[] is zeroed.
190  */
191 static inline void __scx_cmask_init(struct scx_cmask *m, u32 base, u32 nr_cids,
192 				    u32 alloc_cids)
193 {
194 	if (WARN_ON_ONCE(alloc_cids < nr_cids))
195 		nr_cids = alloc_cids;
196 
197 	m->base = base;
198 	m->nr_cids = nr_cids;
199 	m->alloc_words = SCX_CMASK_NR_WORDS(alloc_cids);
200 	memset(m->bits, 0, m->alloc_words * sizeof(u64));
201 }
202 
203 /**
204  * scx_cmask_init - Initialize @m on tight storage
205  * @m: cmask to initialize
206  * @base: first cid of the active range
207  * @nr_cids: number of cids in the active range
208  *
209  * All of bits[] is zeroed.
210  */
211 static inline void scx_cmask_init(struct scx_cmask *m, u32 base, u32 nr_cids)
212 {
213 	__scx_cmask_init(m, base, nr_cids, nr_cids);
214 }
215 
216 /**
217  * scx_cmask_reframe - Reshape @m's active range without resizing storage
218  * @m: cmask to reframe
219  * @base: new active range base
220  * @nr_cids: new active range length, must fit within @m->alloc_words
221  *
222  * Body bits within the new range become garbage - only the head and tail
223  * words are zeroed to keep the padding invariant.
224  */
225 static inline void scx_cmask_reframe(struct scx_cmask *m, u32 base, u32 nr_cids)
226 {
227 	if (WARN_ON_ONCE(SCX_CMASK_NR_WORDS(nr_cids) > m->alloc_words))
228 		return;
229 
230 	if (nr_cids) {
231 		u32 last_word = ((base & 63) + nr_cids - 1) / 64;
232 
233 		m->bits[0] = 0;
234 		m->bits[last_word] = 0;
235 	}
236 
237 	m->base = base;
238 	m->nr_cids = nr_cids;
239 }
240 
241 static inline void __scx_cmask_set(u32 cid, struct scx_cmask *m)
242 {
243 	if (!__scx_cmask_contains(cid, m))
244 		return;
245 	*__scx_cmask_word(cid, m) |= BIT_U64(cid & 63);
246 }
247 
248 /**
249  * scx_cmask_test - test whether @cid is set in @m
250  * @cid: cid to test
251  * @m: cmask to test
252  *
253  * Return %false if @cid is outside @m's active range. Otherwise return the
254  * bit's value. Read via READ_ONCE so callers can race set/clear writers.
255  */
256 static inline bool scx_cmask_test(u32 cid, const struct scx_cmask *m)
257 {
258 	if (!__scx_cmask_contains(cid, m))
259 		return false;
260 	return READ_ONCE(*__scx_cmask_word(cid, m)) & BIT_U64(cid & 63);
261 }
262 
263 /*
264  * Words of bits[] the active range spans, 0 if empty. Tighter than the storage
265  * SCX_CMASK_NR_WORDS() sizes for the worst-case base alignment.
266  */
267 static inline u32 scx_cmask_nr_used_words(const struct scx_cmask *m)
268 {
269 	if (!m->nr_cids)
270 		return 0;
271 	return ((m->base & 63) + m->nr_cids - 1) / 64 + 1;
272 }
273 
274 /**
275  * scx_cmask_for_each_cid - iterate set cids in @m
276  * @cid: s32 loop var that receives each set cid in turn
277  * @m: cmask to iterate
278  *
279  * Visits set bits within @m's active range in ascending order. Scans only the
280  * words the active range spans, where head and tail padding is kept zero, so
281  * no per-cid range check is needed.
282  */
283 #define scx_cmask_for_each_cid(cid, m)						\
284 	for (u64 __bs = (m)->base & ~63u, __wi = 0,				\
285 		     __nw = scx_cmask_nr_used_words(m);				\
286 	     __wi < __nw; __wi++)						\
287 		for (u64 __w = READ_ONCE((m)->bits[__wi]);			\
288 		     __w && ((cid) = __bs + __wi * 64 + __ffs64(__w), true);	\
289 		     __w &= __w - 1)
290 
291 /*
292  * scx_cpu_arg() wraps a cpu arg being handed to an SCX op. For cid-form
293  * schedulers it resolves to the matching cid; for cpu-form it passes @cpu
294  * through. scx_cpu_ret() is the inverse for a cpu/cid returned from an op
295  * (currently only ops.select_cpu); it validates the BPF-supplied cid and
296  * triggers scx_error() on @sch if invalid.
297  */
298 static inline s32 scx_cpu_arg(s32 cpu)
299 {
300 	if (scx_is_cid_type())
301 		return __scx_cpu_to_cid(cpu);
302 	return cpu;
303 }
304 
305 static inline s32 scx_cpu_ret(struct scx_sched *sch, s32 cpu_or_cid)
306 {
307 	if (cpu_or_cid < 0 || !scx_is_cid_type())
308 		return cpu_or_cid;
309 	return scx_cid_to_cpu(sch, cpu_or_cid);
310 }
311 
312 int scx_cmask_ref_init(struct scx_sched *sch, const struct scx_cmask *src,
313 		       struct scx_cmask_ref *ref);
314 void scx_cmask_ref_init_kern(struct scx_sched *sch, struct scx_cmask *m,
315 			     u32 base, u32 nr_cids, struct scx_cmask_ref *ref);
316 void scx_cmask_ref_shard(const struct scx_cmask_ref *ref, s32 shard_idx,
317 			 struct scx_cmask *out);
318 void scx_cmask_ref_from_cpumask(const struct scx_cmask_ref *ref,
319 				const struct cpumask *cpumask);
320 void scx_cmask_ref_or(const struct scx_cmask_ref *ref, const struct scx_cmask *src);
321 void scx_cmask_ref_copy(const struct scx_cmask_ref *ref, const struct scx_cmask *src);
322 
323 #endif /* _KERNEL_SCHED_EXT_CID_H */
324