xref: /linux/kernel/sched/ext/cid.c (revision a8dc810968af02190f55cc7574bc87c93156f266)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * BPF extensible scheduler class: Documentation/scheduler/sched-ext.rst
4  *
5  * Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
6  * Copyright (c) 2026 Tejun Heo <tj@kernel.org>
7  */
8 #include <linux/cacheinfo.h>
9 
10 #include "internal.h"
11 #include "cid.h"
12 
13 /*
14  * cid tables. The cid kfuncs are available whether the root scheduler is
15  * cid-form or cpu-form, the latter to allow gradual migration to cids, so every
16  * root builds a default mapping. Each root enable allocates a fresh set, builds
17  * it privately and publishes the __rcu globals below once the layout is final.
18  * Root disable unpublishes and RCU-frees the set. kfuncs may run before the
19  * tables are published and must check for NULL.
20  */
21 u32 scx_nr_cid_shards;
22 s16 __rcu *scx_cid_to_cpu_tbl;
23 s16 __rcu *scx_cpu_to_cid_tbl;
24 s32 __rcu *scx_cid_to_shard;
25 s32 __rcu *scx_shard_node;
26 struct scx_cid_shard __rcu *scx_cid_shard_ranges;
27 struct scx_cid_topo __rcu *scx_cid_topo;
28 
29 static struct scx_cid_tables *scx_cid_tables;	/* used only during alloc/free */
30 
31 #define SCX_CID_TOPO_NEG	(struct scx_cid_topo) {				\
32 	.core_cid = -1, .core_idx = -1, .llc_cid = -1, .llc_idx = -1,		\
33 	.node_cid = -1, .node_idx = -1, .shard_cid = -1, .shard_idx = -1,	\
34 }
35 
36 /*
37  * Return @cpu's LLC shared_cpu_map. If cacheinfo isn't populated (offline or
38  * !present), record @cpu in @fallbacks and return its node mask instead - the
39  * worst that can happen is that the cpu's LLC becomes coarser than reality.
40  */
41 static const struct cpumask *cpu_llc_mask(int cpu, struct cpumask *fallbacks)
42 {
43 	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(cpu);
44 
45 	if (!ci || !ci->info_list || !ci->num_leaves) {
46 		cpumask_set_cpu(cpu, fallbacks);
47 		return cpumask_of_node(cpu_to_node(cpu));
48 	}
49 	return &ci->info_list[ci->num_leaves - 1].shared_cpu_map;
50 }
51 
52 /*
53  * Compute per-LLC shard layout. Each shard holds at most @shard_size cids, and
54  * in any case no more than SCX_CID_SHARD_MAX_CPUS. Cores are spread as evenly
55  * as possible across shards so cpu count is balanced: the first *@nr_large_p
56  * shards get (*@cores_per_shard_p + 1) cores, the rest get *@cores_per_shard_p.
57  */
58 static void calc_shard_layout(const struct cpumask *llc_cpus, u32 shard_size,
59 			      u32 *cores_per_shard_p, u32 *nr_large_p)
60 {
61 	u32 nr_cores = 0, nr_cpus = 0, nr_shards;
62 	int cpu;
63 
64 	for_each_cpu(cpu, llc_cpus) {
65 		nr_cpus++;
66 		if (cpumask_first(topology_sibling_cpumask(cpu)) == cpu)
67 			nr_cores++;
68 	}
69 
70 	nr_shards = max_t(u32, 1, DIV_ROUND_UP(nr_cpus, shard_size));
71 	nr_shards = max_t(u32, nr_shards,
72 			  DIV_ROUND_UP(nr_cpus, SCX_CID_SHARD_MAX_CPUS));
73 
74 	*cores_per_shard_p = nr_cores / nr_shards;
75 	*nr_large_p = nr_cores % nr_shards;
76 }
77 
78 static void scx_cid_tables_free(struct scx_cid_tables *tbls)
79 {
80 	if (!tbls)
81 		return;
82 	kvfree(tbls->cid_to_cpu);
83 	kvfree(tbls->cpu_to_cid);
84 	kvfree(tbls->cid_to_shard);
85 	kvfree(tbls->shard_node);
86 	kvfree(tbls->shard_ranges);
87 	kvfree(tbls->topo);
88 	kfree(tbls);
89 }
90 
91 static void scx_cid_tables_free_rcufn(struct rcu_head *rcu)
92 {
93 	scx_cid_tables_free(container_of(rcu, struct scx_cid_tables, rcu));
94 }
95 
96 static struct scx_cid_tables *scx_cid_alloc_tables(void)
97 {
98 	u32 npossible = num_possible_cpus();
99 	struct scx_cid_tables *tbls;
100 
101 	tbls = kzalloc_obj(*tbls, GFP_KERNEL);
102 	if (!tbls)
103 		return NULL;
104 
105 	tbls->cid_to_cpu = kvcalloc(npossible, sizeof(*tbls->cid_to_cpu), GFP_KERNEL);
106 	tbls->cpu_to_cid = kvcalloc(nr_cpu_ids, sizeof(*tbls->cpu_to_cid), GFP_KERNEL);
107 	tbls->cid_to_shard = kvcalloc(npossible, sizeof(*tbls->cid_to_shard), GFP_KERNEL);
108 	tbls->shard_node = kvcalloc(npossible, sizeof(*tbls->shard_node), GFP_KERNEL);
109 	tbls->shard_ranges = kvcalloc(npossible, sizeof(*tbls->shard_ranges), GFP_KERNEL);
110 	tbls->topo = kvcalloc(npossible, sizeof(*tbls->topo), GFP_KERNEL);
111 
112 	if (!tbls->cid_to_cpu || !tbls->cpu_to_cid || !tbls->cid_to_shard ||
113 	    !tbls->shard_node || !tbls->shard_ranges || !tbls->topo) {
114 		scx_cid_tables_free(tbls);
115 		return NULL;
116 	}
117 
118 	return tbls;
119 }
120 
121 /**
122  * scx_cid_publish_tables - Publish the tables scx_cid_init() built
123  *
124  * Called after ops.init_cids() where the layout is final.
125  */
126 void scx_cid_publish_tables(void)
127 {
128 	struct scx_cid_tables *tbls = scx_cid_tables;
129 
130 	lockdep_assert_held(&scx_enable_mutex);
131 
132 	scx_nr_cid_shards = tbls->nr_shards;
133 	rcu_assign_pointer(scx_cid_to_cpu_tbl, tbls->cid_to_cpu);
134 	rcu_assign_pointer(scx_cpu_to_cid_tbl, tbls->cpu_to_cid);
135 	rcu_assign_pointer(scx_cid_to_shard, tbls->cid_to_shard);
136 	rcu_assign_pointer(scx_shard_node, tbls->shard_node);
137 	rcu_assign_pointer(scx_cid_shard_ranges, tbls->shard_ranges);
138 	rcu_assign_pointer(scx_cid_topo, tbls->topo);
139 }
140 
141 /**
142  * scx_cid_retire_tables - Unpublish and retire the cid tables
143  *
144  * Called by root disable after the readers which dereference without NULL
145  * checks are drained, inside cpus_read_lock() to exclude the hotplug path.
146  */
147 void scx_cid_retire_tables(void)
148 {
149 	struct scx_cid_tables *tbls = scx_cid_tables;
150 
151 	lockdep_assert_held(&scx_enable_mutex);
152 	lockdep_assert_cpus_held();
153 
154 	if (!tbls)
155 		return;
156 
157 	scx_cid_tables = NULL;
158 	RCU_INIT_POINTER(scx_cid_to_cpu_tbl, NULL);
159 	RCU_INIT_POINTER(scx_cpu_to_cid_tbl, NULL);
160 	RCU_INIT_POINTER(scx_cid_to_shard, NULL);
161 	RCU_INIT_POINTER(scx_shard_node, NULL);
162 	RCU_INIT_POINTER(scx_cid_shard_ranges, NULL);
163 	RCU_INIT_POINTER(scx_cid_topo, NULL);
164 	call_rcu(&tbls->rcu, scx_cid_tables_free_rcufn);
165 }
166 
167 /**
168  * scx_cid_init - build the cid mapping
169  * @sch: the scx_sched being initialized; used as the scx_error() target
170  *
171  * Build a fresh table set. It becomes visible through scx_cid_publish_tables()
172  * and is retired by scx_cid_retire_tables() at disable.
173  *
174  * See "Topological CPU IDs" in cid.h for the model. Walk online cpus by
175  * intersection at each level (parent_scratch & this_level_mask), which keeps
176  * containment correct by construction and naturally splits a physical LLC
177  * straddling two NUMA nodes into two LLC units. The caller must hold
178  * cpus_read_lock.
179  */
180 s32 scx_cid_init(struct scx_sched *sch)
181 {
182 	cpumask_var_t to_walk __free(free_cpumask_var) = CPUMASK_VAR_NULL;
183 	cpumask_var_t node_scratch __free(free_cpumask_var) = CPUMASK_VAR_NULL;
184 	cpumask_var_t llc_scratch __free(free_cpumask_var) = CPUMASK_VAR_NULL;
185 	cpumask_var_t core_scratch __free(free_cpumask_var) = CPUMASK_VAR_NULL;
186 	cpumask_var_t llc_fallback __free(free_cpumask_var) = CPUMASK_VAR_NULL;
187 	cpumask_var_t online_no_topo __free(free_cpumask_var) = CPUMASK_VAR_NULL;
188 	struct scx_cid_tables *tbls;
189 	u32 next_cid = 0;
190 	s32 next_node_idx = 0, next_llc_idx = 0, next_core_idx = 0;
191 	s32 next_shard_idx = 0;
192 	u32 shard_size, max_cids;
193 	u32 notopo_in_shard;
194 	s32 notopo_shard_cid, notopo_shard_idx;
195 	s32 cpu, cid, si;
196 
197 	/* CMASK_MAX_WORDS in cid.bpf.h covers NR_CPUS up to 8192 */
198 	BUILD_BUG_ON(NR_CPUS > 8192);
199 
200 	lockdep_assert_cpus_held();
201 	lockdep_assert_held(&scx_enable_mutex);
202 
203 	shard_size = sch->ops.cid_shard_size ?: SCX_CID_SHARD_SIZE_DFL;
204 	max_cids = min_t(u32, shard_size, SCX_CID_SHARD_MAX_CPUS);
205 
206 	tbls = scx_cid_alloc_tables();
207 	if (!tbls)
208 		return -ENOMEM;
209 
210 	scx_cid_tables = tbls;
211 
212 	for (si = 0; si < num_possible_cpus(); si++)
213 		tbls->shard_node[si] = NUMA_NO_NODE;
214 
215 	if (!zalloc_cpumask_var(&to_walk, GFP_KERNEL) ||
216 	    !zalloc_cpumask_var(&node_scratch, GFP_KERNEL) ||
217 	    !zalloc_cpumask_var(&llc_scratch, GFP_KERNEL) ||
218 	    !zalloc_cpumask_var(&core_scratch, GFP_KERNEL) ||
219 	    !zalloc_cpumask_var(&llc_fallback, GFP_KERNEL) ||
220 	    !zalloc_cpumask_var(&online_no_topo, GFP_KERNEL))
221 		return -ENOMEM;
222 
223 	/* -1 sentinels for sparse-possible cpu id holes (0 is a valid cid) */
224 	for (cpu = 0; cpu < nr_cpu_ids; cpu++)
225 		tbls->cpu_to_cid[cpu] = -1;
226 
227 	cpumask_copy(to_walk, cpu_online_mask);
228 
229 	while (!cpumask_empty(to_walk)) {
230 		s32 next_cpu = cpumask_first(to_walk);
231 		s32 nid = cpu_to_node(next_cpu);
232 		s32 node_cid = next_cid;
233 		s32 node_idx;
234 
235 		/*
236 		 * No NUMA info: skip and let the tail loop assign a no-topo
237 		 * cid. cpumask_of_node(-1) is undefined.
238 		 */
239 		if (nid < 0) {
240 			cpumask_clear_cpu(next_cpu, to_walk);
241 			continue;
242 		}
243 
244 		node_idx = next_node_idx++;
245 
246 		/* node_scratch = to_walk & this node */
247 		cpumask_and(node_scratch, to_walk, cpumask_of_node(nid));
248 		if (WARN_ON_ONCE(!cpumask_test_cpu(next_cpu, node_scratch)))
249 			return -EINVAL;
250 
251 		while (!cpumask_empty(node_scratch)) {
252 			s32 ncpu = cpumask_first(node_scratch);
253 			const struct cpumask *llc_mask = cpu_llc_mask(ncpu, llc_fallback);
254 			s32 llc_cid = next_cid;
255 			s32 llc_idx = next_llc_idx++;
256 			u32 cores_per_shard, nr_large;
257 			u32 shard_local = 0, cores_in_shard = 0, cids_in_shard = 0;
258 			s32 shard_cid, shard_idx;
259 
260 			/* llc_scratch = node_scratch & this llc */
261 			cpumask_and(llc_scratch, node_scratch, llc_mask);
262 			if (WARN_ON_ONCE(!cpumask_test_cpu(ncpu, llc_scratch)))
263 				return -EINVAL;
264 
265 			calc_shard_layout(llc_scratch, shard_size, &cores_per_shard, &nr_large);
266 			shard_cid = next_cid;
267 			shard_idx = next_shard_idx++;
268 			tbls->shard_node[shard_idx] = nid;
269 
270 			while (!cpumask_empty(llc_scratch)) {
271 				s32 lcpu = cpumask_first(llc_scratch);
272 				const struct cpumask *sib = topology_sibling_cpumask(lcpu);
273 				s32 core_cid = next_cid;
274 				s32 core_idx = next_core_idx++;
275 				s32 ccpu;
276 				u32 max_cores, cids_in_core;
277 
278 				/* core_scratch = llc_scratch & this core */
279 				cpumask_and(core_scratch, llc_scratch, sib);
280 				if (WARN_ON_ONCE(!cpumask_test_cpu(lcpu, core_scratch)))
281 					return -EINVAL;
282 
283 				/*
284 				 * Advance to a new shard when either core or
285 				 * cid count reaches max. The latter bounds
286 				 * shard sizes under uneven SMT. Never start an
287 				 * empty shard.
288 				 */
289 				cids_in_core = cpumask_weight(core_scratch);
290 				max_cores = cores_per_shard + (shard_local < nr_large ? 1 : 0);
291 				if (cores_in_shard &&
292 				    (cores_in_shard >= max_cores ||
293 				     cids_in_shard + cids_in_core > max_cids)) {
294 					shard_local++;
295 					cores_in_shard = 0;
296 					cids_in_shard = 0;
297 					shard_cid = next_cid;
298 					shard_idx = next_shard_idx++;
299 					tbls->shard_node[shard_idx] = nid;
300 				}
301 				cores_in_shard++;
302 				cids_in_shard += cids_in_core;
303 
304 				for_each_cpu(ccpu, core_scratch) {
305 					s32 cid = next_cid++;
306 
307 					tbls->cid_to_cpu[cid] = ccpu;
308 					tbls->cpu_to_cid[ccpu] = cid;
309 					tbls->cid_to_shard[cid] = shard_idx;
310 					tbls->topo[cid] = (struct scx_cid_topo){
311 						.core_cid = core_cid,
312 						.core_idx = core_idx,
313 						.llc_cid = llc_cid,
314 						.llc_idx = llc_idx,
315 						.node_cid = node_cid,
316 						.node_idx = node_idx,
317 						.shard_cid = shard_cid,
318 						.shard_idx = shard_idx,
319 					};
320 
321 					cpumask_clear_cpu(ccpu, llc_scratch);
322 					cpumask_clear_cpu(ccpu, node_scratch);
323 					cpumask_clear_cpu(ccpu, to_walk);
324 				}
325 			}
326 		}
327 	}
328 
329 	/*
330 	 * No-topo section: any possible cpu without a cid - normally just the
331 	 * not-online ones. Pack into shards of up to min(@shard_size,
332 	 * SCX_CID_SHARD_MAX_CPUS) cids so that every cid has a valid shard
333 	 * assignment and the hard cap holds even with a large @shard_size.
334 	 * Collect any currently-online cpus that land here in @online_no_topo
335 	 * so we can warn about them at the end.
336 	 */
337 	notopo_in_shard = min_t(u32, shard_size, SCX_CID_SHARD_MAX_CPUS);
338 	notopo_shard_cid = -1;
339 	notopo_shard_idx = -1;
340 
341 	for_each_cpu(cpu, cpu_possible_mask) {
342 		if (tbls->cpu_to_cid[cpu] != -1)
343 			continue;
344 		if (cpu_online(cpu))
345 			cpumask_set_cpu(cpu, online_no_topo);
346 
347 		cid = next_cid++;
348 		tbls->cid_to_cpu[cid] = cpu;
349 		tbls->cpu_to_cid[cpu] = cid;
350 
351 		if (notopo_in_shard >= min_t(u32, shard_size, SCX_CID_SHARD_MAX_CPUS)) {
352 			notopo_shard_cid = cid;
353 			notopo_shard_idx = next_shard_idx++;
354 			notopo_in_shard = 0;
355 		}
356 		notopo_in_shard++;
357 
358 		tbls->cid_to_shard[cid] = notopo_shard_idx;
359 		tbls->topo[cid] = SCX_CID_TOPO_NEG;
360 		tbls->topo[cid].shard_cid = notopo_shard_cid;
361 		tbls->topo[cid].shard_idx = notopo_shard_idx;
362 	}
363 
364 	if (!cpumask_empty(llc_fallback))
365 		pr_warn("scx_cid: cpus without cacheinfo, using node mask as llc: %*pbl\n",
366 			cpumask_pr_args(llc_fallback));
367 	if (!cpumask_empty(online_no_topo))
368 		pr_warn("scx_cid: online cpus with no usable topology: %*pbl\n",
369 			cpumask_pr_args(online_no_topo));
370 
371 	/*
372 	 * Fill cid_shard_ranges[] from cid_to_shard[]. Shards are contiguous
373 	 * cid ranges by construction: base_cid is the first cid landing in a
374 	 * shard, nr_cids is the count.
375 	 */
376 	for (cid = 0; cid < next_cid; cid++) {
377 		s32 sidx = tbls->cid_to_shard[cid];
378 
379 		if (tbls->shard_ranges[sidx].nr_cids == 0)
380 			tbls->shard_ranges[sidx].base_cid = cid;
381 		tbls->shard_ranges[sidx].nr_cids++;
382 	}
383 
384 	tbls->nr_shards = next_shard_idx;
385 	return 0;
386 }
387 
388 /**
389  * scx_cmask_clear - Zero every bit in @m's active range
390  * @m: cmask to clear
391  *
392  * Storage past the active range is left as is.
393  */
394 void scx_cmask_clear(struct scx_cmask *m)
395 {
396 	u32 nr_words;
397 
398 	if (!m->nr_cids)
399 		return;
400 	nr_words = (m->base + m->nr_cids - 1) / 64 - m->base / 64 + 1;
401 	memset(m->bits, 0, nr_words * sizeof(u64));
402 }
403 
404 /**
405  * scx_cmask_fill - Set every bit in @m's active range
406  * @m: cmask to fill
407  *
408  * Counterpart to scx_cmask_clear(). Storage past the active range is left as is.
409  */
410 void scx_cmask_fill(struct scx_cmask *m)
411 {
412 	u32 nr_words, head_bits, tail_bits;
413 
414 	if (!m->nr_cids)
415 		return;
416 	nr_words = (m->base + m->nr_cids - 1) / 64 - m->base / 64 + 1;
417 	memset(m->bits, 0xff, nr_words * sizeof(u64));
418 
419 	/* clear word-0 bits below base */
420 	head_bits = m->base & 63;
421 	if (head_bits)
422 		m->bits[0] &= ~((1ULL << head_bits) - 1);
423 
424 	/* clear last-word bits at or past base + nr_cids */
425 	tail_bits = (m->base + m->nr_cids) & 63;
426 	if (tail_bits)
427 		m->bits[nr_words - 1] &= (1ULL << tail_bits) - 1;
428 }
429 
430 /*
431  * Return the index of the largest entry in @counts, or NUMA_NO_NODE if all
432  * entries are zero. Ties resolve to the lowest index.
433  */
434 static s32 pick_max_node(const u32 *counts, u32 n)
435 {
436 	s32 best = NUMA_NO_NODE;
437 	u32 best_count = 0, i;
438 
439 	for (i = 0; i < n; i++) {
440 		if (counts[i] > best_count) {
441 			best_count = counts[i];
442 			best = i;
443 		}
444 	}
445 	return best;
446 }
447 
448 __bpf_kfunc_start_defs();
449 
450 /**
451  * scx_bpf_cid_override - Install an explicit cpu->cid mapping with shard info
452  * @cpu_to_cid_src: array of nr_cpu_ids s32 entries (cid for each cpu)
453  * @cpu_to_cid_src__sz: must be nr_cpu_ids * sizeof(s32) bytes
454  * @shard_start_src: array of first-cid-of-each-shard, strictly increasing from 0
455  * @shard_start_src__sz: nr_shards * sizeof(s32) bytes
456  * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
457  *
458  * May only be called from ops.init_cids() of the root scheduler. Replace the
459  * topology-probed cid mapping and shard layout with caller-provided ones. Each
460  * possible cpu must map to a unique cid in [0, num_possible_cpus()). The shard
461  * starts must be strictly increasing with the first entry 0 and all values <
462  * num_possible_cpus(). The last shard extends to num_possible_cpus() and no
463  * shard may span more than SCX_CID_SHARD_MAX_CPUS cids. Topo info
464  * (core/LLC/node) is cleared and the shard layout is set from the input. On
465  * invalid input, abort the scheduler.
466  */
467 __bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid_src, u32 cpu_to_cid_src__sz,
468 				       const s32 *shard_start_src, u32 shard_start_src__sz,
469 				       const struct bpf_prog_aux *aux)
470 {
471 	cpumask_var_t seen __free(free_cpumask_var) = CPUMASK_VAR_NULL;
472 	u32 *node_counts __free(kfree) = NULL;
473 	s32 *cpu_to_cid __free(kfree) = NULL;
474 	s32 *shard_start __free(kfree) = NULL;
475 	u32 npossible = num_possible_cpus();
476 	struct scx_cid_tables *tbls;
477 	struct scx_sched *sch;
478 	u32 nr_shards;
479 	bool alloced;
480 	s32 cpu, cid, si;
481 
482 	/*
483 	 * GFP_KERNEL allocs must happen before the rcu read section. Snapshot
484 	 * the BPF-supplied arrays so a concurrent map mutation can't change
485 	 * them between validation and use.
486 	 */
487 	alloced = zalloc_cpumask_var(&seen, GFP_KERNEL);
488 	node_counts = kcalloc(nr_node_ids, sizeof(*node_counts), GFP_KERNEL);
489 	cpu_to_cid = kmemdup(cpu_to_cid_src, cpu_to_cid_src__sz, GFP_KERNEL);
490 	shard_start = kmemdup(shard_start_src, shard_start_src__sz, GFP_KERNEL);
491 
492 	guard(rcu)();
493 
494 	sch = scx_prog_sched(aux);
495 	if (unlikely(!sch))
496 		return;
497 
498 	/* called from ops.init_cids(), so the tables exist and are unpublished */
499 	lockdep_assert_held(&scx_enable_mutex);
500 	tbls = scx_cid_tables;
501 
502 	if (!alloced || !node_counts || !cpu_to_cid || !shard_start) {
503 		scx_error(sch, "scx_bpf_cid_override: allocation failed");
504 		return;
505 	}
506 
507 	if (cpu_to_cid_src__sz != nr_cpu_ids * sizeof(s32)) {
508 		scx_error(sch, "scx_bpf_cid_override: cpu_to_cid expected %zu bytes, got %u",
509 			  nr_cpu_ids * sizeof(s32), cpu_to_cid_src__sz);
510 		return;
511 	}
512 
513 	if (!shard_start_src__sz || shard_start_src__sz % sizeof(s32)) {
514 		scx_error(sch, "scx_bpf_cid_override: invalid shard_start size %u",
515 			  shard_start_src__sz);
516 		return;
517 	}
518 
519 	nr_shards = shard_start_src__sz / sizeof(s32);
520 
521 	/* validate shard_start[]: starts at 0, strictly increasing, in range */
522 	if (shard_start[0] != 0) {
523 		scx_error(sch, "scx_bpf_cid_override: shard_start[0] must be 0, got %d",
524 			  shard_start[0]);
525 		return;
526 	}
527 	for (si = 1; si < nr_shards; si++) {
528 		if (shard_start[si] <= shard_start[si - 1]) {
529 			scx_error(sch, "scx_bpf_cid_override: shard_start not increasing at [%d]",
530 				  si);
531 			return;
532 		}
533 		if (shard_start[si] >= npossible) {
534 			scx_error(sch, "scx_bpf_cid_override: shard_start[%d]=%d >= %u",
535 				  si, shard_start[si], npossible);
536 			return;
537 		}
538 		if (shard_start[si] - shard_start[si - 1] > SCX_CID_SHARD_MAX_CPUS) {
539 			scx_error(sch, "scx_bpf_cid_override: shard[%d] span %d exceeds max %d",
540 				  si - 1, shard_start[si] - shard_start[si - 1],
541 				  SCX_CID_SHARD_MAX_CPUS);
542 			return;
543 		}
544 	}
545 	if (npossible - shard_start[nr_shards - 1] > SCX_CID_SHARD_MAX_CPUS) {
546 		scx_error(sch, "scx_bpf_cid_override: shard[%d] span %d exceeds max %d",
547 			  nr_shards - 1, npossible - shard_start[nr_shards - 1],
548 			  SCX_CID_SHARD_MAX_CPUS);
549 		return;
550 	}
551 
552 	/* validate first so that invalid input leaves the tables untouched */
553 	for_each_possible_cpu(cpu) {
554 		s32 c = cpu_to_cid[cpu];
555 
556 		if (!cid_valid(sch, c))
557 			return;
558 		if (cpumask_test_and_set_cpu(c, seen)) {
559 			scx_error(sch, "cid %d assigned to multiple cpus", c);
560 			return;
561 		}
562 	}
563 
564 	for_each_possible_cpu(cpu) {
565 		s32 c = cpu_to_cid[cpu];
566 
567 		tbls->cpu_to_cid[cpu] = c;
568 		tbls->cid_to_cpu[c] = cpu;
569 	}
570 
571 	/*
572 	 * Derive shard_node[] by majority count: an overridden shard may
573 	 * span NUMA nodes, so assign each to the node that owns the most cpus.
574 	 */
575 	for (si = 0; si < nr_shards; si++) {
576 		u32 end = (si + 1 < nr_shards) ? shard_start[si + 1] : npossible;
577 
578 		memset(node_counts, 0, nr_node_ids * sizeof(*node_counts));
579 		for (cid = shard_start[si]; cid < end; cid++) {
580 			s32 node = cpu_to_node(tbls->cid_to_cpu[cid]);
581 
582 			if (numa_valid_node(node))
583 				node_counts[node]++;
584 		}
585 		tbls->shard_node[si] = pick_max_node(node_counts, nr_node_ids);
586 	}
587 
588 	/*
589 	 * Invalidate stale topo info and install shard layout from
590 	 * @shard_start. Walk shards to derive shard_cid/shard_idx for each cid.
591 	 */
592 	si = 0;
593 	for (cid = 0; cid < npossible; cid++) {
594 		if (si + 1 < nr_shards && cid >= shard_start[si + 1])
595 			si++;
596 		tbls->cid_to_shard[cid] = si;
597 		tbls->topo[cid] = SCX_CID_TOPO_NEG;
598 		tbls->topo[cid].shard_cid = shard_start[si];
599 		tbls->topo[cid].shard_idx = si;
600 	}
601 
602 	/* Rebuild shard_ranges[] for the new layout. */
603 	memset(tbls->shard_ranges, 0, npossible * sizeof(*tbls->shard_ranges));
604 	for (si = 0; si < nr_shards; si++) {
605 		u32 end = (si + 1 < nr_shards) ? shard_start[si + 1] : npossible;
606 
607 		tbls->shard_ranges[si].base_cid = shard_start[si];
608 		tbls->shard_ranges[si].nr_cids = end - shard_start[si];
609 	}
610 
611 	tbls->nr_shards = nr_shards;
612 }
613 
614 /**
615  * scx_bpf_cid_to_cpu - Return the raw CPU id for @cid
616  * @cid: cid to look up
617  * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
618  *
619  * Return the raw CPU id for @cid. Trigger scx_error() and return -EINVAL if
620  * @cid is invalid. The cid<->cpu mapping is static for the lifetime of the
621  * loaded scheduler, so the BPF side can cache the result to avoid repeated
622  * kfunc invocations.
623  */
624 __bpf_kfunc s32 scx_bpf_cid_to_cpu(s32 cid, const struct bpf_prog_aux *aux)
625 {
626 	struct scx_sched *sch;
627 
628 	guard(rcu)();
629 
630 	sch = scx_prog_sched(aux);
631 	if (unlikely(!sch))
632 		return -EINVAL;
633 	return scx_cid_to_cpu(sch, cid);
634 }
635 
636 /**
637  * scx_bpf_cpu_to_cid - Return the cid for @cpu
638  * @cpu: cpu to look up
639  * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
640  *
641  * Return the cid for @cpu. Trigger scx_error() and return -EINVAL if @cpu is
642  * invalid. The cid<->cpu mapping is static for the lifetime of the loaded
643  * scheduler, so the BPF side can cache the result to avoid repeated kfunc
644  * invocations.
645  */
646 __bpf_kfunc s32 scx_bpf_cpu_to_cid(s32 cpu, const struct bpf_prog_aux *aux)
647 {
648 	struct scx_sched *sch;
649 
650 	guard(rcu)();
651 
652 	sch = scx_prog_sched(aux);
653 	if (unlikely(!sch))
654 		return -EINVAL;
655 	return scx_cpu_to_cid(sch, cpu);
656 }
657 
658 /*
659  * Set ops on cmasks. cmask_walk_op2() shares one walk across mutating
660  * (and/or/copy/andnot) and predicate (subset/intersects) two-cmask forms;
661  * cmask_walk_op1() does the same shape over a single cmask range. Every public
662  * entry passes a compile-time-constant @op; cmask_walk_op{1,2}() and
663  * cmask_word_op{1,2}() are __always_inline so the inner switch collapses to the
664  * selected op and cmask_op2_is_pred() folds the predicate early-exit out of
665  * mutating ops.
666  *
667  * Two-cmask ops only touch @dst bits inside the intersection of the two ranges;
668  * bits outside stay untouched. In particular, scx_cmask_copy() does NOT zero
669  * @dst bits that lie outside @src's range.
670  *
671  * Word accesses use READ_ONCE/WRITE_ONCE so a caller may read @src
672  * locklessly. Memory ordering against concurrent writers is the caller's
673  * responsibility.
674  */
675 enum cmask_op2 {
676 	/* mutating */
677 	CMASK_OP2_AND,
678 	CMASK_OP2_OR,
679 	CMASK_OP2_COPY,
680 	CMASK_OP2_ANDNOT,
681 	/* predicates - short-circuit when the per-word result is true */
682 	CMASK_OP2_SUBSET,
683 	CMASK_OP2_INTERSECTS,
684 	/*
685 	 * @a is a BPF-arena cmask. Words on @a use READ_ONCE/WRITE_ONCE since
686 	 * BPF may read/write concurrently. See scx_cmask_ref_or() / _copy().
687 	 */
688 	CMASK_OP2_REF_OR,
689 	CMASK_OP2_REF_COPY,
690 };
691 
692 static __always_inline bool cmask_op2_is_pred(const enum cmask_op2 op)
693 {
694 	return op == CMASK_OP2_SUBSET || op == CMASK_OP2_INTERSECTS;
695 }
696 
697 static __always_inline bool cmask_word_op2(u64 *av, const u64 *bp, u64 mask,
698 					   const enum cmask_op2 op)
699 {
700 	switch (op) {
701 	case CMASK_OP2_AND:
702 		WRITE_ONCE(*av, *av & (~mask | READ_ONCE(*bp)));
703 		return false;
704 	case CMASK_OP2_OR:
705 		WRITE_ONCE(*av, *av | (READ_ONCE(*bp) & mask));
706 		return false;
707 	case CMASK_OP2_COPY:
708 		WRITE_ONCE(*av, (*av & ~mask) | (READ_ONCE(*bp) & mask));
709 		return false;
710 	case CMASK_OP2_ANDNOT:
711 		WRITE_ONCE(*av, *av & ~(READ_ONCE(*bp) & mask));
712 		return false;
713 	case CMASK_OP2_SUBSET:
714 		/* stop on the first bit in @sub not set in @super */
715 		return (READ_ONCE(*bp) & ~READ_ONCE(*av)) & mask;
716 	case CMASK_OP2_INTERSECTS:
717 		return (READ_ONCE(*av) & READ_ONCE(*bp)) & mask;
718 	case CMASK_OP2_REF_OR:
719 		WRITE_ONCE(*av, READ_ONCE(*av) | (READ_ONCE(*bp) & mask));
720 		return false;
721 	case CMASK_OP2_REF_COPY:
722 		WRITE_ONCE(*av, (READ_ONCE(*av) & ~mask) | (READ_ONCE(*bp) & mask));
723 		return false;
724 	}
725 	unreachable();
726 }
727 
728 /*
729  * Walk the intersection of [@a_base, @a_base + @a_nr_cids) with [@b_base,
730  * @b_base + @b_nr_cids) word by word, applying @op. Mutating ops walk all words
731  * and return false; predicates return true on the first word whose per-word
732  * test is true. Empty intersection returns false (matches "no bits to consider"
733  * for both mutate and predicate).
734  *
735  * Base/nr_cids are taken as parameters so callers with snapshotted bounds can
736  * drive the walk with values independent of the cmask's header.
737  */
738 static __always_inline bool cmask_walk_op2(u64 *a_bits, u32 a_base, u32 a_nr_cids,
739 					   const u64 *b_bits, u32 b_base, u32 b_nr_cids,
740 					   const enum cmask_op2 op)
741 {
742 	u32 lo = max(a_base, b_base);
743 	u32 hi = min(a_base + a_nr_cids, b_base + b_nr_cids);
744 	u32 a_word_off = a_base / 64;
745 	u32 b_word_off = b_base / 64;
746 	u32 lo_word = lo / 64;
747 	u32 hi_word = (hi - 1) / 64;
748 	u64 head_mask = GENMASK_U64(63, lo & 63);
749 	u64 tail_mask = GENMASK_U64((hi - 1) & 63, 0);
750 	u32 w;
751 
752 	if (lo >= hi)
753 		return false;
754 
755 	if (lo_word == hi_word)
756 		return cmask_word_op2(&a_bits[lo_word - a_word_off],
757 				      &b_bits[lo_word - b_word_off],
758 				      head_mask & tail_mask, op);
759 
760 	if (cmask_word_op2(&a_bits[lo_word - a_word_off],
761 			   &b_bits[lo_word - b_word_off], head_mask, op) &&
762 	    cmask_op2_is_pred(op))
763 		return true;
764 
765 	for (w = lo_word + 1; w < hi_word; w++)
766 		if (cmask_word_op2(&a_bits[w - a_word_off],
767 				   &b_bits[w - b_word_off], ~0ULL, op) &&
768 		    cmask_op2_is_pred(op))
769 			return true;
770 
771 	return cmask_word_op2(&a_bits[hi_word - a_word_off],
772 			      &b_bits[hi_word - b_word_off], tail_mask, op);
773 }
774 
775 enum cmask_op1 {
776 	CMASK_OP1_ANY_SET,
777 };
778 
779 static __always_inline bool cmask_word_op1(const u64 *ap, u64 mask,
780 					   const enum cmask_op1 op)
781 {
782 	switch (op) {
783 	case CMASK_OP1_ANY_SET:
784 		return READ_ONCE(*ap) & mask;
785 	}
786 	unreachable();
787 }
788 
789 /*
790  * Walk [@a_base, @a_base + @a_nr_cids) of @a_bits word by word, applying @op.
791  * Returns true on the first word whose per-word test is true; returns false if
792  * no word matches or the range is empty. All current op1s short-circuit on
793  * per-word true; if a non-predicate op1 lands here, add a cmask_op1_is_pred()
794  * guard analogous to cmask_op2_is_pred().
795  */
796 static __always_inline bool cmask_walk_op1(const u64 *a_bits, u32 a_base,
797 					   u32 a_nr_cids,
798 					   const enum cmask_op1 op)
799 {
800 	u32 lo = a_base;
801 	u32 hi = a_base + a_nr_cids;
802 	u32 a_word_off = a_base / 64;
803 	u32 lo_word = lo / 64;
804 	u32 hi_word = (hi - 1) / 64;
805 	u64 head_mask = GENMASK_U64(63, lo & 63);
806 	u64 tail_mask = GENMASK_U64((hi - 1) & 63, 0);
807 	u32 w;
808 
809 	if (lo >= hi)
810 		return false;
811 
812 	if (lo_word == hi_word)
813 		return cmask_word_op1(&a_bits[lo_word - a_word_off],
814 				      head_mask & tail_mask, op);
815 
816 	if (cmask_word_op1(&a_bits[lo_word - a_word_off], head_mask, op))
817 		return true;
818 	for (w = lo_word + 1; w < hi_word; w++)
819 		if (cmask_word_op1(&a_bits[w - a_word_off], ~0ULL, op))
820 			return true;
821 	return cmask_word_op1(&a_bits[hi_word - a_word_off], tail_mask, op);
822 }
823 
824 void scx_cmask_and(struct scx_cmask *dst, const struct scx_cmask *src)
825 {
826 	cmask_walk_op2(dst->bits, dst->base, dst->nr_cids,
827 		       src->bits, src->base, src->nr_cids, CMASK_OP2_AND);
828 }
829 
830 void scx_cmask_or(struct scx_cmask *dst, const struct scx_cmask *src)
831 {
832 	cmask_walk_op2(dst->bits, dst->base, dst->nr_cids,
833 		       src->bits, src->base, src->nr_cids, CMASK_OP2_OR);
834 }
835 
836 void scx_cmask_copy(struct scx_cmask *dst, const struct scx_cmask *src)
837 {
838 	cmask_walk_op2(dst->bits, dst->base, dst->nr_cids,
839 		       src->bits, src->base, src->nr_cids, CMASK_OP2_COPY);
840 }
841 
842 void scx_cmask_andnot(struct scx_cmask *dst, const struct scx_cmask *src)
843 {
844 	cmask_walk_op2(dst->bits, dst->base, dst->nr_cids,
845 		       src->bits, src->base, src->nr_cids, CMASK_OP2_ANDNOT);
846 }
847 
848 /*
849  * Return true if @cm has any bit set in [@lo, @hi). Caller must ensure
850  * [@lo, @hi) is contained in @cm's range.
851  */
852 static bool cmask_any_set_in_range(const struct scx_cmask *cm, u32 lo, u32 hi)
853 {
854 	if (lo >= hi)
855 		return false;
856 	return cmask_walk_op1(&cm->bits[lo / 64 - cm->base / 64], lo, hi - lo,
857 			      CMASK_OP1_ANY_SET);
858 }
859 
860 /**
861  * scx_cmask_subset - test whether @sub is a subset of @super
862  * @sub: cmask to test
863  * @super: cmask to test against
864  *
865  * Return true iff every set bit of @sub is also set in @super.
866  */
867 bool scx_cmask_subset(const struct scx_cmask *sub, const struct scx_cmask *super)
868 {
869 	u32 super_end = super->base + super->nr_cids;
870 	u32 sub_end = sub->base + sub->nr_cids;
871 
872 	/*
873 	 * Set bits in @sub outside @super's range can't be in @super, so any
874 	 * such bit means not a subset. The walk below only visits words
875 	 * common to both ranges, so these need a separate scan.
876 	 */
877 	if (sub->base < super->base &&
878 	    cmask_any_set_in_range(sub, sub->base, min(super->base, sub_end)))
879 		return false;
880 	if (sub_end > super_end &&
881 	    cmask_any_set_in_range(sub, max(sub->base, super_end), sub_end))
882 		return false;
883 
884 	return !cmask_walk_op2((u64 *)super->bits, super->base, super->nr_cids,
885 			       sub->bits, sub->base, sub->nr_cids, CMASK_OP2_SUBSET);
886 }
887 
888 bool scx_cmask_intersects(const struct scx_cmask *a, const struct scx_cmask *b)
889 {
890 	return cmask_walk_op2((u64 *)a->bits, a->base, a->nr_cids,
891 			      b->bits, b->base, b->nr_cids, CMASK_OP2_INTERSECTS);
892 }
893 
894 /**
895  * scx_cmask_empty - Test whether @m has no bits set
896  * @m: cmask to test
897  *
898  * Return true iff @m's active range has no bits set.
899  */
900 bool scx_cmask_empty(const struct scx_cmask *m)
901 {
902 	return !cmask_any_set_in_range(m, m->base, m->base + m->nr_cids);
903 }
904 
905 /**
906  * scx_bpf_cid_topo - Copy out per-cid topology info
907  * @cid: cid to look up
908  * @out__uninit: where to copy the topology info; fully written by this call
909  * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
910  *
911  * Fill @out__uninit with the topology info for @cid. Trigger scx_error() if
912  * @cid is out of range. If @cid is valid but in the no-topo section, all fields
913  * are set to -1. All fields are also set to -1 when no cid tables have been
914  * published yet, which a program may observe while racing the root enable.
915  */
916 __bpf_kfunc void scx_bpf_cid_topo(s32 cid, struct scx_cid_topo *out__uninit,
917 				  const struct bpf_prog_aux *aux)
918 {
919 	struct scx_cid_topo *topo;
920 	struct scx_sched *sch;
921 
922 	guard(rcu)();
923 
924 	sch = scx_prog_sched(aux);
925 	topo = rcu_dereference(scx_cid_topo);
926 	if (unlikely(!sch) || !cid_valid(sch, cid) || unlikely(!topo)) {
927 		*out__uninit = SCX_CID_TOPO_NEG;
928 		return;
929 	}
930 
931 	*out__uninit = topo[cid];
932 }
933 
934 __bpf_kfunc_end_defs();
935 
936 BTF_KFUNCS_START(scx_kfunc_ids_init_cids)
937 BTF_ID_FLAGS(func, scx_bpf_cid_override, KF_IMPLICIT_ARGS | KF_SLEEPABLE)
938 BTF_KFUNCS_END(scx_kfunc_ids_init_cids)
939 
940 static const struct btf_kfunc_id_set scx_kfunc_set_init_cids = {
941 	.owner	= THIS_MODULE,
942 	.set	= &scx_kfunc_ids_init_cids,
943 	.filter	= scx_kfunc_context_filter,
944 };
945 
946 BTF_KFUNCS_START(scx_kfunc_ids_cid)
947 BTF_ID_FLAGS(func, scx_bpf_cid_to_cpu, KF_IMPLICIT_ARGS)
948 BTF_ID_FLAGS(func, scx_bpf_cpu_to_cid, KF_IMPLICIT_ARGS)
949 BTF_ID_FLAGS(func, scx_bpf_cid_topo, KF_IMPLICIT_ARGS)
950 BTF_KFUNCS_END(scx_kfunc_ids_cid)
951 
952 static const struct btf_kfunc_id_set scx_kfunc_set_cid = {
953 	.owner	= THIS_MODULE,
954 	.set	= &scx_kfunc_ids_cid,
955 };
956 
957 /**
958  * scx_cmask_ref_init - Bind a scx_cmask_ref to a BPF-arena cmask
959  * @sch: scheduler whose arena hosts @src
960  * @src: BPF-supplied cmask, rebased to its kernel address
961  * @ref: output ref
962  *
963  * Snapshot @src's @base, @nr_cids and @alloc_words. The snapshot is necessary
964  * because BPF may mutate the live header asynchronously.
965  *
966  * Return 0 on success, -EINVAL if the range is out of bounds or @alloc_words
967  * doesn't cover it.
968  */
969 int scx_cmask_ref_init(struct scx_sched *sch, const struct scx_cmask *src,
970 		       struct scx_cmask_ref *ref)
971 {
972 	u32 base, nr_cids, alloc_words, npossible = num_possible_cpus();
973 	s32 *cid_to_shard;
974 
975 	base = READ_ONCE(src->base);
976 	nr_cids = READ_ONCE(src->nr_cids);
977 	alloc_words = READ_ONCE(src->alloc_words);
978 
979 	if (unlikely(base >= npossible || nr_cids > npossible - base ||
980 		     SCX_CMASK_NR_WORDS(nr_cids) > alloc_words))
981 		return -EINVAL;
982 
983 	ref->sch = sch;
984 	ref->src = (struct scx_cmask *)src;
985 	ref->base = base;
986 	ref->nr_cids = nr_cids;
987 
988 	cid_to_shard = rcu_dereference_all(scx_cid_to_shard);
989 	ref->shard_first = cid_to_shard[base];
990 	if (likely(nr_cids))
991 		ref->shard_end = cid_to_shard[base + nr_cids - 1] + 1;
992 	else
993 		ref->shard_end = ref->shard_first;
994 
995 	return 0;
996 }
997 
998 /**
999  * scx_cmask_ref_init_kern - Bind a scx_cmask_ref to a kernel-owned cmask
1000  * @sch: scheduler the cmask belongs to
1001  * @m: kernel address of the target cmask, storage sized for @nr_cids at @base
1002  * @base: first cid of the active range
1003  * @nr_cids: active range length
1004  * @ref: output ref
1005  *
1006  * Like scx_cmask_ref_init() but the geometry is supplied by the caller, not
1007  * read from @m's header, so a concurrent BPF write to the header can't steer
1008  * later sizing or offsets. Rewrite the header from the trusted geometry and
1009  * bind @ref to it.
1010  */
1011 void scx_cmask_ref_init_kern(struct scx_sched *sch, struct scx_cmask *m,
1012 			     u32 base, u32 nr_cids, struct scx_cmask_ref *ref)
1013 {
1014 	s32 *cid_to_shard;
1015 
1016 	WRITE_ONCE(m->base, base);
1017 	WRITE_ONCE(m->nr_cids, nr_cids);
1018 	WRITE_ONCE(m->alloc_words, SCX_CMASK_NR_WORDS(nr_cids));
1019 
1020 	ref->sch = sch;
1021 	ref->src = m;
1022 	ref->base = base;
1023 	ref->nr_cids = nr_cids;
1024 
1025 	cid_to_shard = rcu_dereference_all(scx_cid_to_shard);
1026 	ref->shard_first = cid_to_shard[base];
1027 	if (likely(nr_cids))
1028 		ref->shard_end = cid_to_shard[base + nr_cids - 1] + 1;
1029 	else
1030 		ref->shard_end = ref->shard_first;
1031 }
1032 
1033 /**
1034  * scx_cmask_ref_shard - Read one shard from @ref into @out
1035  * @ref: validated ref
1036  * @shard_idx: target shard, in [@ref->shard_first, @ref->shard_end)
1037  * @out: output cmask whose @out->alloc_words must hold the shard
1038  *
1039  * Set @out to the intersection of @ref's range with @shard_idx's cid range,
1040  * with bits[] read from @ref->src via READ_ONCE. Empty intersection sets
1041  * @out->nr_cids to 0. scx_error()s on @ref's sched if @out can't hold the
1042  * shard.
1043  */
1044 void scx_cmask_ref_shard(const struct scx_cmask_ref *ref, s32 shard_idx,
1045 			 struct scx_cmask *out)
1046 {
1047 	const struct scx_cid_shard *shard =
1048 		&rcu_dereference_all(scx_cid_shard_ranges)[shard_idx];
1049 	u32 shard_base = shard->base_cid;
1050 	u32 shard_end = shard_base + shard->nr_cids;
1051 	u32 isect_base, isect_end, nr_words, src_off, wi;
1052 	u64 head_mask, tail_mask;
1053 
1054 	isect_base = max(ref->base, shard_base);
1055 	isect_end = min(ref->base + ref->nr_cids, shard_end);
1056 
1057 	if (isect_base >= isect_end) {
1058 		out->base = shard_base;
1059 		out->nr_cids = 0;
1060 		return;
1061 	}
1062 
1063 	nr_words = ((isect_end - 1) / 64) - (isect_base / 64) + 1;
1064 	if (nr_words > out->alloc_words) {
1065 		scx_error(ref->sch, "scx_cmask_ref_shard: out alloc_words=%u < %u for shard %d",
1066 			  out->alloc_words, nr_words, shard_idx);
1067 		out->base = shard_base;
1068 		out->nr_cids = 0;
1069 		return;
1070 	}
1071 
1072 	out->base = isect_base;
1073 	out->nr_cids = isect_end - isect_base;
1074 	src_off = (isect_base / 64) - (ref->base / 64);
1075 
1076 	for (wi = 0; wi < nr_words; wi++)
1077 		out->bits[wi] = READ_ONCE(ref->src->bits[src_off + wi]);
1078 
1079 	head_mask = GENMASK_U64(63, isect_base & 63);
1080 	out->bits[0] &= head_mask;
1081 	tail_mask = GENMASK_U64((isect_end - 1) & 63, 0);
1082 	out->bits[nr_words - 1] &= tail_mask;
1083 }
1084 
1085 /**
1086  * scx_cmask_ref_or - OR @src into the arena cmask referenced by @ref
1087  * @ref: validated ref
1088  * @src: stable kernel cmask
1089  *
1090  * Bits inside the intersection of @ref's snapshotted range with @src's range
1091  * are OR'd into @ref->src and bits outside are left unchanged. Stores on
1092  * @ref->src use WRITE_ONCE since BPF may read/write concurrently.
1093  */
1094 void scx_cmask_ref_or(const struct scx_cmask_ref *ref, const struct scx_cmask *src)
1095 {
1096 	cmask_walk_op2(ref->src->bits, ref->base, ref->nr_cids,
1097 		       src->bits, src->base, src->nr_cids, CMASK_OP2_REF_OR);
1098 }
1099 
1100 /**
1101  * scx_cmask_ref_copy - Copy @src into the arena cmask referenced by @ref
1102  * @ref: validated ref
1103  * @src: stable kernel cmask
1104  *
1105  * Bits inside the intersection of @ref's snapshotted range with @src's range
1106  * take @src's values and bits outside are left unchanged. Stores on @ref->src
1107  * use WRITE_ONCE since BPF may read/write concurrently.
1108  */
1109 void scx_cmask_ref_copy(const struct scx_cmask_ref *ref, const struct scx_cmask *src)
1110 {
1111 	cmask_walk_op2(ref->src->bits, ref->base, ref->nr_cids,
1112 		       src->bits, src->base, src->nr_cids, CMASK_OP2_REF_COPY);
1113 }
1114 
1115 /**
1116  * scx_cmask_ref_from_cpumask - Populate @ref's arena cmask from a cpumask
1117  * @ref: kern-bound ref, see scx_cmask_ref_init_kern()
1118  * @cpumask: cpus to translate into cids
1119  *
1120  * Write @ref's active range one word at a time, setting each cid's bit when
1121  * its cpu is in @cpumask. Offsets and length come from @ref's trusted geometry
1122  * and stores use WRITE_ONCE since BPF may read concurrently, so the arena
1123  * header is never read.
1124  */
1125 void scx_cmask_ref_from_cpumask(const struct scx_cmask_ref *ref,
1126 				const struct cpumask *cpumask)
1127 {
1128 	struct scx_cmask *m = ref->src;
1129 	u32 base = ref->base, nr_cids = ref->nr_cids;
1130 	u32 wi, nr_words;
1131 
1132 	if (!nr_cids)
1133 		return;
1134 
1135 	nr_words = (base + nr_cids - 1) / 64 - base / 64 + 1;
1136 	for (wi = 0; wi < nr_words; wi++) {
1137 		u32 word_first_cid = (base / 64 + wi) * 64;
1138 		u64 word = 0;
1139 		u32 bit;
1140 
1141 		for (bit = 0; bit < 64; bit++) {
1142 			u32 cid = word_first_cid + bit;
1143 
1144 			if (cid < base || cid >= base + nr_cids)
1145 				continue;
1146 			if (cpumask_test_cpu(__scx_cid_to_cpu(cid), cpumask))
1147 				word |= BIT_U64(bit);
1148 		}
1149 		WRITE_ONCE(m->bits[wi], word);
1150 	}
1151 }
1152 
1153 int scx_cid_kfunc_init(void)
1154 {
1155 	return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &scx_kfunc_set_init_cids) ?:
1156 		register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &scx_kfunc_set_cid) ?:
1157 		register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &scx_kfunc_set_cid) ?:
1158 		register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &scx_kfunc_set_cid);
1159 }
1160