xref: /linux/tools/sched_ext/scx_qmap.bpf.c (revision 3bd46666cfe51e2bd333fb46a0234694ca594230)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * scx_qmap: a demonstration and testing scheduler for sched_ext features.
4  *
5  * A simple scheduler that exercises a broad set of sched_ext features. Unlikely
6  * to be useful for real workloads. It demonstrates:
7  *
8  * - BPF-side queueing using TIDs.
9  * - BPF arena for scheduler state.
10  * - Core-sched support.
11  * - Hierarchical sub-scheduling: delegating cpus to child cgroup schedulers.
12  *
13  * Base design: Five FIFOs (arena-backed doubly-linked lists through per-task
14  * context). A task is assigned to a FIFO by its compound weight. Each cpu
15  * round-robins the FIFOs, dispatching more from higher ones.
16  *
17  * Sub-scheduling: Any qmap sched can delegate cpus to its own child cgroup
18  * schedulers and keep the rest for its tasks. Terminology:
19  *
20  *   excl   - A cpu the delegatee owns wholly (ENQ_IMMED|ENQ|PREEMPT).
21  *   shared - A cpu delegated as ENQ_IMMED only. Time-shared.
22  *   held_excl / held_shared - What this node was handed by its parent.
23  *            held-excl cpus are re-delegatable. A held-shared cpu is a
24  *            time-share that stays self-local.
25  *   self   - The excl cpus the node kept for itself, plus all of held_shared.
26  *   owner  - Who holds a cid - a child slot, CID_SELF, or CID_NONE.
27  *   avail  - Cpus whose caps are in effect, per ops.sub_ecaps_updated().
28  *   usable - self AND avail. Placement decisions use this: self is the
29  *            delegation split and can run ahead of what the cpus honor.
30  *
31  * The scheduler splits its held-excl cpus among self and the children in
32  * proportion to each node's cpu.weight, handing each the floor of its share as
33  * excl cpus. The leftover from rounding forms a shared pool the round-robin
34  * timer hands around. With no excl cpu to delegate, the node evicts its
35  * children.
36  *
37  * This policy is a demonstration only, not a practical one. The split
38  * considers only direct children and is not work-conserving. It only exists to
39  * drive sub-sched primitives with as simple logic as possible.
40  *
41  * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
42  * Copyright (c) 2022 Tejun Heo <tj@kernel.org>
43  * Copyright (c) 2022 David Vernet <dvernet@meta.com>
44  */
45 #include <scx/common.bpf.h>
46 
47 #include "scx_qmap.h"
48 
49 enum consts {
50 	ONE_SEC_IN_NS		= 1000000000,
51 	ONE_MSEC_IN_NS		= 1000000,
52 	LOWPRI_INTV_NS		= 10 * ONE_MSEC_IN_NS,
53 	SHARED_DSQ		= 0,
54 	HIGHPRI_DSQ		= 1,
55 	LOWPRI_DSQ		= 2,
56 	HIGHPRI_WEIGHT		= 8668,		/* this is what -20 maps to */
57 };
58 
59 char _license[] SEC("license") = "GPL";
60 
61 const volatile u64 slice_ns;
62 const volatile u32 stall_user_nth;
63 const volatile u32 stall_kernel_nth;
64 const volatile u32 dsp_inf_loop_after;
65 const volatile u32 dsp_batch;
66 const volatile bool highpri_boosting;
67 const volatile bool print_dsqs_and_events;
68 const volatile bool print_msgs;
69 const volatile u64 sub_cgroup_id;
70 const volatile s32 disallow_tgid;
71 const volatile bool suppress_dump;
72 const volatile u32 immed_stress_nth;
73 const volatile u32 max_tasks;
74 
75 /* sub-sched: period for handing the round-robin cid pool to the next child */
76 const volatile u64 round_robin_ns;
77 
78 /*
79  * Optional cid-override test harness. When cid_override_mode is non-zero,
80  * qmap_init_cids() calls scx_bpf_cid_override() with the caller-supplied arrays
81  * to exercise the kfunc's acceptance and error paths. See enum
82  * qmap_cid_override for the modes.
83  */
84 const volatile u32 cid_override_mode;
85 const volatile u32 cid_override_nr_shards;
86 
87 UEI_DEFINE(uei);
88 
89 /*
90  * All scheduler state - per-cpu context, stats counters, core-sched sequence
91  * numbers, sub-sched cgroup ids - lives in this single BPF arena map. Userspace
92  * reaches it via skel->arena->qa.
93  */
94 struct {
95 	__uint(type, BPF_MAP_TYPE_ARENA);
96 	__uint(map_flags, BPF_F_MMAPABLE);
97 	__uint(max_entries, 1 << 16);		/* upper bound in pages */
98 #if defined(__TARGET_ARCH_arm64) || defined(__aarch64__)
99 	__ulong(map_extra, 0x1ull << 32);	/* user/BPF mmap base */
100 #else
101 	__ulong(map_extra, 0x1ull << 44);
102 #endif
103 } arena SEC(".maps");
104 
105 struct qmap_arena __arena_global qa;
106 
107 /* ensure that BPF and userspace are seeing the same size for qmap_cmask */
108 _Static_assert(QMAP_CMASK_WORDS == CMASK_NR_WORDS(SCX_QMAP_MAX_CPUS),
109 	       "QMAP_CMASK_WORDS must equal CMASK_NR_WORDS(SCX_QMAP_MAX_CPUS)");
110 _Static_assert(sizeof(struct qmap_cmask) ==
111 	       struct_size_t(struct scx_cmask, bits, QMAP_CMASK_WORDS),
112 	       "qmap_cmask must be exactly sized to back a full scx_cmask");
113 
114 /* Per-queue locks. Each in its own .data section as bpf_res_spin_lock requires. */
115 __hidden struct bpf_res_spin_lock qa_q_lock0 SEC(".data.qa_q_lock0");
116 __hidden struct bpf_res_spin_lock qa_q_lock1 SEC(".data.qa_q_lock1");
117 __hidden struct bpf_res_spin_lock qa_q_lock2 SEC(".data.qa_q_lock2");
118 __hidden struct bpf_res_spin_lock qa_q_lock3 SEC(".data.qa_q_lock3");
119 __hidden struct bpf_res_spin_lock qa_q_lock4 SEC(".data.qa_q_lock4");
120 
121 static struct bpf_res_spin_lock *qa_q_lock(s32 qid)
122 {
123 	switch (qid) {
124 	case 0:	return &qa_q_lock0;
125 	case 1:	return &qa_q_lock1;
126 	case 2:	return &qa_q_lock2;
127 	case 3:	return &qa_q_lock3;
128 	case 4:	return &qa_q_lock4;
129 	default: return NULL;
130 	}
131 }
132 
133 /*
134  * If enabled, CPU performance target is set according to the queue index
135  * according to the following table.
136  */
137 static const u32 qidx_to_cpuperf_target[] = {
138 	[0] = SCX_CPUPERF_ONE * 0 / 4,
139 	[1] = SCX_CPUPERF_ONE * 1 / 4,
140 	[2] = SCX_CPUPERF_ONE * 2 / 4,
141 	[3] = SCX_CPUPERF_ONE * 3 / 4,
142 	[4] = SCX_CPUPERF_ONE * 4 / 4,
143 };
144 
145 /*
146  * Per-queue sequence numbers to implement core-sched ordering.
147  *
148  * Tail seq is assigned to each queued task and incremented. Head seq tracks the
149  * sequence number of the latest dispatched task. The distance between the a
150  * task's seq and the associated queue's head seq is called the queue distance
151  * and used when comparing two tasks for ordering. See qmap_core_sched_before().
152  */
153 
154 /*
155  * Per-task scheduling context. Allocated from the qa.task_ctxs[] slab in
156  * arena. While the task is alive the entry is referenced from task_ctx_stor;
157  * while it's free the entry sits on the free list singly-linked through
158  * @next_free.
159  *
160  * When the task is queued on one of the five priority FIFOs, @q_idx is the
161  * queue index and @q_next/@q_prev link it in the queue's doubly-linked list.
162  * @q_idx is -1 when the task isn't on any queue.
163  */
164 struct task_ctx {
165 	struct task_ctx __arena	*next_free;	/* only valid on free list */
166 	struct task_ctx __arena	*q_next;	/* queue link, NULL if tail */
167 	struct task_ctx __arena	*q_prev;	/* queue link, NULL if head */
168 	struct qmap_fifo __arena *fifo;		/* queue we're on, NULL if not queued */
169 	u64			tid;
170 	s32			pid;	/* for dump only */
171 	bool			force_local;	/* Dispatch directly to local_dsq */
172 	bool			highpri;
173 	u64			core_sched_seq;
174 	struct scx_cmask	cpus_allowed;	/* per-task affinity in cid space */
175 };
176 
177 /*
178  * Slab stride for task_ctx. cpus_allowed's flex array bits[] overlaps the
179  * tail bytes appended per entry; struct_size() gives the actual per-entry
180  * footprint.
181  */
182 #define TASK_CTX_STRIDE							\
183 	struct_size_t(struct task_ctx, cpus_allowed.bits,		\
184 		      CMASK_NR_WORDS(SCX_QMAP_MAX_CPUS))
185 
186 /* All task_ctx pointers are arena pointers. */
187 typedef struct task_ctx __arena task_ctx_t;
188 
189 /* Holds an arena pointer to the task's slab entry. */
190 struct task_ctx_stor_val {
191 	task_ctx_t		*taskc;
192 };
193 
194 struct {
195 	__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
196 	__uint(map_flags, BPF_F_NO_PREALLOC);
197 	__type(key, int);
198 	__type(value, struct task_ctx_stor_val);
199 } task_ctx_stor SEC(".maps");
200 
201 /* Protects the task_ctx slab free list. */
202 __hidden struct bpf_res_spin_lock qa_task_lock SEC(".data.qa_task_lock");
203 
204 static int qmap_spin_lock(struct bpf_res_spin_lock *lock)
205 {
206 	if (bpf_res_spin_lock(lock)) {
207 		scx_bpf_error("res_spin_lock failed");
208 		return -EBUSY;
209 	}
210 	return 0;
211 }
212 
213 /*
214  * Try prev_cid, then scan cpus_allowed AND idle_cids AND usable_cids
215  * round-robin from prev_cid + 1. Atomic claim retries on race; bounded by
216  * IDLE_PICK_RETRIES to keep the verifier's insn budget in check.
217  */
218 #define IDLE_PICK_RETRIES	16
219 
220 static s32 pick_direct_dispatch_cid(struct task_struct *p, s32 prev_cid,
221 				    task_ctx_t *taskc)
222 {
223 	u32 nr_cids = scx_bpf_nr_cids();
224 	s32 cid;
225 	u32 i;
226 
227 	if (cmask_test(prev_cid, &qa.usable_cids.mask) &&
228 	    cmask_test_and_clear(prev_cid, &qa.idle_cids.mask))
229 		return prev_cid;
230 
231 	cid = prev_cid;
232 	bpf_for(i, 0, IDLE_PICK_RETRIES) {
233 		cid = cmask_next_and2_set_wrap(&taskc->cpus_allowed,
234 					       &qa.idle_cids.mask,
235 					       &qa.usable_cids.mask, cid + 1);
236 		barrier_var(cid);
237 		if (cid >= nr_cids)
238 			return -1;
239 		if (cmask_test_and_clear(cid, &qa.idle_cids.mask))
240 			return cid;
241 	}
242 	return -1;
243 }
244 
245 /*
246  * Force a reference to the arena map. The verifier associates an arena with
247  * a program by finding an LD_IMM64 instruction that loads the arena's BPF
248  * map; programs that only use arena pointers returned from task-local
249  * storage (like qmap_select_cpu) never reference @arena directly. Without
250  * this, the verifier rejects addr_space_cast with "addr_space_cast insn
251  * can only be used in a program that has an associated arena".
252  */
253 #define QMAP_TOUCH_ARENA() do { asm volatile("" :: "r"(&arena)); } while (0)
254 
255 static task_ctx_t *lookup_task_ctx(struct task_struct *p)
256 {
257 	struct task_ctx_stor_val *v;
258 
259 	QMAP_TOUCH_ARENA();
260 
261 	v = bpf_task_storage_get(&task_ctx_stor, p, 0, 0);
262 	if (!v || !v->taskc)
263 		return NULL;
264 	return v->taskc;
265 }
266 
267 /* Append @taskc to the tail of @fifo. Must not already be queued. */
268 static void qmap_fifo_enqueue(struct qmap_fifo __arena *fifo, task_ctx_t *taskc)
269 {
270 	struct bpf_res_spin_lock *lock = qa_q_lock(fifo->idx);
271 
272 	if (!lock || qmap_spin_lock(lock))
273 		return;
274 	taskc->fifo = fifo;
275 	taskc->q_next = NULL;
276 	taskc->q_prev = fifo->tail;
277 	if (fifo->tail)
278 		fifo->tail->q_next = taskc;
279 	else
280 		fifo->head = taskc;
281 	fifo->tail = taskc;
282 	bpf_res_spin_unlock(lock);
283 }
284 
285 /* Pop the head of @fifo. Returns NULL if empty. */
286 static task_ctx_t *qmap_fifo_pop(struct qmap_fifo __arena *fifo)
287 {
288 	struct bpf_res_spin_lock *lock = qa_q_lock(fifo->idx);
289 	task_ctx_t *taskc;
290 
291 	if (!lock || qmap_spin_lock(lock))
292 		return NULL;
293 	taskc = fifo->head;
294 	if (taskc) {
295 		fifo->head = taskc->q_next;
296 		if (taskc->q_next)
297 			taskc->q_next->q_prev = NULL;
298 		else
299 			fifo->tail = NULL;
300 		taskc->q_next = NULL;
301 		taskc->q_prev = NULL;
302 		taskc->fifo = NULL;
303 	}
304 	bpf_res_spin_unlock(lock);
305 	return taskc;
306 }
307 
308 /* Remove @taskc from its fifo. No-op if not queued. */
309 static void qmap_fifo_remove(task_ctx_t *taskc)
310 {
311 	struct qmap_fifo __arena *fifo = taskc->fifo;
312 	struct bpf_res_spin_lock *lock;
313 
314 	if (!fifo)
315 		return;
316 
317 	lock = qa_q_lock(fifo->idx);
318 	if (!lock || qmap_spin_lock(lock))
319 		return;
320 
321 	/* Re-check under lock — a concurrent pop may have cleared fifo. */
322 	if (taskc->fifo != fifo) {
323 		bpf_res_spin_unlock(lock);
324 		return;
325 	}
326 
327 	if (taskc->q_next)
328 		taskc->q_next->q_prev = taskc->q_prev;
329 	else
330 		fifo->tail = taskc->q_prev;
331 	if (taskc->q_prev)
332 		taskc->q_prev->q_next = taskc->q_next;
333 	else
334 		fifo->head = taskc->q_next;
335 	taskc->q_next = NULL;
336 	taskc->q_prev = NULL;
337 	taskc->fifo = NULL;
338 	bpf_res_spin_unlock(lock);
339 }
340 
341 s32 BPF_STRUCT_OPS(qmap_select_cid, struct task_struct *p,
342 		   s32 prev_cid, u64 wake_flags)
343 {
344 	task_ctx_t *taskc;
345 	s32 cid;
346 
347 	if (!(taskc = lookup_task_ctx(p)))
348 		return prev_cid;
349 
350 	if (p->scx.weight < 2 && !(p->flags & PF_KTHREAD))
351 		return prev_cid;
352 
353 	cid = pick_direct_dispatch_cid(p, prev_cid, taskc);
354 
355 	if (cid >= 0) {
356 		taskc->force_local = true;
357 		return cid;
358 	} else {
359 		return prev_cid;
360 	}
361 }
362 
363 /*
364  * A received time-shared cid is held ENQ_IMMED-only, so inserts meant to run
365  * there must set SCX_ENQ_IMMED.
366  */
367 static u64 needs_immed(s32 cid)
368 {
369 	return qa.cid_shared[cid] ? SCX_ENQ_IMMED : 0;
370 }
371 
372 /* first cid this node does NOT hold for fault injection, -1 if none */
373 static s32 first_unavail_cid(void)
374 {
375 	s32 nr_cids = qa.nr_cids, c;
376 
377 	if (nr_cids > SCX_QMAP_MAX_CPUS) {
378 		scx_bpf_error("-ERANGE");
379 		return -1;
380 	}
381 
382 	bpf_for(c, 0, nr_cids) {
383 		if (!cmask_test(c, &qa.held_excl.mask) &&
384 		    !cmask_test(c, &qa.held_shared.mask))
385 			return c;
386 	}
387 	return -1;
388 }
389 
390 static int weight_to_idx(u32 weight)
391 {
392 	/* Coarsely map the compound weight to a FIFO. */
393 	if (weight <= 25)
394 		return 0;
395 	else if (weight <= 50)
396 		return 1;
397 	else if (weight < 200)
398 		return 2;
399 	else if (weight < 400)
400 		return 3;
401 	else
402 		return 4;
403 }
404 
405 void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags)
406 {
407 	static u32 user_cnt, kernel_cnt;
408 	task_ctx_t *taskc;
409 	int idx = weight_to_idx(p->scx.weight);
410 	s32 cid;
411 
412 	if (enq_flags & SCX_ENQ_REENQ) {
413 		u64 reason = p->scx.flags & SCX_TASK_REENQ_REASON_MASK;
414 
415 		__sync_fetch_and_add(&qa.nr_reenqueued, 1);
416 		if (scx_bpf_task_cid(p) == 0)
417 			__sync_fetch_and_add(&qa.nr_reenqueued_cid0, 1);
418 		/* cap-loss and IMMED-handback bounces, relocated below */
419 		if (reason == SCX_TASK_REENQ_CAP)
420 			__sync_fetch_and_add(&qa.nr_reenq_cap, 1);
421 		else if (reason == SCX_TASK_REENQ_IMMED)
422 			__sync_fetch_and_add(&qa.nr_reenq_immed, 1);
423 	}
424 
425 	if (p->flags & PF_KTHREAD) {
426 		if (stall_kernel_nth && !(++kernel_cnt % stall_kernel_nth))
427 			return;
428 	} else {
429 		if (stall_user_nth && !(++user_cnt % stall_user_nth))
430 			return;
431 	}
432 
433 	if (qa.test_error_cnt && !--qa.test_error_cnt)
434 		scx_bpf_error("test triggering error");
435 
436 	if (!(taskc = lookup_task_ctx(p)))
437 		return;
438 
439 	/*
440 	 * All enqueued tasks must have their core_sched_seq updated for correct
441 	 * core-sched ordering. Also, take a look at the end of qmap_dispatch().
442 	 */
443 	taskc->core_sched_seq = qa.core_sched_tail_seqs[idx]++;
444 
445 	/*
446 	 * A task of ours that can run on none of our self cids - the parent
447 	 * didn't grant them or we delegated them to children - would starve in
448 	 * SHARED/FIFO since we only pull from those on self cids.
449 	 *
450 	 * Force it onto its first allowed cid's local DSQ with SCX_ENQ_RESCUE.
451 	 * If we hold ENQ on that cid it runs. Otherwise the kernel diverts the
452 	 * task to its rescue path. IMMED would turn the insert into a legal
453 	 * placement on a time-shared cid and the kernel would bounce it back
454 	 * here instead of rescuing it.
455 	 */
456 	if (!cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask)) {
457 		s32 c = cmask_next_set_wrap(&taskc->cpus_allowed, 0);
458 
459 		if (c >= 0 && c < scx_bpf_nr_cids()) {
460 			taskc->force_local = false;
461 			__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
462 			scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | c, slice_ns,
463 					   enq_flags | SCX_ENQ_RESCUE);
464 			return;
465 		}
466 	}
467 
468 	/*
469 	 * Fault injection: deliberately dispatch one of our own tasks to a cid
470 	 * we don't hold. The inserts carry SCX_ENQ_RESCUE and divert to the
471 	 * kernel rescue path, a deterministic rescue-traffic generator. Under
472 	 * -B 0 the kernel cap check rejects and re-enqueues them instead, so
473 	 * nr_inject_attempts tracks nr_reenq_cap 1:1 and proves delivery-time
474 	 * enforcement. Throttled.
475 	 */
476 	if (qa.inject_mode == QMAP_INJ_WRONG_CID && p->nr_cpus_allowed > 1 &&
477 	    !(enq_flags & SCX_ENQ_REENQ)) {
478 		static u32 inj_cnt;
479 
480 		if (!(++inj_cnt % 64)) {
481 			s32 bad = first_unavail_cid();
482 
483 			if (bad >= 0 && cmask_test(bad, &taskc->cpus_allowed)) {
484 				__sync_fetch_and_add(&qa.nr_inject_attempts, 1);
485 				__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
486 				scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | bad, slice_ns,
487 						   enq_flags | SCX_ENQ_RESCUE);
488 				return;
489 			}
490 		}
491 	}
492 
493 	/*
494 	 * IMMED stress testing: Every immed_stress_nth'th enqueue, dispatch
495 	 * directly to prev_cpu's local DSQ even when busy to force dsq->nr > 1
496 	 * and exercise the kernel IMMED reenqueue trigger paths.
497 	 */
498 	if (immed_stress_nth && !(enq_flags & SCX_ENQ_REENQ)) {
499 		static u32 immed_stress_cnt;
500 
501 		if (!(++immed_stress_cnt % immed_stress_nth)) {
502 			taskc->force_local = false;
503 			scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | scx_bpf_task_cid(p),
504 					   slice_ns, enq_flags);
505 			return;
506 		}
507 	}
508 
509 	/*
510 	 * If qmap_select_cid() is telling us to or this is the last runnable
511 	 * task on the CPU, enqueue locally.
512 	 */
513 	if (taskc->force_local) {
514 		taskc->force_local = false;
515 		scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, slice_ns,
516 				   enq_flags | needs_immed(scx_bpf_task_cid(p)));
517 		return;
518 	}
519 
520 	/* see lowpri_timerfn() */
521 	if (__COMPAT_has_generic_reenq() &&
522 	    p->scx.weight < 2 && !(p->flags & PF_KTHREAD) && !(enq_flags & SCX_ENQ_REENQ)) {
523 		scx_bpf_dsq_insert(p, LOWPRI_DSQ, slice_ns, enq_flags);
524 		return;
525 	}
526 
527 	/* if select_cid() wasn't called, try direct dispatch */
528 	if (!__COMPAT_is_enq_cpu_selected(enq_flags) &&
529 	    (cid = pick_direct_dispatch_cid(p, scx_bpf_task_cid(p), taskc)) >= 0) {
530 		__sync_fetch_and_add(&qa.nr_ddsp_from_enq, 1);
531 		scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | cid, slice_ns,
532 				   enq_flags | needs_immed(cid));
533 		return;
534 	}
535 
536 	/*
537 	 * If the task was re-enqueued due to the CPU being preempted by a
538 	 * higher priority scheduling class, just re-enqueue the task directly
539 	 * on the global DSQ. As we want another CPU to pick it up, find and
540 	 * kick an idle cid.
541 	 */
542 	if (enq_flags & SCX_ENQ_REENQ) {
543 		s32 cid;
544 
545 		scx_bpf_dsq_insert(p, SHARED_DSQ, 0, enq_flags);
546 		cid = cmask_next_and2_set_wrap(&taskc->cpus_allowed,
547 					       &qa.idle_cids.mask,
548 					       &qa.usable_cids.mask, 0);
549 		if (cid < scx_bpf_nr_cids())
550 			scx_bpf_kick_cid(cid, SCX_KICK_IDLE);
551 		return;
552 	}
553 
554 	/* Queue on the selected FIFO. */
555 	qmap_fifo_enqueue(&qa.fifos[idx], taskc);
556 
557 	if (highpri_boosting && p->scx.weight >= HIGHPRI_WEIGHT) {
558 		taskc->highpri = true;
559 		__sync_fetch_and_add(&qa.nr_highpri_queued, 1);
560 	}
561 	__sync_fetch_and_add(&qa.nr_enqueued, 1);
562 }
563 
564 void BPF_STRUCT_OPS(qmap_dequeue, struct task_struct *p, u64 deq_flags)
565 {
566 	task_ctx_t *taskc;
567 
568 	__sync_fetch_and_add(&qa.nr_dequeued, 1);
569 	if (deq_flags & SCX_DEQ_CORE_SCHED_EXEC)
570 		__sync_fetch_and_add(&qa.nr_core_sched_execed, 1);
571 
572 	taskc = lookup_task_ctx(p);
573 	if (taskc && taskc->fifo) {
574 		if (taskc->highpri)
575 			__sync_fetch_and_sub(&qa.nr_highpri_queued, 1);
576 		qmap_fifo_remove(taskc);
577 	}
578 }
579 
580 static void update_core_sched_head_seq(struct task_struct *p)
581 {
582 	int idx = weight_to_idx(p->scx.weight);
583 	task_ctx_t *taskc;
584 
585 	if ((taskc = lookup_task_ctx(p)))
586 		qa.core_sched_head_seqs[idx] = taskc->core_sched_seq;
587 }
588 
589 /*
590  * One pass over SHARED_DSQ: rescue stranded tasks and boost highpri ones. A
591  * task whose cids were lost while it was queued in the fifos would strand on
592  * SHARED_DSQ, which is consumed only on self cids it can't run on - move it to
593  * the kernel rescue path. One whose cids were lost after the highpri cull is
594  * likewise rescued out of HIGHPRI_DSQ below.
595  *
596  * To demonstrate the use of scx_bpf_dsq_move(), implement silly selective
597  * priority boosting mechanism by moving highpri tasks to HIGHPRI_DSQ and then
598  * consuming them first. This makes minor difference only when dsp_batch is
599  * larger than 1.
600  *
601  * scx_bpf_dsq_move[_vtime]() are allowed both from ops.dispatch() and
602  * non-rq-lock holding BPF programs. As demonstration, this function is called
603  * from qmap_dispatch() and monitor_timerfn().
604  */
605 static bool scan_shared_dsq(bool from_timer)
606 {
607 	struct task_struct *p;
608 	s32 this_cid = scx_bpf_this_cid();
609 	u32 nr_cids = scx_bpf_nr_cids();
610 
611 	/* rescue strands and move highpri tasks to HIGHPRI_DSQ */
612 	bpf_for_each(scx_dsq, p, SHARED_DSQ, 0) {
613 		static u64 highpri_seq;
614 		task_ctx_t *taskc;
615 		s32 c;
616 
617 		if (!(taskc = lookup_task_ctx(p)))
618 			return false;
619 
620 		/* stranded? rescue - it can't be dispatched here either way */
621 		if (!cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask)) {
622 			c = cmask_next_set_wrap(&taskc->cpus_allowed, 0);
623 			if (c >= 0 && c < scx_bpf_nr_cids()) {
624 				__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
625 				scx_bpf_dsq_move(BPF_FOR_EACH_ITER, p, SCX_DSQ_LOCAL_ON | c,
626 						 SCX_ENQ_RESCUE);
627 			}
628 			continue;
629 		}
630 
631 		if (taskc->highpri) {
632 			/* exercise the set_*() and vtime interface too */
633 			scx_bpf_dsq_move_set_slice(BPF_FOR_EACH_ITER, slice_ns * 2);
634 			scx_bpf_dsq_move_set_vtime(BPF_FOR_EACH_ITER, highpri_seq++);
635 			scx_bpf_dsq_move_vtime(BPF_FOR_EACH_ITER, p, HIGHPRI_DSQ, 0);
636 		}
637 	}
638 
639 	/*
640 	 * Scan HIGHPRI_DSQ and dispatch until a task that can run here is
641 	 * found. Prefer this_cid if the task allows it; otherwise RR-scan the
642 	 * task's cpus_allowed starting after this_cid.
643 	 */
644 	bpf_for_each(scx_dsq, p, HIGHPRI_DSQ, 0) {
645 		task_ctx_t *taskc;
646 		bool dispatched = false;
647 		s32 cid;
648 
649 		if (!(taskc = lookup_task_ctx(p)))
650 			return false;
651 
652 		/* only run highpri tasks on cids this node can use right now */
653 		if (cmask_test(this_cid, &taskc->cpus_allowed) &&
654 		    cmask_test(this_cid, &qa.usable_cids.mask))
655 			cid = this_cid;
656 		else
657 			cid = cmask_next_and_set_wrap(&taskc->cpus_allowed,
658 						      &qa.usable_cids.mask,
659 						      this_cid + 1);
660 		if (cid >= nr_cids) {
661 			s32 c;
662 
663 			/* self cids lack caps in effect yet, leave it queued */
664 			if (cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask))
665 				continue;
666 
667 			/* stranded after the cull - rescue it from here */
668 			c = cmask_next_set_wrap(&taskc->cpus_allowed, 0);
669 			if (c >= 0 && c < nr_cids) {
670 				__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
671 				scx_bpf_dsq_move(BPF_FOR_EACH_ITER, p, SCX_DSQ_LOCAL_ON | c,
672 						 SCX_ENQ_RESCUE);
673 			}
674 			continue;
675 		}
676 
677 		if (scx_bpf_dsq_move(BPF_FOR_EACH_ITER, p, SCX_DSQ_LOCAL_ON | cid,
678 				     SCX_ENQ_PREEMPT | needs_immed(cid))) {
679 			if (cid == this_cid) {
680 				dispatched = true;
681 				__sync_fetch_and_add(&qa.nr_expedited_local, 1);
682 			} else {
683 				__sync_fetch_and_add(&qa.nr_expedited_remote, 1);
684 			}
685 			if (from_timer)
686 				__sync_fetch_and_add(&qa.nr_expedited_from_timer, 1);
687 		} else {
688 			__sync_fetch_and_add(&qa.nr_expedited_lost, 1);
689 		}
690 
691 		if (dispatched)
692 			return true;
693 	}
694 
695 	return false;
696 }
697 
698 void BPF_STRUCT_OPS(qmap_dispatch, s32 cid, struct task_struct *prev)
699 {
700 	struct task_struct *p;
701 	struct cpu_ctx __arena *cpuc;
702 	task_ctx_t *taskc;
703 	u32 batch = dsp_batch ?: 1;
704 	s32 owner, i;
705 
706 	if (scan_shared_dsq(false))
707 		return;
708 
709 	/*
710 	 * Sub-sched routing: a child-owned cid goes to its owner. Never run
711 	 * this node's own tasks on a delegated cid. Read without the guard.
712 	 */
713 	owner = qa.part.cid_owner[cid];
714 	if (owner == CID_SHARED) {
715 		/* route to the live rr holder (0 = self, runs below) */
716 		s32 pos = qa.part.rr_pos;
717 		u64 holder_cgid = (pos >= 0 && pos < MAX_PARTS) ?
718 				  qa.part.rr_slots[pos] : 0;
719 
720 		if (holder_cgid) {
721 			scx_bpf_sub_dispatch(holder_cgid);
722 			return;
723 		}
724 	} else if (owner >= 0 && owner < MAX_SUB_SCHEDS) {
725 		u64 cgid = qa.sub_sched_ctxs[owner].cgroup_id;
726 
727 		if (cgid) {
728 			if (scx_bpf_sub_dispatch(cgid))
729 				__sync_fetch_and_add(&qa.sub_sched_ctxs[owner].nr_dsps, 1);
730 			return;
731 		}
732 	}
733 
734 	if (!qa.nr_highpri_queued && scx_bpf_dsq_move_to_local(SHARED_DSQ, needs_immed(cid)))
735 		return;
736 
737 	if (dsp_inf_loop_after && qa.nr_dispatched > dsp_inf_loop_after) {
738 		/*
739 		 * PID 2 should be kthreadd which should mostly be idle and off
740 		 * the scheduler. Let's keep dispatching it to force the kernel
741 		 * to call this function over and over again.
742 		 */
743 		p = bpf_task_from_pid(2);
744 		if (p) {
745 			scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, slice_ns, 0);
746 			bpf_task_release(p);
747 			return;
748 		}
749 	}
750 
751 	cpuc = &qa.cpu_ctxs[scx_bpf_this_cid()];
752 
753 	for (i = 0; i < 5; i++) {
754 		/* Advance the dispatch cursor and pick the fifo. */
755 		if (!cpuc->dsp_cnt) {
756 			cpuc->dsp_idx = (cpuc->dsp_idx + 1) % 5;
757 			cpuc->dsp_cnt = 1 << cpuc->dsp_idx;
758 		}
759 
760 		/* Dispatch or advance. */
761 		bpf_repeat(BPF_MAX_LOOPS) {
762 			task_ctx_t *taskc;
763 
764 			taskc = qmap_fifo_pop(&qa.fifos[cpuc->dsp_idx]);
765 			if (!taskc)
766 				break;
767 
768 			p = scx_bpf_tid_to_task(taskc->tid);
769 			if (!p)
770 				continue;
771 
772 			if (taskc->highpri)
773 				__sync_fetch_and_sub(&qa.nr_highpri_queued, 1);
774 
775 			update_core_sched_head_seq(p);
776 			__sync_fetch_and_add(&qa.nr_dispatched, 1);
777 
778 			scx_bpf_dsq_insert(p, SHARED_DSQ, slice_ns, 0);
779 
780 			/*
781 			 * scx_qmap uses a global BPF queue that any CPU's
782 			 * dispatch can pop from. If this CPU popped a task that
783 			 * can't run here, it gets stranded on SHARED_DSQ after
784 			 * consume_dispatch_q() skips it. Kick the task's home
785 			 * CPU so it drains SHARED_DSQ.
786 			 *
787 			 * There's a race between the pop and the flush of the
788 			 * buffered dsq_insert:
789 			 *
790 			 *  CPU 0 (dispatching)      CPU 1 (home, idle)
791 			 *  ~~~~~~~~~~~~~~~~~~~      ~~~~~~~~~~~~~~~~~~~
792 			 *  pop from BPF queue
793 			 *  dsq_insert(buffered)
794 			 *                           balance:
795 			 *                             SHARED_DSQ empty
796 			 *                             BPF queue empty
797 			 *                             -> goes idle
798 			 *  flush -> on SHARED
799 			 *  kick CPU 1
800 			 *                           wakes, drains task
801 			 *
802 			 * The kick prevents indefinite stalls but a per-CPU
803 			 * kthread like ksoftirqd can be briefly stranded when
804 			 * its home CPU enters idle with softirq pending,
805 			 * triggering:
806 			 *
807 			 *  "NOHZ tick-stop error: local softirq work is pending, handler #N!!!"
808 			 *
809 			 * from report_idle_softirq(). The kick lands shortly
810 			 * after and the home CPU drains the task. This could be
811 			 * avoided by e.g. dispatching pinned tasks to local or
812 			 * global DSQs, but the current code is left as-is to
813 			 * document this class of issue -- other schedulers
814 			 * seeing similar warnings can use this as a reference.
815 			 */
816 			if (!cmask_test(cid, &taskc->cpus_allowed))
817 				scx_bpf_kick_cid(scx_bpf_task_cid(p), 0);
818 			batch--;
819 			cpuc->dsp_cnt--;
820 			if (!batch || !scx_bpf_dispatch_nr_slots()) {
821 				if (scan_shared_dsq(false) ||
822 				    scx_bpf_dsq_move_to_local(SHARED_DSQ, needs_immed(cid)))
823 					return;
824 				goto prev;
825 			}
826 			if (!cpuc->dsp_cnt)
827 				break;
828 		}
829 
830 		cpuc->dsp_cnt = 0;
831 	}
832 
833 	if (scan_shared_dsq(false))
834 		return;
835 prev:
836 	/*
837 	 * No other tasks. @prev will keep running. Update its core_sched_seq as
838 	 * if the task were enqueued and dispatched immediately.
839 	 *
840 	 * No @prev to keep running means the CPU goes idle. If its claim was
841 	 * never used, that is not a transition and ops.update_idle() stays
842 	 * silent. Restore the claim here.
843 	 */
844 	if (prev) {
845 		taskc = lookup_task_ctx(prev);
846 		if (!taskc)
847 			return;
848 
849 		taskc->core_sched_seq =
850 			qa.core_sched_tail_seqs[weight_to_idx(prev->scx.weight)]++;
851 	} else {
852 		cmask_set(cid, &qa.idle_cids.mask);
853 	}
854 }
855 
856 void BPF_STRUCT_OPS(qmap_tick, struct task_struct *p)
857 {
858 	struct cpu_ctx __arena *cpuc = &qa.cpu_ctxs[scx_bpf_this_cid()];
859 	int idx;
860 
861 	/*
862 	 * Use the running avg of weights to select the target cpuperf level.
863 	 * This is a demonstration of the cpuperf feature rather than a
864 	 * practical strategy to regulate CPU frequency.
865 	 */
866 	cpuc->avg_weight = cpuc->avg_weight * 3 / 4 + p->scx.weight / 4;
867 	idx = weight_to_idx(cpuc->avg_weight);
868 	cpuc->cpuperf_target = qidx_to_cpuperf_target[idx];
869 
870 	scx_bpf_cidperf_set(scx_bpf_task_cid(p), cpuc->cpuperf_target);
871 }
872 
873 /*
874  * The distance from the head of the queue scaled by the weight of the queue.
875  * The lower the number, the older the task and the higher the priority.
876  */
877 static s64 task_qdist(struct task_struct *p, task_ctx_t *taskc)
878 {
879 	int idx = weight_to_idx(p->scx.weight);
880 	s64 qdist;
881 
882 	qdist = taskc->core_sched_seq - qa.core_sched_head_seqs[idx];
883 
884 	/*
885 	 * As queue index increments, the priority doubles. The queue w/ index 3
886 	 * is dispatched twice more frequently than 2. Reflect the difference by
887 	 * scaling qdists accordingly. Note that the shift amount needs to be
888 	 * flipped depending on the sign to avoid flipping priority direction.
889 	 */
890 	if (qdist >= 0)
891 		return qdist << (4 - idx);
892 	else
893 		return qdist << idx;
894 }
895 
896 /*
897  * This is called to determine the task ordering when core-sched is picking
898  * tasks to execute on SMT siblings and should encode about the same ordering as
899  * the regular scheduling path. Use the priority-scaled distances from the head
900  * of the queues to compare the two tasks which should be consistent with the
901  * dispatch path behavior.
902  */
903 bool BPF_STRUCT_OPS(qmap_core_sched_before,
904 		    struct task_struct *a, struct task_struct *b)
905 {
906 	task_ctx_t *taskc_a = lookup_task_ctx(a);
907 	task_ctx_t *taskc_b = lookup_task_ctx(b);
908 
909 	/*
910 	 * A task delegated to a sub-scheduler has no task_ctx here. Order such
911 	 * pairs by the kernel's default ordering - a running task after every
912 	 * waiting task, then by runnable_at.
913 	 */
914 	if (!taskc_a || !taskc_b) {
915 		if (a->on_cpu != b->on_cpu)
916 			return b->on_cpu;
917 		return time_before(a->scx.runnable_at, b->scx.runnable_at);
918 	}
919 
920 	return task_qdist(a, taskc_a) < task_qdist(b, taskc_b);
921 }
922 
923 /*
924  * sched_switch tracepoint and cpu_release handlers are no longer needed.
925  * With SCX_OPS_ALWAYS_ENQ_IMMED, wakeup_preempt_scx() reenqueues IMMED
926  * tasks when a higher-priority scheduling class takes the CPU.
927  */
928 
929 s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init_task, struct task_struct *p,
930 			     struct scx_init_task_args *args)
931 {
932 	struct task_ctx_stor_val *v;
933 	task_ctx_t *taskc;
934 
935 	if (qa.inject_mode == QMAP_INJ_INIT_FAIL &&
936 	    !bpf_strncmp(p->comm, 6, "qmfail"))
937 		return -ENOMEM;
938 
939 	if (p->tgid == disallow_tgid)
940 		p->scx.disallow = true;
941 
942 	/* pop a slab entry off the free list */
943 	if (qmap_spin_lock(&qa_task_lock))
944 		return -EBUSY;
945 	taskc = qa.task_free_head;
946 	if (taskc)
947 		qa.task_free_head = taskc->next_free;
948 	bpf_res_spin_unlock(&qa_task_lock);
949 	if (!taskc) {
950 		scx_bpf_error("task_ctx slab exhausted (max_tasks=%u)", max_tasks);
951 		return -ENOMEM;
952 	}
953 
954 	taskc->next_free = NULL;
955 	taskc->q_next = NULL;
956 	taskc->q_prev = NULL;
957 	taskc->fifo = NULL;
958 	taskc->tid = p->scx.tid;
959 	taskc->pid = p->pid;
960 	taskc->force_local = false;
961 	taskc->highpri = false;
962 	taskc->core_sched_seq = 0;
963 	cmask_init(&taskc->cpus_allowed, 0, scx_bpf_nr_cids());
964 
965 	v = bpf_task_storage_get(&task_ctx_stor, p, NULL,
966 				 BPF_LOCAL_STORAGE_GET_F_CREATE);
967 	if (!v) {
968 		/* push back to the free list */
969 		if (!qmap_spin_lock(&qa_task_lock)) {
970 			taskc->next_free = qa.task_free_head;
971 			qa.task_free_head = taskc;
972 			bpf_res_spin_unlock(&qa_task_lock);
973 		}
974 		return -ENOMEM;
975 	}
976 	v->taskc = taskc;
977 	return 0;
978 }
979 
980 void BPF_STRUCT_OPS(qmap_exit_task, struct task_struct *p,
981 		    struct scx_exit_task_args *args)
982 {
983 	struct task_ctx_stor_val *v;
984 	task_ctx_t *taskc;
985 
986 	v = bpf_task_storage_get(&task_ctx_stor, p, NULL, 0);
987 	if (!v || !v->taskc)
988 		return;
989 	taskc = v->taskc;
990 	v->taskc = NULL;
991 
992 	if (qmap_spin_lock(&qa_task_lock))
993 		return;
994 	taskc->next_free = qa.task_free_head;
995 	qa.task_free_head = taskc;
996 	bpf_res_spin_unlock(&qa_task_lock);
997 }
998 
999 void BPF_STRUCT_OPS(qmap_dump, struct scx_dump_ctx *dctx)
1000 {
1001 	task_ctx_t *taskc;
1002 	s32 i;
1003 
1004 	QMAP_TOUCH_ARENA();
1005 
1006 	if (suppress_dump)
1007 		return;
1008 
1009 	/*
1010 	 * Walk the queue lists without locking - kfunc calls (scx_bpf_dump)
1011 	 * aren't in the verifier's kfunc_spin_allowed() list so we can't hold
1012 	 * a lock and dump. Best-effort; racing may print stale tids but the
1013 	 * walk is bounded by bpf_repeat() so it always terminates.
1014 	 */
1015 	bpf_for(i, 0, 5) {
1016 		scx_bpf_dump("QMAP FIFO[%d]:", i);
1017 		taskc = qa.fifos[i].head;
1018 		bpf_repeat(4096) {
1019 			if (!taskc)
1020 				break;
1021 			scx_bpf_dump(" %d:%llu", taskc->pid, taskc->tid);
1022 			taskc = taskc->q_next;
1023 		}
1024 		scx_bpf_dump("\n");
1025 	}
1026 }
1027 
1028 void BPF_STRUCT_OPS(qmap_dump_cid, struct scx_dump_ctx *dctx, s32 cid, bool idle)
1029 {
1030 	struct cpu_ctx __arena *cpuc = &qa.cpu_ctxs[cid];
1031 
1032 	if (suppress_dump || idle)
1033 		return;
1034 
1035 	scx_bpf_dump("QMAP: dsp_idx=%llu dsp_cnt=%llu avg_weight=%u cpuperf_target=%u",
1036 		     cpuc->dsp_idx, cpuc->dsp_cnt, cpuc->avg_weight,
1037 		     cpuc->cpuperf_target);
1038 }
1039 
1040 void BPF_STRUCT_OPS(qmap_dump_task, struct scx_dump_ctx *dctx, struct task_struct *p)
1041 {
1042 	struct task_ctx_stor_val *v;
1043 	task_ctx_t *taskc;
1044 
1045 	QMAP_TOUCH_ARENA();
1046 
1047 	if (suppress_dump)
1048 		return;
1049 	v = bpf_task_storage_get(&task_ctx_stor, p, NULL, 0);
1050 	if (!v || !v->taskc)
1051 		return;
1052 	taskc = v->taskc;
1053 
1054 	scx_bpf_dump("QMAP: force_local=%d core_sched_seq=%llu",
1055 		     taskc->force_local, taskc->core_sched_seq);
1056 }
1057 
1058 s32 BPF_STRUCT_OPS(qmap_cpuctl_init, struct cgroup *cgrp, struct scx_cgroup_init_args *args)
1059 {
1060 	QMAP_TOUCH_ARENA();
1061 
1062 	if (print_msgs)
1063 		bpf_printk("CGRP INIT %llu weight=%u period=%lu quota=%ld burst=%lu",
1064 			   cgrp->kn->id, args->weight, args->bw_period_us,
1065 			   args->bw_quota_us, args->bw_burst_us);
1066 
1067 	if (qa.inject_mode == QMAP_INJ_CGRP_INIT_FAIL) {
1068 		char name[7] = {};
1069 
1070 		bpf_probe_read_kernel_str(name, sizeof(name), cgrp->kn->name);
1071 		if (!bpf_strncmp(name, 6, "qmfail"))
1072 			return -ENOMEM;
1073 	}
1074 
1075 	return 0;
1076 }
1077 
1078 static void redistribute(void);
1079 
1080 void BPF_STRUCT_OPS(qmap_cpuctl_set_weight, struct cgroup *cgrp, u32 weight)
1081 {
1082 	u64 cgid = cgrp->kn->id;
1083 	s32 i;
1084 
1085 	QMAP_TOUCH_ARENA();
1086 
1087 	if (print_msgs)
1088 		bpf_printk("CGRP SET %llu weight=%u", cgid, weight);
1089 
1090 	/*
1091 	 * Knobs belong to the parent, so this op carries the child subs'
1092 	 * attach point weights. Adjust the matching sub's share of the cid
1093 	 * partition. Other cgroups don't participate in the split.
1094 	 */
1095 	for (i = 0; i < MAX_SUB_SCHEDS; i++) {
1096 		if (qa.sub_sched_ctxs[i].cgroup_id != cgid)
1097 			continue;
1098 		if (qa.sub_sched_ctxs[i].weight != weight) {
1099 			qa.sub_sched_ctxs[i].weight = weight;
1100 			redistribute();
1101 		}
1102 		break;
1103 	}
1104 }
1105 
1106 void BPF_STRUCT_OPS(qmap_cpuctl_set_bandwidth, struct cgroup *cgrp, u64 period_us,
1107 		    u64 quota_us, u64 burst_us)
1108 {
1109 	if (print_msgs)
1110 		bpf_printk("CGRP SET %llu period=%lu quota=%ld burst=%lu",
1111 			   cgrp->kn->id, period_us, quota_us, burst_us);
1112 }
1113 
1114 void BPF_STRUCT_OPS(qmap_cpuctl_move, struct task_struct *p, struct cgroup *from,
1115 		    struct cgroup *to)
1116 {
1117 	if (print_msgs)
1118 		bpf_printk("CGRP MOVE %d %llu -> %llu",
1119 			   p->pid, from->kn->id, to->kn->id);
1120 }
1121 
1122 void BPF_STRUCT_OPS(qmap_update_idle, s32 cid, bool idle)
1123 {
1124 	QMAP_TOUCH_ARENA();
1125 
1126 	/*
1127 	 * The kernel delivers update_idle() for every cid this node holds
1128 	 * SCX_CAP_BASE on. Track every cid's idle state regardless of
1129 	 * delegation: the direct-dispatch pick masks idle_cids with usable_cids
1130 	 * at selection, so a cid already idle when it returns to self needs no
1131 	 * reseed here.
1132 	 */
1133 	if (idle)
1134 		cmask_set(cid, &qa.idle_cids.mask);
1135 	else
1136 		cmask_clear(cid, &qa.idle_cids.mask);
1137 }
1138 
1139 void BPF_STRUCT_OPS(qmap_set_cmask, struct task_struct *p,
1140 		    const struct scx_cmask *cmask_in)
1141 {
1142 	struct scx_cmask __arena *cmask = (struct scx_cmask __arena *)(long)cmask_in;
1143 	task_ctx_t *taskc;
1144 
1145 	taskc = lookup_task_ctx(p);
1146 	if (!taskc)
1147 		return;
1148 	cmask_copy(&taskc->cpus_allowed, cmask);
1149 }
1150 
1151 struct monitor_timer {
1152 	struct bpf_timer timer;
1153 };
1154 
1155 struct {
1156 	__uint(type, BPF_MAP_TYPE_ARRAY);
1157 	__uint(max_entries, 1);
1158 	__type(key, u32);
1159 	__type(value, struct monitor_timer);
1160 } monitor_timer SEC(".maps");
1161 
1162 /*
1163  * Aggregate cidperf across the first nr_online_cids cids. Post-hotplug
1164  * the first-N-are-online invariant drifts, so some cap/cur values may
1165  * be stale. For this demo monitor that's fine; the scheduler exits on
1166  * the enable-time hotplug_seq mismatch and userspace restarts, which
1167  * rebuilds the layout.
1168  */
1169 static void monitor_cpuperf(void)
1170 {
1171 	u32 nr_online = scx_bpf_nr_online_cids();
1172 	u64 cap_sum = 0, cur_sum = 0, cur_min = SCX_CPUPERF_ONE, cur_max = 0;
1173 	u64 target_sum = 0, target_min = SCX_CPUPERF_ONE, target_max = 0;
1174 	s32 cid;
1175 
1176 	QMAP_TOUCH_ARENA();
1177 
1178 	bpf_for(cid, 0, nr_online) {
1179 		struct cpu_ctx __arena *cpuc = &qa.cpu_ctxs[cid];
1180 		u32 cap = scx_bpf_cidperf_cap(cid);
1181 		u32 cur = scx_bpf_cidperf_cur(cid);
1182 		u32 target;
1183 
1184 		cur_min = cur < cur_min ? cur : cur_min;
1185 		cur_max = cur > cur_max ? cur : cur_max;
1186 
1187 		cur_sum += (u64)cur * cap / SCX_CPUPERF_ONE;
1188 		cap_sum += cap;
1189 
1190 		target = cpuc->cpuperf_target;
1191 		target_sum += target;
1192 		target_min = target < target_min ? target : target_min;
1193 		target_max = target > target_max ? target : target_max;
1194 	}
1195 
1196 	if (!nr_online || !cap_sum)
1197 		return;
1198 
1199 	qa.cpuperf_min = cur_min;
1200 	qa.cpuperf_avg = cur_sum * SCX_CPUPERF_ONE / cap_sum;
1201 	qa.cpuperf_max = cur_max;
1202 
1203 	qa.cpuperf_target_min = target_min;
1204 	qa.cpuperf_target_avg = target_sum / nr_online;
1205 	qa.cpuperf_target_max = target_max;
1206 }
1207 
1208 /*
1209  * Dump the currently queued tasks in the shared DSQ to demonstrate the usage of
1210  * scx_bpf_dsq_nr_queued() and DSQ iterator. Raise the dispatch batch count to
1211  * see meaningful dumps in the trace pipe.
1212  */
1213 static void dump_shared_dsq(void)
1214 {
1215 	struct task_struct *p;
1216 	s32 nr;
1217 
1218 	if (!(nr = scx_bpf_dsq_nr_queued(SHARED_DSQ)))
1219 		return;
1220 
1221 	bpf_printk("Dumping %d tasks in SHARED_DSQ in reverse order", nr);
1222 
1223 	bpf_rcu_read_lock();
1224 	bpf_for_each(scx_dsq, p, SHARED_DSQ, SCX_DSQ_ITER_REV)
1225 		bpf_printk("%s[%d]", p->comm, p->pid);
1226 	bpf_rcu_read_unlock();
1227 }
1228 
1229 static int monitor_timerfn(void *map, int *key, struct bpf_timer *timer)
1230 {
1231 	bpf_rcu_read_lock();
1232 	scan_shared_dsq(true);
1233 	bpf_rcu_read_unlock();
1234 
1235 	monitor_cpuperf();
1236 
1237 	if (print_dsqs_and_events) {
1238 		struct scx_event_stats events;
1239 
1240 		dump_shared_dsq();
1241 
1242 		__COMPAT_scx_bpf_events(&events, sizeof(events));
1243 
1244 		bpf_printk("%35s: %lld", "SCX_EV_SELECT_CPU_FALLBACK",
1245 			   scx_read_event(&events, SCX_EV_SELECT_CPU_FALLBACK));
1246 		bpf_printk("%35s: %lld", "SCX_EV_DISPATCH_LOCAL_DSQ_OFFLINE",
1247 			   scx_read_event(&events, SCX_EV_DISPATCH_LOCAL_DSQ_OFFLINE));
1248 		bpf_printk("%35s: %lld", "SCX_EV_DISPATCH_KEEP_LAST",
1249 			   scx_read_event(&events, SCX_EV_DISPATCH_KEEP_LAST));
1250 		bpf_printk("%35s: %lld", "SCX_EV_ENQ_SKIP_EXITING",
1251 			   scx_read_event(&events, SCX_EV_ENQ_SKIP_EXITING));
1252 		bpf_printk("%35s: %lld", "SCX_EV_REFILL_SLICE_DFL",
1253 			   scx_read_event(&events, SCX_EV_REFILL_SLICE_DFL));
1254 		bpf_printk("%35s: %lld", "SCX_EV_BYPASS_DURATION",
1255 			   scx_read_event(&events, SCX_EV_BYPASS_DURATION));
1256 		bpf_printk("%35s: %lld", "SCX_EV_BYPASS_DISPATCH",
1257 			   scx_read_event(&events, SCX_EV_BYPASS_DISPATCH));
1258 		bpf_printk("%35s: %lld", "SCX_EV_BYPASS_ACTIVATE",
1259 			   scx_read_event(&events, SCX_EV_BYPASS_ACTIVATE));
1260 	}
1261 
1262 	if (bpf_timer_start(timer, ONE_SEC_IN_NS, 0))
1263 		scx_bpf_error("failed to re-arm stats timer");
1264 	return 0;
1265 }
1266 
1267 struct lowpri_timer {
1268 	struct bpf_timer timer;
1269 };
1270 
1271 struct {
1272 	__uint(type, BPF_MAP_TYPE_ARRAY);
1273 	__uint(max_entries, 1);
1274 	__type(key, u32);
1275 	__type(value, struct lowpri_timer);
1276 } lowpri_timer SEC(".maps");
1277 
1278 /*
1279  * Nice 19 tasks are put into the lowpri DSQ. Every 10ms, reenq is triggered and
1280  * the tasks are transferred to SHARED_DSQ.
1281  */
1282 static int lowpri_timerfn(void *map, int *key, struct bpf_timer *timer)
1283 {
1284 	scx_bpf_dsq_reenq(LOWPRI_DSQ, 0);
1285 	if (bpf_timer_start(timer, LOWPRI_INTV_NS, 0))
1286 		scx_bpf_error("failed to re-arm lowpri timer");
1287 	return 0;
1288 }
1289 
1290 struct round_robin_timer {
1291 	struct bpf_timer timer;
1292 };
1293 
1294 struct {
1295 	__uint(type, BPF_MAP_TYPE_ARRAY);
1296 	__uint(max_entries, 1);
1297 	__type(key, u32);
1298 	__type(value, struct round_robin_timer);
1299 } round_robin_timer SEC(".maps");
1300 
1301 enum part_pending_flags {
1302 	PART_REFRESH = BIT_U64(0),
1303 	PART_REDISTRIBUTE = BIT_U64(1),
1304 };
1305 
1306 /*
1307  * Partition update synchronization. qa.part can be written from concurrent
1308  * contexts. This single-runner guard admits one writer at a time without
1309  * holding a lock across the grant/revoke kfuncs. part_pending coalesces
1310  * refresh and repartition requests that arrive while it is held.
1311  *
1312  * They live in .bss, not the arena: rr_advance() runs from a bpf_timer
1313  * callback, where the verifier rejects atomic ops on arena memory.
1314  */
1315 static u64 part_busy;
1316 static u64 part_pending;
1317 
1318 static bool part_try_start(void)
1319 {
1320 	/* set busy, report whether it was previously clear (we acquired it) */
1321 	return !__sync_fetch_and_or(&part_busy, 1);
1322 }
1323 
1324 static void part_end(void)
1325 {
1326 	__sync_fetch_and_and(&part_busy, 0);
1327 }
1328 
1329 /*
1330  * compute_partition() scratch.
1331  *
1332  * The excl-held cids are handed out in cid order: position 0..nr_excl-1 over
1333  * the held cids is split into contiguous ranges, one per participant that gets
1334  * at least one excl cid. Range k is owned by cp_range_owner[k] and ends at the
1335  * cumulative position cp_range_end[k].
1336  */
1337 static s32 cp_range_owner[MAX_PARTS];	/* exclusive range k: its owner id ... */
1338 static s32 cp_range_end[MAX_PARTS];	/* ... and the cumulative position it ends at */
1339 
1340 /* a participant in the partition: self or an attached child */
1341 struct participant {
1342 	s32 slot;	/* child slot, or CID_SELF */
1343 	u32 weight;	/* cpu.weight */
1344 };
1345 
1346 /**
1347  * place_one - assign one excl-held cid to its owner
1348  * @cid: the excl-held cid to place
1349  * @n: its position among the excl-held cids, in [0, nr_excl)
1350  * @total_excl:	how many positions are owned exclusively (the rest are shared)
1351  *
1352  * Position @n below @total_excl is owned exclusively. It falls in the range
1353  * whose cumulative end it is under, owned by cp_range_owner[]. A position at or
1354  * above @total_excl is the rounding leftover which joins the shared pool.
1355  *
1356  * A separate __noinline function to help verification.
1357  */
1358 __noinline int place_one(s32 cid, s32 n, s32 total_excl)
1359 {
1360 	s32 owner = CID_SELF, i, s;
1361 
1362 	if (cid < 0 || cid >= SCX_QMAP_MAX_CPUS || n < 0 || n >= SCX_QMAP_MAX_CPUS ||
1363 	    total_excl < 0) {
1364 		scx_bpf_error("-ERANGE");
1365 		return 0;
1366 	}
1367 
1368 	if (n < total_excl) {
1369 		for (i = 0; i < MAX_PARTS; i++) {
1370 			if (n < cp_range_end[i]) {
1371 				owner = cp_range_owner[i];
1372 				break;
1373 			}
1374 		}
1375 		qa.part.cid_owner[cid] = owner;
1376 	} else {
1377 		s = n - total_excl;
1378 		if (s < 0 || s >= MAX_PARTS) {
1379 			scx_bpf_error("-ERANGE");
1380 			return 0;
1381 		}
1382 		qa.part.shared_cids[s] = cid;
1383 		/* time-shared: dispatch resolves the live holder via rr_pos */
1384 		qa.part.cid_owner[cid] = CID_SHARED;
1385 	}
1386 	return 0;
1387 }
1388 
1389 /**
1390  * compute_partition - build the cid partition from this node's held caps
1391  *
1392  * Decide each cid's owner, the shared pool and the rr rotation. __noinline to
1393  * help verification. See the comment at the top of the file.
1394  */
1395 __noinline void compute_partition(void)
1396 {
1397 	s32 nr_cids = qa.nr_cids;
1398 	s32 nr_excl, total_excl = 0, nr_rr = 0;
1399 	s32 sum_w, i, cid, n = 0, share, self_w;
1400 	u64 cgid_snap[MAX_SUB_SCHEDS];
1401 	s32 w_snap[MAX_SUB_SCHEDS];
1402 
1403 	if (nr_cids > SCX_QMAP_MAX_CPUS) {
1404 		scx_bpf_error("-ERANGE");
1405 		return;
1406 	}
1407 
1408 	/* find out the cids we hold */
1409 	scx_bpf_sub_caps(0, SCX_CAP_ENQ, &qa.held_excl.mask);
1410 	scx_bpf_sub_caps(0, SCX_CAP_ENQ_IMMED, &qa.held_shared.mask);
1411 	cmask_andnot(&qa.held_shared.mask, &qa.held_excl.mask);	/* held only as ENQ_IMMED */
1412 
1413 	qa.part.nr_shared = 0;
1414 	qa.part.nr_rr = 0;
1415 	qa.part.rr_pos = 0;
1416 
1417 	nr_excl = cmask_weight(&qa.held_excl.mask);
1418 	qa.part.nr_excl = nr_excl;
1419 
1420 	/* no excl cid: held_shared stays self-local, the rest unheld */
1421 	if (!nr_excl) {
1422 		bpf_for(cid, 0, nr_cids) {
1423 			if (cmask_test(cid, &qa.held_shared.mask))
1424 				qa.part.cid_owner[cid] = CID_SELF;
1425 			else
1426 				qa.part.cid_owner[cid] = CID_NONE;
1427 		}
1428 		return;
1429 	}
1430 
1431 	/*
1432 	 * Snapshot membership and weights so the sum_w and share loops agree. A
1433 	 * mid-compute change would otherwise wrap nr_shared negative. The self
1434 	 * weight is fixed at the default: a cgroup's weight is its parent's
1435 	 * knob, not the scheduler's own business.
1436 	 */
1437 	self_w = 100;
1438 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1439 		cgid_snap[i] = qa.sub_sched_ctxs[i].cgroup_id;
1440 		w_snap[i] = cgid_snap[i] ? (qa.sub_sched_ctxs[i].weight ?: 100) : 0;
1441 	}
1442 
1443 	/*
1444 	 * Participants are self plus each child. Give each a fixed range/rr
1445 	 * slot: self at slot 0, child i at slot i+1.
1446 	 *
1447 	 * sum_w totals every participant's weight.
1448 	 */
1449 	sum_w = self_w;
1450 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1451 		barrier_var(sum_w);
1452 		sum_w += w_snap[i];
1453 	}
1454 
1455 	/*
1456 	 * Split [0, nr_excl) into one contiguous range per participant, each
1457 	 * the floor of its weight share. cp_range_owner[]/cp_range_end[] record
1458 	 * each range's owner and cumulative end, total_excl counts the
1459 	 * exclusive slots, and the rest (nr_excl - total_excl) are shared.
1460 	 * rr_slots[] lists every participant for the round-robin.
1461 	 */
1462 	share = (u64)nr_excl * self_w / sum_w;
1463 	total_excl += share;
1464 	cp_range_owner[0] = CID_SELF;
1465 	cp_range_end[0] = total_excl;
1466 	qa.part.rr_slots[nr_rr++] = 0;		/* self holds slot 0 (cgid 0 = no grant) */
1467 
1468 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1469 		u64 cgid = cgid_snap[i];
1470 		s32 w = w_snap[i];
1471 
1472 		barrier_var(total_excl);
1473 		share = (u64)nr_excl * w / sum_w;
1474 		total_excl += share;
1475 		cp_range_owner[i + 1] = cgid ? i : CID_NONE;
1476 		cp_range_end[i + 1] = total_excl;
1477 
1478 		if (cgid) {
1479 			barrier_var(nr_rr);
1480 			if (nr_rr < 0 || nr_rr >= MAX_PARTS) {
1481 				scx_bpf_error("-ERANGE");
1482 				return;
1483 			}
1484 			qa.part.rr_slots[nr_rr++] = cgid;
1485 		}
1486 	}
1487 
1488 	/* assign each cid: held-excl by position, the rest self/none */
1489 	bpf_for(cid, 0, nr_cids) {
1490 		if (cmask_test(cid, &qa.held_excl.mask)) {
1491 			place_one(cid, n, total_excl);
1492 			n++;
1493 			barrier_var(n);
1494 		} else if (cmask_test(cid, &qa.held_shared.mask)) {
1495 			qa.part.cid_owner[cid] = CID_SELF;	/* time-share, self-local */
1496 		} else {
1497 			qa.part.cid_owner[cid] = CID_NONE;	/* not held */
1498 		}
1499 	}
1500 
1501 	qa.part.nr_shared = nr_excl - total_excl;
1502 	qa.part.nr_rr = nr_rr;
1503 }
1504 
1505 /*
1506  * Charge elapsed wall time to each cid's current owner. Runs under the
1507  * partition guard before every ownership change and from the stats flush, so
1508  * alloc_ns[] reflects the layout that was in effect. Shared-pool time is
1509  * charged to the live round-robin holder.
1510  */
1511 static __noinline void account_alloc(void)
1512 {
1513 	u64 now = bpf_ktime_get_ns();
1514 	s32 rr_owner = CID_SELF;
1515 	s32 nr_cids = qa.nr_cids;
1516 	u64 delta;
1517 	s32 cid, i;
1518 
1519 	if (nr_cids < 0 || nr_cids > SCX_QMAP_MAX_CPUS) {
1520 		scx_bpf_error("-ERANGE");
1521 		return;
1522 	}
1523 
1524 	/* first call starts the clock */
1525 	if (!qa.alloc_ts) {
1526 		qa.alloc_ts = now;
1527 		return;
1528 	}
1529 	delta = now - qa.alloc_ts;
1530 	qa.alloc_ts = now;
1531 	qa.alloc_window_ns += delta;
1532 
1533 	/* resolve the live shared-pool holder to an owner id */
1534 	if (qa.part.nr_shared && qa.part.nr_rr) {
1535 		u32 pos = qa.part.rr_pos;
1536 		u64 cgid = pos < MAX_PARTS ? qa.part.rr_slots[pos] : 0;
1537 
1538 		if (cgid) {
1539 			rr_owner = CID_NONE;
1540 			bpf_for(i, 0, MAX_SUB_SCHEDS)
1541 				if (qa.sub_sched_ctxs[i].cgroup_id == cgid)
1542 					rr_owner = i;
1543 		}
1544 	}
1545 
1546 	bpf_for(cid, 0, nr_cids) {
1547 		s32 owner = qa.part.cid_owner[cid];
1548 
1549 		if (owner == CID_SHARED)
1550 			owner = rr_owner;
1551 		if (owner >= 0 && owner < MAX_SUB_SCHEDS)
1552 			qa.alloc_ns[owner] += delta;
1553 		else if (owner == CID_SELF)
1554 			qa.self_alloc_ns += delta;
1555 	}
1556 }
1557 
1558 /*
1559  * usable_cids = self_cids & avail_cids. The inputs have separate writers,
1560  * apply_partition() and qmap_sub_ecaps_updated(), so the result is rebuilt in
1561  * full under the partition guard, in scratch first so that readers never see
1562  * self_cids alone.
1563  */
1564 static void refresh_usable(void)
1565 {
1566 	cmask_copy(&qa.usable_scratch.mask, &qa.self_cids.mask);
1567 	cmask_and(&qa.usable_scratch.mask, &qa.avail_cids.mask);
1568 	cmask_copy(&qa.usable_cids.mask, &qa.usable_scratch.mask);
1569 }
1570 
1571 /*
1572  * apply_partition - execute the plan compute_partition() built
1573  *
1574  * Turn the owner map into the per-child, shared and self cmasks and issue the
1575  * grant/revoke kfuncs as a delta against each child's previous grant. If no
1576  * excl cid, evict every child.
1577  */
1578 __noinline void apply_partition(void)
1579 {
1580 	s32 nr_cids = qa.nr_cids;
1581 	s32 nr_shared = qa.part.nr_shared;
1582 	s32 i, cid;
1583 
1584 	if (nr_cids < 0 || nr_cids > SCX_QMAP_MAX_CPUS ||
1585 	    nr_shared < 0 || nr_shared > MAX_PARTS) {
1586 		scx_bpf_error("-ERANGE");
1587 		return;
1588 	}
1589 
1590 	/* no excl cpu: run own tasks on the held shares, evict children */
1591 	if (!qa.part.nr_excl) {
1592 		cmask_copy(&qa.self_cids.mask, &qa.held_shared.mask);
1593 		refresh_usable();
1594 		bpf_for(i, 0, MAX_SUB_SCHEDS)
1595 			if (qa.sub_sched_ctxs[i].cgroup_id)
1596 				scx_bpf_sub_kill(qa.sub_sched_ctxs[i].cgroup_id,
1597 						 "parent holds no excl cpu to distribute");
1598 		return;
1599 	}
1600 
1601 	/*
1602 	 * Snapshot the old pool. The per-child revoke below clears ENQ_IMMED on
1603 	 * the previously-granted pool, so a cid that left the pool (now a
1604 	 * sibling's excl) doesn't keep a stale ENQ_IMMED on its last holder.
1605 	 */
1606 	cmask_copy(&qa.prev_rr_cids.mask, &qa.rr_cids.mask);
1607 
1608 	/* turn the owner map into the rr pool, per-child excl, and self sets */
1609 	cmask_init(&qa.rr_cids.mask, 0, nr_cids);
1610 	cmask_init(&qa.self_cids.mask, 0, nr_cids);
1611 
1612 	/* snapshot each child's grant, then rebuild the new sets below */
1613 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1614 		cmask_copy(&qa.sub_sched_ctxs[i].prev_granted.mask,
1615 			   &qa.sub_sched_ctxs[i].granted_cids.mask);
1616 		cmask_init(&qa.sub_sched_ctxs[i].granted_cids.mask, 0, nr_cids);
1617 	}
1618 
1619 	bpf_for(i, 0, nr_shared)
1620 		cmask_set(qa.part.shared_cids[i], &qa.rr_cids.mask);
1621 	bpf_for(cid, 0, nr_cids) {
1622 		s32 o = qa.part.cid_owner[cid];
1623 
1624 		if (cmask_test(cid, &qa.rr_cids.mask))
1625 			continue;
1626 		if (o >= 0 && o < MAX_SUB_SCHEDS)
1627 			cmask_set(cid, &qa.sub_sched_ctxs[o].granted_cids.mask);
1628 		else if (o == CID_SELF)
1629 			cmask_set(cid, &qa.self_cids.mask);
1630 	}
1631 	refresh_usable();
1632 
1633 	/*
1634 	 * Apply each child's exclusive cids as a delta against its previous
1635 	 * grant. Separately clear the previous shared grant (ENQ_IMMED on the
1636 	 * old pool), covering cids still pooled and cids that left for a
1637 	 * sibling's excl. The current holder is granted the new pool below.
1638 	 */
1639 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1640 		struct sub_sched_ctx __arena *ssc = &qa.sub_sched_ctxs[i];
1641 		u64 cgid = ssc->cgroup_id;
1642 
1643 		if (!cgid)
1644 			continue;
1645 
1646 		cmask_copy(&qa.to_revoke_cids.mask, &ssc->prev_granted.mask);
1647 		cmask_andnot(&qa.to_revoke_cids.mask, &ssc->granted_cids.mask);
1648 		cmask_copy(&qa.to_grant_cids.mask, &ssc->granted_cids.mask);
1649 		cmask_andnot(&qa.to_grant_cids.mask, &ssc->prev_granted.mask);
1650 
1651 		scx_bpf_sub_revoke(cgid, SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1652 				   &qa.prev_rr_cids.mask);
1653 		scx_bpf_sub_revoke(cgid, SCX_CAP_ENQ | SCX_CAP_PREEMPT |
1654 				   SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1655 				   &qa.to_revoke_cids.mask);
1656 		scx_bpf_sub_grant(cgid, SCX_CAP_ENQ | SCX_CAP_PREEMPT |
1657 				  SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1658 				  &qa.to_grant_cids.mask, NULL);
1659 	}
1660 
1661 	/* the current holder of the shared pool gets ENQ_IMMED on all of it */
1662 	if (nr_shared) {
1663 		s32 pos = qa.part.rr_pos;
1664 		u64 holder_cgid;
1665 
1666 		if (pos < 0 || pos >= MAX_PARTS) {
1667 			scx_bpf_error("-ERANGE");
1668 			return;
1669 		}
1670 
1671 		holder_cgid = qa.part.rr_slots[pos];	/* 0 = self, nothing to grant */
1672 		if (holder_cgid)
1673 			scx_bpf_sub_grant(holder_cgid,
1674 					  SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1675 					  &qa.rr_cids.mask, NULL);
1676 	}
1677 }
1678 
1679 /**
1680  * execute_partition - Run pending partition updates
1681  *
1682  * The rr timer is the backstop if the loop reaches its iteration limit.
1683  */
1684 static void execute_partition(void)
1685 {
1686 	u64 pending;
1687 	s32 i;
1688 
1689 	bpf_for(i, 0, 1024) {
1690 		if (!part_try_start())
1691 			break;
1692 
1693 		pending = __sync_fetch_and_and(&part_pending, 0);
1694 		if (pending & PART_REDISTRIBUTE) {
1695 			/* charge elapsed time before repartitioning */
1696 			account_alloc();
1697 			compute_partition();
1698 			apply_partition();
1699 		} else if (pending & PART_REFRESH) {
1700 			refresh_usable();
1701 		}
1702 
1703 		/*
1704 		 * Requests are published before trying the guard. Releasing it
1705 		 * before checking pending work ensures a racing request is
1706 		 * either observed here or handled by a caller that acquires the
1707 		 * guard.
1708 		 */
1709 		part_end();
1710 		if (!__sync_fetch_and_or(&part_pending, 0))
1711 			break;
1712 	}
1713 }
1714 
1715 static void redistribute(void)
1716 {
1717 	__sync_fetch_and_or(&part_pending, PART_REDISTRIBUTE);
1718 	execute_partition();
1719 }
1720 
1721 /*
1722  * Userspace pokes this (PROG_RUN) to bring alloc_ns[] current before reading
1723  * it for the stats display. Skipping when the partition guard is held is
1724  * fine - alloc_ts is untouched, so the elapsed time is charged next time.
1725  */
1726 SEC("syscall")
1727 int flush_alloc(void *ctx)
1728 {
1729 	if (part_try_start()) {
1730 		account_alloc();
1731 		part_end();
1732 		execute_partition();
1733 	}
1734 	return 0;
1735 }
1736 
1737 /*
1738  * Hand the shared pool to the next participant in the rotation. Self's turn
1739  * just revokes the pool back to this sched. A child's turn grants it ENQ_IMMED
1740  * on the entire pool. As only excl-held cids are time-shared, a wall-clock
1741  * rotation works. Driven by the round-robin timer.
1742  */
1743 static void rr_advance(void)
1744 {
1745 	s32 nr_shared, old_pos, new_pos;
1746 	u64 old_cgid, new_cgid;
1747 	u32 nr_rr;		/* unsigned for % */
1748 
1749 	/* a redistribute holds the partition and rebuilds the pool, so skip */
1750 	if (!part_try_start())
1751 		return;
1752 
1753 	nr_rr = qa.part.nr_rr;
1754 	nr_shared = qa.part.nr_shared;
1755 
1756 	if (nr_shared < 0 || nr_shared > MAX_PARTS) {
1757 		scx_bpf_error("-ERANGE");
1758 		return;
1759 	}
1760 
1761 	if (nr_shared && nr_rr >= 2) {
1762 		/* close out the outgoing holder's pool time */
1763 		account_alloc();
1764 
1765 		old_pos = qa.part.rr_pos;
1766 		new_pos = (old_pos + 1) % nr_rr;
1767 		old_cgid = qa.part.rr_slots[old_pos];
1768 		new_cgid = qa.part.rr_slots[new_pos];
1769 		qa.part.rr_pos = new_pos;
1770 
1771 		/*
1772 		 * Move the ENQ_IMMED cap to the next participant. The shared
1773 		 * cids stay marked CID_SHARED. qmap_dispatch() resolves the
1774 		 * live holder via rr_pos without the guard, so a dispatch
1775 		 * racing this handoff may reenqueue a task once. Harmless for a
1776 		 * time-share.
1777 		 */
1778 		if (old_cgid)
1779 			scx_bpf_sub_revoke(old_cgid,
1780 					   SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1781 					   &qa.rr_cids.mask);
1782 		if (new_cgid)
1783 			scx_bpf_sub_grant(new_cgid,
1784 					  SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1785 					  &qa.rr_cids.mask, NULL);
1786 	}
1787 
1788 	part_end();
1789 
1790 	execute_partition();
1791 }
1792 
1793 /* advance the time-shared cid pool every round_robin_ns */
1794 static int round_robin_timerfn(void *map, int *key, struct bpf_timer *timer)
1795 {
1796 	rr_advance();
1797 	if (bpf_timer_start(timer, round_robin_ns, 0))
1798 		scx_bpf_error("failed to re-arm round-robin timer");
1799 	return 0;
1800 }
1801 
1802 /*
1803  * Custom cid layout for the cid-override test. On invalid input the kfunc
1804  * scx_error()s and aborts the scheduler.
1805  */
1806 s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init_cids)
1807 {
1808 	u32 nr_cpu_ids = scx_bpf_nr_cpu_ids();
1809 
1810 	if (!cid_override_mode)
1811 		return 0;
1812 
1813 	/* the arena arrays are sized SCX_QMAP_MAX_CPUS */
1814 	if (nr_cpu_ids > SCX_QMAP_MAX_CPUS) {
1815 		scx_bpf_error("nr_cpu_ids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
1816 			      nr_cpu_ids, SCX_QMAP_MAX_CPUS);
1817 		return -EINVAL;
1818 	}
1819 
1820 	scx_bpf_cid_override(qa.cid_override_cpu_to_cid, nr_cpu_ids,
1821 			     qa.cid_override_shard_start, cid_override_nr_shards);
1822 	return 0;
1823 }
1824 
1825 s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init)
1826 {
1827 	u8 __arena *slab;
1828 	u32 nr_pages, key = 0, i;
1829 	u32 nr_cids, nr_cpu_ids;
1830 	struct bpf_timer *timer;
1831 	s32 ret;
1832 
1833 	nr_cids = scx_bpf_nr_cids();
1834 	nr_cpu_ids = scx_bpf_nr_cpu_ids();
1835 
1836 	if (nr_cids > SCX_QMAP_MAX_CPUS) {
1837 		scx_bpf_error("nr_cids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
1838 			      nr_cids, SCX_QMAP_MAX_CPUS);
1839 		return -EINVAL;
1840 	}
1841 	if (nr_cpu_ids > SCX_QMAP_MAX_CPUS) {
1842 		scx_bpf_error("nr_cpu_ids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
1843 			      nr_cpu_ids, SCX_QMAP_MAX_CPUS);
1844 		return -EINVAL;
1845 	}
1846 
1847 	/*
1848 	 * Allocate the task_ctx slab in arena and thread the entire slab onto
1849 	 * the free list. max_tasks is set by userspace before load. Each entry
1850 	 * is TASK_CTX_STRIDE bytes - task_ctx's trailing cpus_allowed flex
1851 	 * array extends into the stride tail.
1852 	 */
1853 	if (!max_tasks) {
1854 		scx_bpf_error("max_tasks must be > 0");
1855 		return -EINVAL;
1856 	}
1857 
1858 	nr_pages = (max_tasks * TASK_CTX_STRIDE + PAGE_SIZE - 1) / PAGE_SIZE;
1859 	slab = bpf_arena_alloc_pages(&arena, NULL, nr_pages, NUMA_NO_NODE, 0);
1860 	if (!slab) {
1861 		scx_bpf_error("failed to allocate task_ctx slab");
1862 		return -ENOMEM;
1863 	}
1864 	qa.task_ctxs = (task_ctx_t *)slab;
1865 
1866 	bpf_for(i, 0, 5)
1867 		qa.fifos[i].idx = i;
1868 
1869 	bpf_for(i, 0, max_tasks) {
1870 		task_ctx_t *cur = (task_ctx_t *)(slab + i * TASK_CTX_STRIDE);
1871 		task_ctx_t *next = (i + 1 < max_tasks) ?
1872 			(task_ctx_t *)(slab + (i + 1) * TASK_CTX_STRIDE) : NULL;
1873 		cur->next_free = next;
1874 	}
1875 	qa.task_free_head = (task_ctx_t *)slab;
1876 
1877 	/* cache the cid count, trusted to be <= SCX_QMAP_MAX_CPUS hereafter */
1878 	qa.nr_cids = nr_cids;
1879 
1880 	/* cmasks are embedded in qa, so they only need initializing */
1881 	cmask_init(&qa.idle_cids.mask, 0, nr_cids);
1882 	cmask_init(&qa.rr_cids.mask, 0, nr_cids);
1883 	cmask_init(&qa.prev_rr_cids.mask, 0, nr_cids);
1884 	cmask_init(&qa.self_cids.mask, 0, nr_cids);
1885 	cmask_init(&qa.avail_cids.mask, 0, nr_cids);
1886 	cmask_init(&qa.usable_cids.mask, 0, nr_cids);
1887 	cmask_init(&qa.to_revoke_cids.mask, 0, nr_cids);
1888 	cmask_init(&qa.to_grant_cids.mask, 0, nr_cids);
1889 	cmask_init(&qa.usable_scratch.mask, 0, nr_cids);
1890 	cmask_init(&qa.held_excl.mask, 0, nr_cids);
1891 	cmask_init(&qa.held_shared.mask, 0, nr_cids);
1892 
1893 	scx_bpf_sub_caps(0, SCX_CAP_ENQ, &qa.held_excl.mask);
1894 	scx_bpf_sub_caps(0, SCX_CAP_ENQ_IMMED, &qa.held_shared.mask);
1895 	cmask_andnot(&qa.held_shared.mask, &qa.held_excl.mask);
1896 
1897 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1898 		cmask_init(&qa.sub_sched_ctxs[i].granted_cids.mask, 0, nr_cids);
1899 		cmask_init(&qa.sub_sched_ctxs[i].prev_granted.mask, 0, nr_cids);
1900 	}
1901 
1902 	/*
1903 	 * The root starts holding every cid and gets no ecaps notifications, so
1904 	 * its avail set is fixed here. qmap_sub_ecaps_updated() maintains the
1905 	 * per-cid state as effective caps settle, and redistribute() rebuilds
1906 	 * owner and self from held caps. A non-root node starts with nothing.
1907 	 */
1908 	bpf_for(i, 0, nr_cids) {
1909 		if (!sub_cgroup_id) {
1910 			cmask_set(i, &qa.self_cids.mask);
1911 			cmask_set(i, &qa.avail_cids.mask);
1912 			cmask_set(i, &qa.usable_cids.mask);
1913 			qa.part.cid_owner[i] = CID_SELF;
1914 		} else {
1915 			qa.part.cid_owner[i] = CID_NONE;
1916 		}
1917 	}
1918 	qa.part.nr_shared = 0;
1919 
1920 	ret = scx_bpf_create_dsq(SHARED_DSQ, -1);
1921 	if (ret) {
1922 		scx_bpf_error("failed to create DSQ %d (%d)", SHARED_DSQ, ret);
1923 		return ret;
1924 	}
1925 
1926 	ret = scx_bpf_create_dsq(HIGHPRI_DSQ, -1);
1927 	if (ret) {
1928 		scx_bpf_error("failed to create DSQ %d (%d)", HIGHPRI_DSQ, ret);
1929 		return ret;
1930 	}
1931 
1932 	ret = scx_bpf_create_dsq(LOWPRI_DSQ, -1);
1933 	if (ret)
1934 		return ret;
1935 
1936 	timer = bpf_map_lookup_elem(&monitor_timer, &key);
1937 	if (!timer)
1938 		return -ESRCH;
1939 	bpf_timer_init(timer, &monitor_timer, CLOCK_MONOTONIC);
1940 	bpf_timer_set_callback(timer, monitor_timerfn);
1941 	ret = bpf_timer_start(timer, ONE_SEC_IN_NS, 0);
1942 	if (ret)
1943 		return ret;
1944 
1945 	if (__COMPAT_has_generic_reenq()) {
1946 		/* see lowpri_timerfn() */
1947 		timer = bpf_map_lookup_elem(&lowpri_timer, &key);
1948 		if (!timer)
1949 			return -ESRCH;
1950 		bpf_timer_init(timer, &lowpri_timer, CLOCK_MONOTONIC);
1951 		bpf_timer_set_callback(timer, lowpri_timerfn);
1952 		ret = bpf_timer_start(timer, LOWPRI_INTV_NS, 0);
1953 		if (ret)
1954 			return ret;
1955 	}
1956 
1957 	/* sub-sched: drive the boundary-cid round-robin from a bpf timer */
1958 	timer = bpf_map_lookup_elem(&round_robin_timer, &key);
1959 	if (!timer)
1960 		return -ESRCH;
1961 	bpf_timer_init(timer, &round_robin_timer, CLOCK_MONOTONIC);
1962 	bpf_timer_set_callback(timer, round_robin_timerfn);
1963 	ret = bpf_timer_start(timer, round_robin_ns, 0);
1964 	if (ret)
1965 		return ret;
1966 
1967 	return 0;
1968 }
1969 
1970 void BPF_STRUCT_OPS(qmap_exit, struct scx_exit_info *ei)
1971 {
1972 	UEI_RECORD(uei, ei);
1973 }
1974 
1975 /*
1976  * Seed a new sub slot with the cgroup's current weight. The kernel delivers
1977  * ops.cpuctl_set_weight() only on value-changing writes, so a weight set
1978  * before the sub attached would otherwise go unnoticed.
1979  */
1980 static u32 cgrp_cur_weight(u64 cgid)
1981 {
1982 	struct cgroup_subsys_state *css;
1983 	struct cgroup *cgrp;
1984 	u32 weight = 100;
1985 
1986 	cgrp = bpf_cgroup_from_id(cgid);
1987 	if (!cgrp)
1988 		return weight;
1989 
1990 	css = BPF_CORE_READ(cgrp, subsys[cpu_cgrp_id]);
1991 	if (css) {
1992 		struct task_group *tg = container_of(css, struct task_group, css);
1993 		u32 w = BPF_CORE_READ(tg, scx.weight);
1994 
1995 		if (w)
1996 			weight = w;
1997 	}
1998 	bpf_cgroup_release(cgrp);
1999 	return weight;
2000 }
2001 
2002 s32 BPF_STRUCT_OPS(qmap_sub_attach, struct scx_sub_attach_args *args)
2003 {
2004 	s32 i;
2005 
2006 	/* as long as there is at least one excl cpu, children can attach */
2007 	if (!cmask_weight(&qa.held_excl.mask))
2008 		return -ENOSPC;
2009 
2010 	for (i = 0; i < MAX_SUB_SCHEDS; i++) {
2011 		if (qa.sub_sched_ctxs[i].cgroup_id)
2012 			continue;
2013 
2014 		qa.sub_sched_ctxs[i].cgroup_id = args->ops->sub_cgroup_id;
2015 		qa.sub_sched_ctxs[i].weight = cgrp_cur_weight(args->ops->sub_cgroup_id);
2016 		qa.nr_sub_scheds++;
2017 		bpf_printk("attaching sub-sched[%d] on %s", i, args->cgroup_path);
2018 		redistribute();
2019 		return 0;
2020 	}
2021 
2022 	return -ENOSPC;
2023 }
2024 
2025 void BPF_STRUCT_OPS(qmap_sub_detach, struct scx_sub_detach_args *args)
2026 {
2027 	s32 i;
2028 
2029 	for (i = 0; i < MAX_SUB_SCHEDS; i++) {
2030 		if (qa.sub_sched_ctxs[i].cgroup_id != args->ops->sub_cgroup_id)
2031 			continue;
2032 
2033 		qa.sub_sched_ctxs[i].cgroup_id = 0;
2034 		qa.sub_sched_ctxs[i].weight = 100;
2035 		cmask_init(&qa.sub_sched_ctxs[i].granted_cids.mask, 0, qa.nr_cids);
2036 		qa.nr_sub_scheds--;
2037 		bpf_printk("detaching sub-sched[%d] on %s", i, args->cgroup_path);
2038 		redistribute();
2039 		break;
2040 	}
2041 }
2042 
2043 void BPF_STRUCT_OPS(qmap_sub_caps_updated, const struct scx_cmask *cmask, u64 caps)
2044 {
2045 	/* our held caps changed, redistribute */
2046 	redistribute();
2047 }
2048 
2049 void BPF_STRUCT_OPS(qmap_sub_ecaps_updated, s32 cid, u64 before, u64 after)
2050 {
2051 	/*
2052 	 * Effective caps updated. Track which cids hold shared caps so a self
2053 	 * task placed there enqueues IMMED, and which cids have ENQ_IMMED in
2054 	 * effect at all (avail, see the header comment).
2055 	 */
2056 	if (after & SCX_CAP_ENQ_IMMED) {
2057 		qa.cid_shared[cid] = (after & SCX_CAP_ENQ) ? 0 : 1;
2058 		cmask_set(cid, &qa.avail_cids.mask);
2059 	} else {
2060 		qa.cid_shared[cid] = 0;
2061 		cmask_clear(cid, &qa.avail_cids.mask);
2062 	}
2063 
2064 	__sync_fetch_and_or(&part_pending, PART_REFRESH);
2065 	execute_partition();
2066 }
2067 
2068 SCX_OPS_CID_DEFINE(qmap_ops,
2069 	       .flags			= SCX_OPS_ENQ_EXITING | SCX_OPS_TID_TO_TASK,
2070 	       .select_cid		= (void *)qmap_select_cid,
2071 	       .enqueue			= (void *)qmap_enqueue,
2072 	       .dequeue			= (void *)qmap_dequeue,
2073 	       .dispatch		= (void *)qmap_dispatch,
2074 	       .tick			= (void *)qmap_tick,
2075 	       .core_sched_before	= (void *)qmap_core_sched_before,
2076 	       .set_cmask		= (void *)qmap_set_cmask,
2077 	       .update_idle		= (void *)qmap_update_idle,
2078 	       .init_task		= (void *)qmap_init_task,
2079 	       .exit_task		= (void *)qmap_exit_task,
2080 	       .dump			= (void *)qmap_dump,
2081 	       .dump_cid		= (void *)qmap_dump_cid,
2082 	       .dump_task		= (void *)qmap_dump_task,
2083 	       .cpuctl_init		= (void *)qmap_cpuctl_init,
2084 	       .cpuctl_set_weight	= (void *)qmap_cpuctl_set_weight,
2085 	       .cpuctl_set_bandwidth	= (void *)qmap_cpuctl_set_bandwidth,
2086 	       .cpuctl_move		= (void *)qmap_cpuctl_move,
2087 	       .sub_attach		= (void *)qmap_sub_attach,
2088 	       .sub_detach		= (void *)qmap_sub_detach,
2089 	       .sub_caps_updated	= (void *)qmap_sub_caps_updated,
2090 	       .sub_ecaps_updated	= (void *)qmap_sub_ecaps_updated,
2091 	       .init_cids		= (void *)qmap_init_cids,
2092 	       .init			= (void *)qmap_init,
2093 	       .exit			= (void *)qmap_exit,
2094 	       .timeout_ms		= 5000U,
2095 	       .name			= "qmap");
2096