xref: /linux/kernel/sched/ext/cid.c (revision 11260c335ec6071af5543aef73000b28f041c124)
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  */
cpu_llc_mask(int cpu,struct cpumask * fallbacks)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  */
calc_shard_layout(const struct cpumask * llc_cpus,u32 shard_size,u32 * cores_per_shard_p,u32 * nr_large_p)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 
scx_cid_tables_free(struct scx_cid_tables * tbls)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 
scx_cid_tables_free_rcufn(struct rcu_head * rcu)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 
scx_cid_alloc_tables(void)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  */
scx_cid_publish_tables(void)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  */
scx_cid_retire_tables(void)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  */
scx_cid_init(struct scx_sched * sch)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  */
scx_cmask_clear(struct scx_cmask * m)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  */
scx_cmask_fill(struct scx_cmask * m)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  */
pick_max_node(const u32 * counts,u32 n)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__arena: array of nr_cpu_ids s32 entries (cid for each cpu)
453  * @cpu_to_cid_cnt: number of entries, must be nr_cpu_ids
454  * @shard_start__arena: array of first-cid-of-each-shard, one entry per shard
455  * @shard_start_cnt: number of shards
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  */
scx_bpf_cid_override(const s32 * cpu_to_cid__arena,u32 cpu_to_cid_cnt,const s32 * shard_start__arena,u32 shard_start_cnt,const struct bpf_prog_aux * aux)467 __bpf_kfunc void scx_bpf_cid_override(const s32 *cpu_to_cid__arena, u32 cpu_to_cid_cnt,
468 				      const s32 *shard_start__arena, u32 shard_start_cnt,
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 = shard_start_cnt;
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 arena write can't change
485 	 * them between validation and use.
486 	 *
487 	 * The BPF-supplied counts size the snapshots and thus the arena reads.
488 	 * Gate the copies on the count bounds, reported below once @sch is
489 	 * available. The bounded reads, at most 32KB, stay within the guard
490 	 * region that arena fault recovery covers.
491 	 */
492 	alloced = zalloc_cpumask_var(&seen, GFP_KERNEL);
493 	node_counts = kcalloc(nr_node_ids, sizeof(*node_counts), GFP_KERNEL);
494 	if (cpu_to_cid_cnt == nr_cpu_ids)
495 		cpu_to_cid = kmemdup(cpu_to_cid__arena, cpu_to_cid_cnt * sizeof(s32),
496 				     GFP_KERNEL);
497 	if (nr_shards && nr_shards <= npossible)
498 		shard_start = kmemdup(shard_start__arena, nr_shards * sizeof(s32),
499 				      GFP_KERNEL);
500 
501 	guard(rcu)();
502 
503 	sch = scx_prog_sched(aux);
504 	if (unlikely(!sch))
505 		return;
506 
507 	/* called from ops.init_cids(), so the tables exist and are unpublished */
508 	lockdep_assert_held(&scx_enable_mutex);
509 	tbls = scx_cid_tables;
510 
511 	if (cpu_to_cid_cnt != nr_cpu_ids) {
512 		scx_error(sch, "scx_bpf_cid_override: cpu_to_cid expected %u entries, got %u",
513 			  nr_cpu_ids, cpu_to_cid_cnt);
514 		return;
515 	}
516 
517 	if (!nr_shards || nr_shards > npossible) {
518 		scx_error(sch, "scx_bpf_cid_override: invalid shard_start count %u",
519 			  nr_shards);
520 		return;
521 	}
522 
523 	if (!alloced || !node_counts || !cpu_to_cid || !shard_start) {
524 		scx_error(sch, "scx_bpf_cid_override: allocation failed");
525 		return;
526 	}
527 
528 	/* validate shard_start[]: starts at 0, strictly increasing, in range */
529 	if (shard_start[0] != 0) {
530 		scx_error(sch, "scx_bpf_cid_override: shard_start[0] must be 0, got %d",
531 			  shard_start[0]);
532 		return;
533 	}
534 	for (si = 1; si < nr_shards; si++) {
535 		if (shard_start[si] <= shard_start[si - 1]) {
536 			scx_error(sch, "scx_bpf_cid_override: shard_start not increasing at [%d]",
537 				  si);
538 			return;
539 		}
540 		if (shard_start[si] >= npossible) {
541 			scx_error(sch, "scx_bpf_cid_override: shard_start[%d]=%d >= %u",
542 				  si, shard_start[si], npossible);
543 			return;
544 		}
545 		if (shard_start[si] - shard_start[si - 1] > SCX_CID_SHARD_MAX_CPUS) {
546 			scx_error(sch, "scx_bpf_cid_override: shard[%d] span %d exceeds max %d",
547 				  si - 1, shard_start[si] - shard_start[si - 1],
548 				  SCX_CID_SHARD_MAX_CPUS);
549 			return;
550 		}
551 	}
552 	if (npossible - shard_start[nr_shards - 1] > SCX_CID_SHARD_MAX_CPUS) {
553 		scx_error(sch, "scx_bpf_cid_override: shard[%d] span %d exceeds max %d",
554 			  nr_shards - 1, npossible - shard_start[nr_shards - 1],
555 			  SCX_CID_SHARD_MAX_CPUS);
556 		return;
557 	}
558 
559 	/* validate first so that invalid input leaves the tables untouched */
560 	for_each_possible_cpu(cpu) {
561 		s32 c = cpu_to_cid[cpu];
562 
563 		if (!cid_valid(sch, c))
564 			return;
565 		if (cpumask_test_and_set_cpu(c, seen)) {
566 			scx_error(sch, "cid %d assigned to multiple cpus", c);
567 			return;
568 		}
569 	}
570 
571 	for_each_possible_cpu(cpu) {
572 		s32 c = cpu_to_cid[cpu];
573 
574 		tbls->cpu_to_cid[cpu] = c;
575 		tbls->cid_to_cpu[c] = cpu;
576 	}
577 
578 	/*
579 	 * Derive shard_node[] by majority count: an overridden shard may
580 	 * span NUMA nodes, so assign each to the node that owns the most cpus.
581 	 */
582 	for (si = 0; si < nr_shards; si++) {
583 		u32 end = (si + 1 < nr_shards) ? shard_start[si + 1] : npossible;
584 
585 		memset(node_counts, 0, nr_node_ids * sizeof(*node_counts));
586 		for (cid = shard_start[si]; cid < end; cid++) {
587 			s32 node = cpu_to_node(tbls->cid_to_cpu[cid]);
588 
589 			if (numa_valid_node(node))
590 				node_counts[node]++;
591 		}
592 		tbls->shard_node[si] = pick_max_node(node_counts, nr_node_ids);
593 	}
594 
595 	/*
596 	 * Invalidate stale topo info and install shard layout from
597 	 * @shard_start. Walk shards to derive shard_cid/shard_idx for each cid.
598 	 */
599 	si = 0;
600 	for (cid = 0; cid < npossible; cid++) {
601 		if (si + 1 < nr_shards && cid >= shard_start[si + 1])
602 			si++;
603 		tbls->cid_to_shard[cid] = si;
604 		tbls->topo[cid] = SCX_CID_TOPO_NEG;
605 		tbls->topo[cid].shard_cid = shard_start[si];
606 		tbls->topo[cid].shard_idx = si;
607 	}
608 
609 	/* Rebuild shard_ranges[] for the new layout. */
610 	memset(tbls->shard_ranges, 0, npossible * sizeof(*tbls->shard_ranges));
611 	for (si = 0; si < nr_shards; si++) {
612 		u32 end = (si + 1 < nr_shards) ? shard_start[si + 1] : npossible;
613 
614 		tbls->shard_ranges[si].base_cid = shard_start[si];
615 		tbls->shard_ranges[si].nr_cids = end - shard_start[si];
616 	}
617 
618 	tbls->nr_shards = nr_shards;
619 }
620 
621 /**
622  * scx_bpf_cid_to_cpu - Return the raw CPU id for @cid
623  * @cid: cid to look up
624  * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
625  *
626  * Return the raw CPU id for @cid. Trigger scx_error() and return -EINVAL if
627  * @cid is invalid. The cid<->cpu mapping is static for the lifetime of the
628  * loaded scheduler, so the BPF side can cache the result to avoid repeated
629  * kfunc invocations.
630  */
scx_bpf_cid_to_cpu(s32 cid,const struct bpf_prog_aux * aux)631 __bpf_kfunc s32 scx_bpf_cid_to_cpu(s32 cid, const struct bpf_prog_aux *aux)
632 {
633 	struct scx_sched *sch;
634 
635 	guard(rcu)();
636 
637 	sch = scx_prog_sched(aux);
638 	if (unlikely(!sch))
639 		return -EINVAL;
640 	return scx_cid_to_cpu(sch, cid);
641 }
642 
643 /**
644  * scx_bpf_cpu_to_cid - Return the cid for @cpu
645  * @cpu: cpu to look up
646  * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
647  *
648  * Return the cid for @cpu. Trigger scx_error() and return -EINVAL if @cpu is
649  * invalid. The cid<->cpu mapping is static for the lifetime of the loaded
650  * scheduler, so the BPF side can cache the result to avoid repeated kfunc
651  * invocations.
652  */
scx_bpf_cpu_to_cid(s32 cpu,const struct bpf_prog_aux * aux)653 __bpf_kfunc s32 scx_bpf_cpu_to_cid(s32 cpu, const struct bpf_prog_aux *aux)
654 {
655 	struct scx_sched *sch;
656 
657 	guard(rcu)();
658 
659 	sch = scx_prog_sched(aux);
660 	if (unlikely(!sch))
661 		return -EINVAL;
662 	return scx_cpu_to_cid(sch, cpu);
663 }
664 
665 /*
666  * Set ops on cmasks. cmask_walk_op2() shares one walk across mutating
667  * (and/or/copy/andnot) and predicate (subset/intersects) two-cmask forms;
668  * cmask_walk_op1() does the same shape over a single cmask range. Every public
669  * entry passes a compile-time-constant @op; cmask_walk_op{1,2}() and
670  * cmask_word_op{1,2}() are __always_inline so the inner switch collapses to the
671  * selected op and cmask_op2_is_pred() folds the predicate early-exit out of
672  * mutating ops.
673  *
674  * Two-cmask ops only touch @dst bits inside the intersection of the two ranges;
675  * bits outside stay untouched. In particular, scx_cmask_copy() does NOT zero
676  * @dst bits that lie outside @src's range.
677  *
678  * Word accesses use READ_ONCE/WRITE_ONCE so a caller may read @src
679  * locklessly. Memory ordering against concurrent writers is the caller's
680  * responsibility.
681  */
682 enum cmask_op2 {
683 	/* mutating */
684 	CMASK_OP2_AND,
685 	CMASK_OP2_OR,
686 	CMASK_OP2_COPY,
687 	CMASK_OP2_ANDNOT,
688 	/* predicates - short-circuit when the per-word result is true */
689 	CMASK_OP2_SUBSET,
690 	CMASK_OP2_INTERSECTS,
691 	/*
692 	 * @a is a BPF-arena cmask. Words on @a use READ_ONCE/WRITE_ONCE since
693 	 * BPF may read/write concurrently. See scx_cmask_ref_or() / _copy().
694 	 */
695 	CMASK_OP2_REF_OR,
696 	CMASK_OP2_REF_COPY,
697 };
698 
cmask_op2_is_pred(const enum cmask_op2 op)699 static __always_inline bool cmask_op2_is_pred(const enum cmask_op2 op)
700 {
701 	return op == CMASK_OP2_SUBSET || op == CMASK_OP2_INTERSECTS;
702 }
703 
cmask_word_op2(u64 * av,const u64 * bp,u64 mask,const enum cmask_op2 op)704 static __always_inline bool cmask_word_op2(u64 *av, const u64 *bp, u64 mask,
705 					   const enum cmask_op2 op)
706 {
707 	switch (op) {
708 	case CMASK_OP2_AND:
709 		WRITE_ONCE(*av, *av & (~mask | READ_ONCE(*bp)));
710 		return false;
711 	case CMASK_OP2_OR:
712 		WRITE_ONCE(*av, *av | (READ_ONCE(*bp) & mask));
713 		return false;
714 	case CMASK_OP2_COPY:
715 		WRITE_ONCE(*av, (*av & ~mask) | (READ_ONCE(*bp) & mask));
716 		return false;
717 	case CMASK_OP2_ANDNOT:
718 		WRITE_ONCE(*av, *av & ~(READ_ONCE(*bp) & mask));
719 		return false;
720 	case CMASK_OP2_SUBSET:
721 		/* stop on the first bit in @sub not set in @super */
722 		return (READ_ONCE(*bp) & ~READ_ONCE(*av)) & mask;
723 	case CMASK_OP2_INTERSECTS:
724 		return (READ_ONCE(*av) & READ_ONCE(*bp)) & mask;
725 	case CMASK_OP2_REF_OR:
726 		WRITE_ONCE(*av, READ_ONCE(*av) | (READ_ONCE(*bp) & mask));
727 		return false;
728 	case CMASK_OP2_REF_COPY:
729 		WRITE_ONCE(*av, (READ_ONCE(*av) & ~mask) | (READ_ONCE(*bp) & mask));
730 		return false;
731 	}
732 	unreachable();
733 }
734 
735 /*
736  * Walk the intersection of [@a_base, @a_base + @a_nr_cids) with [@b_base,
737  * @b_base + @b_nr_cids) word by word, applying @op. Mutating ops walk all words
738  * and return false; predicates return true on the first word whose per-word
739  * test is true. Empty intersection returns false (matches "no bits to consider"
740  * for both mutate and predicate).
741  *
742  * Base/nr_cids are taken as parameters so callers with snapshotted bounds can
743  * drive the walk with values independent of the cmask's header.
744  */
cmask_walk_op2(u64 * a_bits,u32 a_base,u32 a_nr_cids,const u64 * b_bits,u32 b_base,u32 b_nr_cids,const enum cmask_op2 op)745 static __always_inline bool cmask_walk_op2(u64 *a_bits, u32 a_base, u32 a_nr_cids,
746 					   const u64 *b_bits, u32 b_base, u32 b_nr_cids,
747 					   const enum cmask_op2 op)
748 {
749 	u32 lo = max(a_base, b_base);
750 	u32 hi = min(a_base + a_nr_cids, b_base + b_nr_cids);
751 	u32 a_word_off = a_base / 64;
752 	u32 b_word_off = b_base / 64;
753 	u32 lo_word = lo / 64;
754 	u32 hi_word = (hi - 1) / 64;
755 	u64 head_mask = GENMASK_U64(63, lo & 63);
756 	u64 tail_mask = GENMASK_U64((hi - 1) & 63, 0);
757 	u32 w;
758 
759 	if (lo >= hi)
760 		return false;
761 
762 	if (lo_word == hi_word)
763 		return cmask_word_op2(&a_bits[lo_word - a_word_off],
764 				      &b_bits[lo_word - b_word_off],
765 				      head_mask & tail_mask, op);
766 
767 	if (cmask_word_op2(&a_bits[lo_word - a_word_off],
768 			   &b_bits[lo_word - b_word_off], head_mask, op) &&
769 	    cmask_op2_is_pred(op))
770 		return true;
771 
772 	for (w = lo_word + 1; w < hi_word; w++)
773 		if (cmask_word_op2(&a_bits[w - a_word_off],
774 				   &b_bits[w - b_word_off], ~0ULL, op) &&
775 		    cmask_op2_is_pred(op))
776 			return true;
777 
778 	return cmask_word_op2(&a_bits[hi_word - a_word_off],
779 			      &b_bits[hi_word - b_word_off], tail_mask, op);
780 }
781 
782 enum cmask_op1 {
783 	CMASK_OP1_ANY_SET,
784 };
785 
cmask_word_op1(const u64 * ap,u64 mask,const enum cmask_op1 op)786 static __always_inline bool cmask_word_op1(const u64 *ap, u64 mask,
787 					   const enum cmask_op1 op)
788 {
789 	switch (op) {
790 	case CMASK_OP1_ANY_SET:
791 		return READ_ONCE(*ap) & mask;
792 	}
793 	unreachable();
794 }
795 
796 /*
797  * Walk [@a_base, @a_base + @a_nr_cids) of @a_bits word by word, applying @op.
798  * Returns true on the first word whose per-word test is true; returns false if
799  * no word matches or the range is empty. All current op1s short-circuit on
800  * per-word true; if a non-predicate op1 lands here, add a cmask_op1_is_pred()
801  * guard analogous to cmask_op2_is_pred().
802  */
cmask_walk_op1(const u64 * a_bits,u32 a_base,u32 a_nr_cids,const enum cmask_op1 op)803 static __always_inline bool cmask_walk_op1(const u64 *a_bits, u32 a_base,
804 					   u32 a_nr_cids,
805 					   const enum cmask_op1 op)
806 {
807 	u32 lo = a_base;
808 	u32 hi = a_base + a_nr_cids;
809 	u32 a_word_off = a_base / 64;
810 	u32 lo_word = lo / 64;
811 	u32 hi_word = (hi - 1) / 64;
812 	u64 head_mask = GENMASK_U64(63, lo & 63);
813 	u64 tail_mask = GENMASK_U64((hi - 1) & 63, 0);
814 	u32 w;
815 
816 	if (lo >= hi)
817 		return false;
818 
819 	if (lo_word == hi_word)
820 		return cmask_word_op1(&a_bits[lo_word - a_word_off],
821 				      head_mask & tail_mask, op);
822 
823 	if (cmask_word_op1(&a_bits[lo_word - a_word_off], head_mask, op))
824 		return true;
825 	for (w = lo_word + 1; w < hi_word; w++)
826 		if (cmask_word_op1(&a_bits[w - a_word_off], ~0ULL, op))
827 			return true;
828 	return cmask_word_op1(&a_bits[hi_word - a_word_off], tail_mask, op);
829 }
830 
scx_cmask_and(struct scx_cmask * dst,const struct scx_cmask * src)831 void scx_cmask_and(struct scx_cmask *dst, const struct scx_cmask *src)
832 {
833 	cmask_walk_op2(dst->bits, dst->base, dst->nr_cids,
834 		       src->bits, src->base, src->nr_cids, CMASK_OP2_AND);
835 }
836 
scx_cmask_or(struct scx_cmask * dst,const struct scx_cmask * src)837 void scx_cmask_or(struct scx_cmask *dst, const struct scx_cmask *src)
838 {
839 	cmask_walk_op2(dst->bits, dst->base, dst->nr_cids,
840 		       src->bits, src->base, src->nr_cids, CMASK_OP2_OR);
841 }
842 
scx_cmask_copy(struct scx_cmask * dst,const struct scx_cmask * src)843 void scx_cmask_copy(struct scx_cmask *dst, const struct scx_cmask *src)
844 {
845 	cmask_walk_op2(dst->bits, dst->base, dst->nr_cids,
846 		       src->bits, src->base, src->nr_cids, CMASK_OP2_COPY);
847 }
848 
scx_cmask_andnot(struct scx_cmask * dst,const struct scx_cmask * src)849 void scx_cmask_andnot(struct scx_cmask *dst, const struct scx_cmask *src)
850 {
851 	cmask_walk_op2(dst->bits, dst->base, dst->nr_cids,
852 		       src->bits, src->base, src->nr_cids, CMASK_OP2_ANDNOT);
853 }
854 
855 /*
856  * Return true if @cm has any bit set in [@lo, @hi). Caller must ensure
857  * [@lo, @hi) is contained in @cm's range.
858  */
cmask_any_set_in_range(const struct scx_cmask * cm,u32 lo,u32 hi)859 static bool cmask_any_set_in_range(const struct scx_cmask *cm, u32 lo, u32 hi)
860 {
861 	if (lo >= hi)
862 		return false;
863 	return cmask_walk_op1(&cm->bits[lo / 64 - cm->base / 64], lo, hi - lo,
864 			      CMASK_OP1_ANY_SET);
865 }
866 
867 /**
868  * scx_cmask_subset - test whether @sub is a subset of @super
869  * @sub: cmask to test
870  * @super: cmask to test against
871  *
872  * Return true iff every set bit of @sub is also set in @super.
873  */
scx_cmask_subset(const struct scx_cmask * sub,const struct scx_cmask * super)874 bool scx_cmask_subset(const struct scx_cmask *sub, const struct scx_cmask *super)
875 {
876 	u32 super_end = super->base + super->nr_cids;
877 	u32 sub_end = sub->base + sub->nr_cids;
878 
879 	/*
880 	 * Set bits in @sub outside @super's range can't be in @super, so any
881 	 * such bit means not a subset. The walk below only visits words
882 	 * common to both ranges, so these need a separate scan.
883 	 */
884 	if (sub->base < super->base &&
885 	    cmask_any_set_in_range(sub, sub->base, min(super->base, sub_end)))
886 		return false;
887 	if (sub_end > super_end &&
888 	    cmask_any_set_in_range(sub, max(sub->base, super_end), sub_end))
889 		return false;
890 
891 	return !cmask_walk_op2((u64 *)super->bits, super->base, super->nr_cids,
892 			       sub->bits, sub->base, sub->nr_cids, CMASK_OP2_SUBSET);
893 }
894 
scx_cmask_intersects(const struct scx_cmask * a,const struct scx_cmask * b)895 bool scx_cmask_intersects(const struct scx_cmask *a, const struct scx_cmask *b)
896 {
897 	return cmask_walk_op2((u64 *)a->bits, a->base, a->nr_cids,
898 			      b->bits, b->base, b->nr_cids, CMASK_OP2_INTERSECTS);
899 }
900 
901 /**
902  * scx_cmask_empty - Test whether @m has no bits set
903  * @m: cmask to test
904  *
905  * Return true iff @m's active range has no bits set.
906  */
scx_cmask_empty(const struct scx_cmask * m)907 bool scx_cmask_empty(const struct scx_cmask *m)
908 {
909 	return !cmask_any_set_in_range(m, m->base, m->base + m->nr_cids);
910 }
911 
912 /**
913  * scx_bpf_cid_topo - Copy out per-cid topology info
914  * @cid: cid to look up
915  * @out__uninit: where to copy the topology info; fully written by this call
916  * @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
917  *
918  * Fill @out__uninit with the topology info for @cid. Trigger scx_error() if
919  * @cid is out of range. If @cid is valid but in the no-topo section, all fields
920  * are set to -1. All fields are also set to -1 when no cid tables have been
921  * published yet, which a program may observe while racing the root enable.
922  */
scx_bpf_cid_topo(s32 cid,struct scx_cid_topo * out__uninit,const struct bpf_prog_aux * aux)923 __bpf_kfunc void scx_bpf_cid_topo(s32 cid, struct scx_cid_topo *out__uninit,
924 				  const struct bpf_prog_aux *aux)
925 {
926 	struct scx_cid_topo *topo;
927 	struct scx_sched *sch;
928 
929 	guard(rcu)();
930 
931 	sch = scx_prog_sched(aux);
932 	topo = rcu_dereference(scx_cid_topo);
933 	if (unlikely(!sch) || !cid_valid(sch, cid) || unlikely(!topo)) {
934 		*out__uninit = SCX_CID_TOPO_NEG;
935 		return;
936 	}
937 
938 	*out__uninit = topo[cid];
939 }
940 
941 __bpf_kfunc_end_defs();
942 
943 BTF_KFUNCS_START(scx_kfunc_ids_init_cids)
944 BTF_ID_FLAGS(func, scx_bpf_cid_override, KF_IMPLICIT_ARGS | KF_SLEEPABLE)
945 BTF_KFUNCS_END(scx_kfunc_ids_init_cids)
946 
947 static const struct btf_kfunc_id_set scx_kfunc_set_init_cids = {
948 	.owner	= THIS_MODULE,
949 	.set	= &scx_kfunc_ids_init_cids,
950 	.filter	= scx_kfunc_context_filter,
951 };
952 
953 BTF_KFUNCS_START(scx_kfunc_ids_cid)
954 BTF_ID_FLAGS(func, scx_bpf_cid_to_cpu, KF_IMPLICIT_ARGS)
955 BTF_ID_FLAGS(func, scx_bpf_cpu_to_cid, KF_IMPLICIT_ARGS)
956 BTF_ID_FLAGS(func, scx_bpf_cid_topo, KF_IMPLICIT_ARGS)
957 BTF_KFUNCS_END(scx_kfunc_ids_cid)
958 
959 static const struct btf_kfunc_id_set scx_kfunc_set_cid = {
960 	.owner	= THIS_MODULE,
961 	.set	= &scx_kfunc_ids_cid,
962 };
963 
964 /**
965  * scx_cmask_ref_init - Bind a scx_cmask_ref to a BPF-arena cmask
966  * @sch: scheduler whose arena hosts @src
967  * @src: BPF-supplied cmask, rebased to its kernel address
968  * @ref: output ref
969  *
970  * Snapshot @src's @base, @nr_cids and @alloc_words. The snapshot is necessary
971  * because BPF may mutate the live header asynchronously.
972  *
973  * Return 0 on success, -EINVAL if the range is out of bounds or @alloc_words
974  * doesn't cover it.
975  */
scx_cmask_ref_init(struct scx_sched * sch,const struct scx_cmask * src,struct scx_cmask_ref * ref)976 int scx_cmask_ref_init(struct scx_sched *sch, const struct scx_cmask *src,
977 		       struct scx_cmask_ref *ref)
978 {
979 	u32 base, nr_cids, alloc_words, npossible = num_possible_cpus();
980 	s32 *cid_to_shard;
981 
982 	base = READ_ONCE(src->base);
983 	nr_cids = READ_ONCE(src->nr_cids);
984 	alloc_words = READ_ONCE(src->alloc_words);
985 
986 	if (unlikely(base >= npossible || nr_cids > npossible - base ||
987 		     SCX_CMASK_NR_WORDS(nr_cids) > alloc_words))
988 		return -EINVAL;
989 
990 	ref->sch = sch;
991 	ref->src = (struct scx_cmask *)src;
992 	ref->base = base;
993 	ref->nr_cids = nr_cids;
994 
995 	cid_to_shard = rcu_dereference_all(scx_cid_to_shard);
996 	ref->shard_first = cid_to_shard[base];
997 	if (likely(nr_cids))
998 		ref->shard_end = cid_to_shard[base + nr_cids - 1] + 1;
999 	else
1000 		ref->shard_end = ref->shard_first;
1001 
1002 	return 0;
1003 }
1004 
1005 /**
1006  * scx_cmask_ref_init_kern - Bind a scx_cmask_ref to a kernel-owned cmask
1007  * @sch: scheduler the cmask belongs to
1008  * @m: kernel address of the target cmask, storage sized for @nr_cids at @base
1009  * @base: first cid of the active range
1010  * @nr_cids: active range length
1011  * @ref: output ref
1012  *
1013  * Like scx_cmask_ref_init() but the geometry is supplied by the caller, not
1014  * read from @m's header, so a concurrent BPF write to the header can't steer
1015  * later sizing or offsets. Rewrite the header from the trusted geometry and
1016  * bind @ref to it.
1017  */
scx_cmask_ref_init_kern(struct scx_sched * sch,struct scx_cmask * m,u32 base,u32 nr_cids,struct scx_cmask_ref * ref)1018 void scx_cmask_ref_init_kern(struct scx_sched *sch, struct scx_cmask *m,
1019 			     u32 base, u32 nr_cids, struct scx_cmask_ref *ref)
1020 {
1021 	s32 *cid_to_shard;
1022 
1023 	WRITE_ONCE(m->base, base);
1024 	WRITE_ONCE(m->nr_cids, nr_cids);
1025 	WRITE_ONCE(m->alloc_words, SCX_CMASK_NR_WORDS(nr_cids));
1026 
1027 	ref->sch = sch;
1028 	ref->src = m;
1029 	ref->base = base;
1030 	ref->nr_cids = nr_cids;
1031 
1032 	cid_to_shard = rcu_dereference_all(scx_cid_to_shard);
1033 	ref->shard_first = cid_to_shard[base];
1034 	if (likely(nr_cids))
1035 		ref->shard_end = cid_to_shard[base + nr_cids - 1] + 1;
1036 	else
1037 		ref->shard_end = ref->shard_first;
1038 }
1039 
1040 /**
1041  * scx_cmask_ref_shard - Read one shard from @ref into @out
1042  * @ref: validated ref
1043  * @shard_idx: target shard, in [@ref->shard_first, @ref->shard_end)
1044  * @out: output cmask whose @out->alloc_words must hold the shard
1045  *
1046  * Set @out to the intersection of @ref's range with @shard_idx's cid range,
1047  * with bits[] read from @ref->src via READ_ONCE. Empty intersection sets
1048  * @out->nr_cids to 0. scx_error()s on @ref's sched if @out can't hold the
1049  * shard.
1050  */
scx_cmask_ref_shard(const struct scx_cmask_ref * ref,s32 shard_idx,struct scx_cmask * out)1051 void scx_cmask_ref_shard(const struct scx_cmask_ref *ref, s32 shard_idx,
1052 			 struct scx_cmask *out)
1053 {
1054 	const struct scx_cid_shard *shard =
1055 		&rcu_dereference_all(scx_cid_shard_ranges)[shard_idx];
1056 	u32 shard_base = shard->base_cid;
1057 	u32 shard_end = shard_base + shard->nr_cids;
1058 	u32 isect_base, isect_end, nr_words, src_off, wi;
1059 	u64 head_mask, tail_mask;
1060 
1061 	isect_base = max(ref->base, shard_base);
1062 	isect_end = min(ref->base + ref->nr_cids, shard_end);
1063 
1064 	if (isect_base >= isect_end) {
1065 		out->base = shard_base;
1066 		out->nr_cids = 0;
1067 		return;
1068 	}
1069 
1070 	nr_words = ((isect_end - 1) / 64) - (isect_base / 64) + 1;
1071 	if (nr_words > out->alloc_words) {
1072 		scx_error(ref->sch, "scx_cmask_ref_shard: out alloc_words=%u < %u for shard %d",
1073 			  out->alloc_words, nr_words, shard_idx);
1074 		out->base = shard_base;
1075 		out->nr_cids = 0;
1076 		return;
1077 	}
1078 
1079 	out->base = isect_base;
1080 	out->nr_cids = isect_end - isect_base;
1081 	src_off = (isect_base / 64) - (ref->base / 64);
1082 
1083 	for (wi = 0; wi < nr_words; wi++)
1084 		out->bits[wi] = READ_ONCE(ref->src->bits[src_off + wi]);
1085 
1086 	head_mask = GENMASK_U64(63, isect_base & 63);
1087 	out->bits[0] &= head_mask;
1088 	tail_mask = GENMASK_U64((isect_end - 1) & 63, 0);
1089 	out->bits[nr_words - 1] &= tail_mask;
1090 }
1091 
1092 /**
1093  * scx_cmask_ref_or - OR @src into the arena cmask referenced by @ref
1094  * @ref: validated ref
1095  * @src: stable kernel cmask
1096  *
1097  * Bits inside the intersection of @ref's snapshotted range with @src's range
1098  * are OR'd into @ref->src and bits outside are left unchanged. Stores on
1099  * @ref->src use WRITE_ONCE since BPF may read/write concurrently.
1100  */
scx_cmask_ref_or(const struct scx_cmask_ref * ref,const struct scx_cmask * src)1101 void scx_cmask_ref_or(const struct scx_cmask_ref *ref, const struct scx_cmask *src)
1102 {
1103 	cmask_walk_op2(ref->src->bits, ref->base, ref->nr_cids,
1104 		       src->bits, src->base, src->nr_cids, CMASK_OP2_REF_OR);
1105 }
1106 
1107 /**
1108  * scx_cmask_ref_copy - Copy @src into the arena cmask referenced by @ref
1109  * @ref: validated ref
1110  * @src: stable kernel cmask
1111  *
1112  * Bits inside the intersection of @ref's snapshotted range with @src's range
1113  * take @src's values and bits outside are left unchanged. Stores on @ref->src
1114  * use WRITE_ONCE since BPF may read/write concurrently.
1115  */
scx_cmask_ref_copy(const struct scx_cmask_ref * ref,const struct scx_cmask * src)1116 void scx_cmask_ref_copy(const struct scx_cmask_ref *ref, const struct scx_cmask *src)
1117 {
1118 	cmask_walk_op2(ref->src->bits, ref->base, ref->nr_cids,
1119 		       src->bits, src->base, src->nr_cids, CMASK_OP2_REF_COPY);
1120 }
1121 
1122 /**
1123  * scx_cmask_ref_from_cpumask - Populate @ref's arena cmask from a cpumask
1124  * @ref: kern-bound ref, see scx_cmask_ref_init_kern()
1125  * @cpumask: cpus to translate into cids
1126  *
1127  * Write @ref's active range one word at a time, setting each cid's bit when
1128  * its cpu is in @cpumask. Offsets and length come from @ref's trusted geometry
1129  * and stores use WRITE_ONCE since BPF may read concurrently, so the arena
1130  * header is never read.
1131  */
scx_cmask_ref_from_cpumask(const struct scx_cmask_ref * ref,const struct cpumask * cpumask)1132 void scx_cmask_ref_from_cpumask(const struct scx_cmask_ref *ref,
1133 				const struct cpumask *cpumask)
1134 {
1135 	struct scx_cmask *m = ref->src;
1136 	u32 base = ref->base, nr_cids = ref->nr_cids;
1137 	u32 wi, nr_words;
1138 
1139 	if (!nr_cids)
1140 		return;
1141 
1142 	nr_words = (base + nr_cids - 1) / 64 - base / 64 + 1;
1143 	for (wi = 0; wi < nr_words; wi++) {
1144 		u32 word_first_cid = (base / 64 + wi) * 64;
1145 		u64 word = 0;
1146 		u32 bit;
1147 
1148 		for (bit = 0; bit < 64; bit++) {
1149 			u32 cid = word_first_cid + bit;
1150 
1151 			if (cid < base || cid >= base + nr_cids)
1152 				continue;
1153 			if (cpumask_test_cpu(__scx_cid_to_cpu(cid), cpumask))
1154 				word |= BIT_U64(bit);
1155 		}
1156 		WRITE_ONCE(m->bits[wi], word);
1157 	}
1158 }
1159 
scx_cid_kfunc_init(void)1160 int scx_cid_kfunc_init(void)
1161 {
1162 	return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &scx_kfunc_set_init_cids) ?:
1163 		register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &scx_kfunc_set_cid) ?:
1164 		register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &scx_kfunc_set_cid) ?:
1165 		register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &scx_kfunc_set_cid);
1166 }
1167