xref: /linux/tools/sched_ext/scx_qmap.bpf.c (revision cce3618e2d4bd309966bed9930692e5d72e3a4ef)
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 static void dispatch_to_rescue(struct task_struct *p, task_ctx_t *taskc,
373 			       u64 enq_flags)
374 {
375 	u32 cid = cmask_next_set_wrap(&taskc->cpus_allowed, 0);
376 
377 	if (cid >= scx_bpf_nr_cids()) {
378 		scx_bpf_error("task %d has no allowed cid", p->pid);
379 		return;
380 	}
381 
382 	taskc->force_local = false;
383 	__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
384 	scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | cid, slice_ns,
385 			   enq_flags | SCX_ENQ_RESCUE);
386 }
387 
388 /* first cid this node does NOT hold for fault injection, -1 if none */
389 static s32 first_unavail_cid(void)
390 {
391 	s32 nr_cids = qa.nr_cids, c;
392 
393 	if (nr_cids > SCX_QMAP_MAX_CPUS) {
394 		scx_bpf_error("-ERANGE");
395 		return -1;
396 	}
397 
398 	bpf_for(c, 0, nr_cids) {
399 		if (!cmask_test(c, &qa.held_excl.mask) &&
400 		    !cmask_test(c, &qa.held_shared.mask))
401 			return c;
402 	}
403 	return -1;
404 }
405 
406 static int weight_to_idx(u32 weight)
407 {
408 	/* Coarsely map the compound weight to a FIFO. */
409 	if (weight <= 25)
410 		return 0;
411 	else if (weight <= 50)
412 		return 1;
413 	else if (weight < 200)
414 		return 2;
415 	else if (weight < 400)
416 		return 3;
417 	else
418 		return 4;
419 }
420 
421 void BPF_STRUCT_OPS(qmap_enqueue, struct task_struct *p, u64 enq_flags)
422 {
423 	static u32 user_cnt, kernel_cnt;
424 	task_ctx_t *taskc;
425 	int idx = weight_to_idx(p->scx.weight);
426 	s32 cid;
427 
428 	if (enq_flags & SCX_ENQ_REENQ) {
429 		u64 reason = p->scx.flags & SCX_TASK_REENQ_REASON_MASK;
430 
431 		__sync_fetch_and_add(&qa.nr_reenqueued, 1);
432 		if (scx_bpf_task_cid(p) == 0)
433 			__sync_fetch_and_add(&qa.nr_reenqueued_cid0, 1);
434 		/* cap-loss and IMMED-handback bounces, relocated below */
435 		if (reason == SCX_TASK_REENQ_CAP)
436 			__sync_fetch_and_add(&qa.nr_reenq_cap, 1);
437 		else if (reason == SCX_TASK_REENQ_IMMED)
438 			__sync_fetch_and_add(&qa.nr_reenq_immed, 1);
439 	}
440 
441 	if (p->flags & PF_KTHREAD) {
442 		if (stall_kernel_nth && !(++kernel_cnt % stall_kernel_nth))
443 			return;
444 	} else {
445 		if (stall_user_nth && !(++user_cnt % stall_user_nth))
446 			return;
447 	}
448 
449 	if (qa.test_error_cnt && !--qa.test_error_cnt)
450 		scx_bpf_error("test triggering error");
451 
452 	if (!(taskc = lookup_task_ctx(p)))
453 		return;
454 
455 	/*
456 	 * All enqueued tasks must have their core_sched_seq updated for correct
457 	 * core-sched ordering. Also, take a look at the end of qmap_dispatch().
458 	 */
459 	taskc->core_sched_seq = qa.core_sched_tail_seqs[idx]++;
460 
461 	if (enq_flags & SCX_ENQ_BLOCKED)
462 		__sync_fetch_and_add(&qa.nr_enq_blocked, 1);
463 
464 	/*
465 	 * A task of ours that can run on none of our self cids - the parent
466 	 * didn't grant them or we delegated them to children - would starve in
467 	 * SHARED/FIFO since we only pull from those on self cids.
468 	 *
469 	 * Force it onto its first allowed cid's local DSQ with SCX_ENQ_RESCUE.
470 	 * If we hold ENQ on that cid it runs. Otherwise the kernel diverts the
471 	 * task to its rescue path. IMMED would turn the insert into a legal
472 	 * placement on a time-shared cid and the kernel would bounce it back
473 	 * here instead of rescuing it. Do this before the blocked-donor fast
474 	 * paths, which also require an eligible self cid to make progress.
475 	 */
476 	if (!cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask)) {
477 		dispatch_to_rescue(p, taskc, enq_flags);
478 		return;
479 	}
480 
481 	/*
482 	 * SCX_OPS_ALWAYS_ENQ_IMMED makes the local insertion below implicitly
483 	 * carry SCX_ENQ_IMMED. If the CPU can't run the blocked donor immediately,
484 	 * the core returns it through ops.enqueue() with SCX_ENQ_REENQ. Inserting
485 	 * it into the same local DSQ would repeat the IMMED handback until the
486 	 * scheduler is ejected. Move reenqueued blocked donors to the shared DSQ,
487 	 * which doesn't carry SCX_ENQ_IMMED, so another CPU can consume them.
488 	 */
489 	if ((enq_flags & (SCX_ENQ_BLOCKED | SCX_ENQ_REENQ)) ==
490 	    (SCX_ENQ_BLOCKED | SCX_ENQ_REENQ)) {
491 		taskc->force_local = false;
492 		scx_bpf_dsq_insert(p, SHARED_DSQ, 0, enq_flags);
493 		cid = cmask_next_and2_set_wrap(&taskc->cpus_allowed,
494 					       &qa.idle_cids.mask,
495 					       &qa.self_cids.mask, 0);
496 		if (cid < scx_bpf_nr_cids())
497 			scx_bpf_kick_cid(cid, SCX_KICK_IDLE);
498 		return;
499 	}
500 
501 	/*
502 	 * Insert a blocked mutex donor at the head of an eligible local DSQ with
503 	 * a fresh slice and %SCX_ENQ_PREEMPT, requesting an immediate reschedule.
504 	 * The test above guarantees that cpus_allowed intersects self_cids, but
505 	 * the donor's current cid may have been delegated to a child. Search the
506 	 * intersection starting at the current cid, preserving it when qmap still
507 	 * holds it and wrapping to another eligible self cid otherwise.
508 	 *
509 	 * A self cid may be held exclusively with SCX_CAP_ENQ or time-shared with
510 	 * only SCX_CAP_ENQ_IMMED. Add needs_immed() so either kind can accept the
511 	 * local insertion instead of rejecting and reenqueuing the donor for a
512 	 * capability miss. Once selected, the core proxy-exec path can run the
513 	 * mutex owner using the donor's scheduling context.
514 	 *
515 	 * This policy is intentionally unfair and can strongly prioritize tasks
516 	 * using contended mutexes; scx_qmap is a demonstration scheduler and
517 	 * this behavior makes proxy-exec support easy to observe.
518 	 */
519 	if (enq_flags & SCX_ENQ_BLOCKED) {
520 		cid = cmask_next_and_set_wrap(&taskc->cpus_allowed,
521 					      &qa.self_cids.mask,
522 					      scx_bpf_task_cid(p));
523 		if (cid >= scx_bpf_nr_cids()) {
524 			/* self_cids may have changed since the intersection test */
525 			dispatch_to_rescue(p, taskc, enq_flags);
526 			return;
527 		}
528 		scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | cid, slice_ns,
529 				   enq_flags | needs_immed(cid) | SCX_ENQ_PREEMPT);
530 		return;
531 	}
532 
533 	/*
534 	 * Fault injection: deliberately dispatch one of our own tasks to a cid
535 	 * we don't hold. The inserts carry SCX_ENQ_RESCUE and divert to the
536 	 * kernel rescue path, a deterministic rescue-traffic generator. Under
537 	 * -B 0 the kernel cap check rejects and re-enqueues them instead, so
538 	 * nr_inject_attempts tracks nr_reenq_cap 1:1 and proves delivery-time
539 	 * enforcement. Throttled.
540 	 */
541 	if (qa.inject_mode == QMAP_INJ_WRONG_CID && p->nr_cpus_allowed > 1 &&
542 	    !(enq_flags & SCX_ENQ_REENQ)) {
543 		static u32 inj_cnt;
544 
545 		if (!(++inj_cnt % 64)) {
546 			s32 bad = first_unavail_cid();
547 
548 			if (bad >= 0 && cmask_test(bad, &taskc->cpus_allowed)) {
549 				__sync_fetch_and_add(&qa.nr_inject_attempts, 1);
550 				__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
551 				scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | bad, slice_ns,
552 						   enq_flags | SCX_ENQ_RESCUE);
553 				return;
554 			}
555 		}
556 	}
557 
558 	/*
559 	 * IMMED stress testing: Every immed_stress_nth'th enqueue, dispatch
560 	 * directly to prev_cpu's local DSQ even when busy to force dsq->nr > 1
561 	 * and exercise the kernel IMMED reenqueue trigger paths.
562 	 */
563 	if (immed_stress_nth && !(enq_flags & SCX_ENQ_REENQ)) {
564 		static u32 immed_stress_cnt;
565 
566 		if (!(++immed_stress_cnt % immed_stress_nth)) {
567 			taskc->force_local = false;
568 			scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | scx_bpf_task_cid(p),
569 					   slice_ns, enq_flags);
570 			return;
571 		}
572 	}
573 
574 	/*
575 	 * If qmap_select_cid() is telling us to or this is the last runnable
576 	 * task on the CPU, enqueue locally.
577 	 */
578 	if (taskc->force_local) {
579 		taskc->force_local = false;
580 		scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, slice_ns,
581 				   enq_flags | needs_immed(scx_bpf_task_cid(p)));
582 		return;
583 	}
584 
585 	/* see lowpri_timerfn() */
586 	if (__COMPAT_has_generic_reenq() &&
587 	    p->scx.weight < 2 && !(p->flags & PF_KTHREAD) && !(enq_flags & SCX_ENQ_REENQ)) {
588 		scx_bpf_dsq_insert(p, LOWPRI_DSQ, slice_ns, enq_flags);
589 		return;
590 	}
591 
592 	/* if select_cid() wasn't called, try direct dispatch */
593 	if (!__COMPAT_is_enq_cpu_selected(enq_flags) &&
594 	    (cid = pick_direct_dispatch_cid(p, scx_bpf_task_cid(p), taskc)) >= 0) {
595 		__sync_fetch_and_add(&qa.nr_ddsp_from_enq, 1);
596 		scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | cid, slice_ns,
597 				   enq_flags | needs_immed(cid));
598 		return;
599 	}
600 
601 	/*
602 	 * If the task was re-enqueued due to the CPU being preempted by a
603 	 * higher priority scheduling class, just re-enqueue the task directly
604 	 * on the global DSQ. As we want another CPU to pick it up, find and
605 	 * kick an idle cid.
606 	 */
607 	if (enq_flags & SCX_ENQ_REENQ) {
608 		s32 cid;
609 
610 		scx_bpf_dsq_insert(p, SHARED_DSQ, 0, enq_flags);
611 		cid = cmask_next_and2_set_wrap(&taskc->cpus_allowed,
612 					       &qa.idle_cids.mask,
613 					       &qa.usable_cids.mask, 0);
614 		if (cid < scx_bpf_nr_cids())
615 			scx_bpf_kick_cid(cid, SCX_KICK_IDLE);
616 		return;
617 	}
618 
619 	/* Queue on the selected FIFO. */
620 	qmap_fifo_enqueue(&qa.fifos[idx], taskc);
621 
622 	if (highpri_boosting && p->scx.weight >= HIGHPRI_WEIGHT) {
623 		taskc->highpri = true;
624 		__sync_fetch_and_add(&qa.nr_highpri_queued, 1);
625 	}
626 	__sync_fetch_and_add(&qa.nr_enqueued, 1);
627 }
628 
629 void BPF_STRUCT_OPS(qmap_dequeue, struct task_struct *p, u64 deq_flags)
630 {
631 	task_ctx_t *taskc;
632 
633 	__sync_fetch_and_add(&qa.nr_dequeued, 1);
634 	if (deq_flags & SCX_DEQ_CORE_SCHED_EXEC)
635 		__sync_fetch_and_add(&qa.nr_core_sched_execed, 1);
636 
637 	taskc = lookup_task_ctx(p);
638 	if (taskc && taskc->fifo) {
639 		if (taskc->highpri)
640 			__sync_fetch_and_sub(&qa.nr_highpri_queued, 1);
641 		qmap_fifo_remove(taskc);
642 	}
643 }
644 
645 static void update_core_sched_head_seq(struct task_struct *p)
646 {
647 	int idx = weight_to_idx(p->scx.weight);
648 	task_ctx_t *taskc;
649 
650 	if ((taskc = lookup_task_ctx(p)))
651 		qa.core_sched_head_seqs[idx] = taskc->core_sched_seq;
652 }
653 
654 /*
655  * Send @p, stranded on the DSQ @it iterates, to the kernel rescue path. To
656  * exercise both kfuncs, move tasks with an even pid there with
657  * scx_bpf_dsq_move() and re-enqueue the others with scx_bpf_dsq_reenq_task(),
658  * which takes them through qmap_enqueue()'s rescue. The choice is per task so
659  * that a marked task isn't moved by another scan before the re-enqueue reaches
660  * it.
661  */
662 static __always_inline void rescue_stranded(struct bpf_iter_scx_dsq *it,
663 					    struct task_struct *p, task_ctx_t *taskc)
664 {
665 	s32 c;
666 
667 	if (p->pid & 1) {
668 		scx_bpf_dsq_reenq_task(it, p);
669 		return;
670 	}
671 
672 	c = cmask_next_set_wrap(&taskc->cpus_allowed, 0);
673 	if (c >= 0 && c < scx_bpf_nr_cids()) {
674 		__sync_fetch_and_add(&qa.nr_rescue_dsp, 1);
675 		scx_bpf_dsq_move(it, p, SCX_DSQ_LOCAL_ON | c, SCX_ENQ_RESCUE);
676 	}
677 }
678 
679 /*
680  * One pass over SHARED_DSQ: rescue stranded tasks and boost highpri ones. A
681  * task whose cids were lost while it was queued in the fifos would strand on
682  * SHARED_DSQ, which is consumed only on self cids it can't run on - send it to
683  * the kernel rescue path. One whose cids were lost after the highpri cull is
684  * likewise rescued out of HIGHPRI_DSQ below.
685  *
686  * To demonstrate the use of scx_bpf_dsq_move(), implement silly selective
687  * priority boosting mechanism by moving highpri tasks to HIGHPRI_DSQ and then
688  * consuming them first. This makes minor difference only when dsp_batch is
689  * larger than 1.
690  *
691  * scx_bpf_dsq_move[_vtime]() and scx_bpf_dsq_reenq_task() are allowed both from
692  * ops.dispatch() and non-rq-lock holding BPF programs. As demonstration, this
693  * function is called from qmap_dispatch() and monitor_timerfn().
694  */
695 static bool scan_shared_dsq(bool from_timer)
696 {
697 	struct task_struct *p;
698 	s32 this_cid = scx_bpf_this_cid();
699 	u32 nr_cids = scx_bpf_nr_cids();
700 
701 	/* rescue strands and move highpri tasks to HIGHPRI_DSQ */
702 	bpf_for_each(scx_dsq, p, SHARED_DSQ, 0) {
703 		static u64 highpri_seq;
704 		task_ctx_t *taskc;
705 
706 		if (!(taskc = lookup_task_ctx(p)))
707 			return false;
708 
709 		/* stranded? rescue - it can't be dispatched here either way */
710 		if (!cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask)) {
711 			rescue_stranded(BPF_FOR_EACH_ITER, p, taskc);
712 			continue;
713 		}
714 
715 		if (taskc->highpri) {
716 			/* exercise the set_*() and vtime interface too */
717 			scx_bpf_dsq_move_set_slice(BPF_FOR_EACH_ITER, slice_ns * 2);
718 			scx_bpf_dsq_move_set_vtime(BPF_FOR_EACH_ITER, highpri_seq++);
719 			scx_bpf_dsq_move_vtime(BPF_FOR_EACH_ITER, p, HIGHPRI_DSQ, 0);
720 		}
721 	}
722 
723 	/*
724 	 * Scan HIGHPRI_DSQ and dispatch until a task that can run here is
725 	 * found. Prefer this_cid if the task allows it; otherwise RR-scan the
726 	 * task's cpus_allowed starting after this_cid.
727 	 */
728 	bpf_for_each(scx_dsq, p, HIGHPRI_DSQ, 0) {
729 		task_ctx_t *taskc;
730 		bool dispatched = false;
731 		s32 cid;
732 
733 		if (!(taskc = lookup_task_ctx(p)))
734 			return false;
735 
736 		/* only run highpri tasks on cids this node can use right now */
737 		if (cmask_test(this_cid, &taskc->cpus_allowed) &&
738 		    cmask_test(this_cid, &qa.usable_cids.mask))
739 			cid = this_cid;
740 		else
741 			cid = cmask_next_and_set_wrap(&taskc->cpus_allowed,
742 						      &qa.usable_cids.mask,
743 						      this_cid + 1);
744 		if (cid >= nr_cids) {
745 			/* self cids lack caps in effect yet, leave it queued */
746 			if (cmask_intersects(&taskc->cpus_allowed, &qa.self_cids.mask))
747 				continue;
748 
749 			/* stranded after the cull - rescue it from here */
750 			rescue_stranded(BPF_FOR_EACH_ITER, p, taskc);
751 			continue;
752 		}
753 
754 		if (scx_bpf_dsq_move(BPF_FOR_EACH_ITER, p, SCX_DSQ_LOCAL_ON | cid,
755 				     SCX_ENQ_PREEMPT | needs_immed(cid))) {
756 			if (cid == this_cid) {
757 				dispatched = true;
758 				__sync_fetch_and_add(&qa.nr_expedited_local, 1);
759 			} else {
760 				__sync_fetch_and_add(&qa.nr_expedited_remote, 1);
761 			}
762 			if (from_timer)
763 				__sync_fetch_and_add(&qa.nr_expedited_from_timer, 1);
764 		} else {
765 			__sync_fetch_and_add(&qa.nr_expedited_lost, 1);
766 		}
767 
768 		if (dispatched)
769 			return true;
770 	}
771 
772 	return false;
773 }
774 
775 void BPF_STRUCT_OPS(qmap_dispatch, s32 cid, struct task_struct *prev)
776 {
777 	struct task_struct *p;
778 	struct cpu_ctx __arena *cpuc;
779 	task_ctx_t *taskc;
780 	u32 batch = dsp_batch ?: 1;
781 	s32 owner, i;
782 
783 	if (scan_shared_dsq(false))
784 		return;
785 
786 	/*
787 	 * Sub-sched routing: a child-owned cid goes to its owner. Never run
788 	 * this node's own tasks on a delegated cid. Read without the guard.
789 	 */
790 	owner = qa.part.cid_owner[cid];
791 	if (owner == CID_SHARED) {
792 		/* route to the live rr holder (0 = self, runs below) */
793 		s32 pos = qa.part.rr_pos;
794 		u64 holder_cgid = (pos >= 0 && pos < MAX_PARTS) ?
795 				  qa.part.rr_slots[pos] : 0;
796 
797 		if (holder_cgid) {
798 			scx_bpf_sub_dispatch(holder_cgid);
799 			return;
800 		}
801 	} else if (owner >= 0 && owner < MAX_SUB_SCHEDS) {
802 		u64 cgid = qa.sub_sched_ctxs[owner].cgroup_id;
803 
804 		if (cgid) {
805 			if (scx_bpf_sub_dispatch(cgid))
806 				__sync_fetch_and_add(&qa.sub_sched_ctxs[owner].nr_dsps, 1);
807 			return;
808 		}
809 	}
810 
811 	if (!qa.nr_highpri_queued && scx_bpf_dsq_move_to_local(SHARED_DSQ, needs_immed(cid)))
812 		return;
813 
814 	if (dsp_inf_loop_after && qa.nr_dispatched > dsp_inf_loop_after) {
815 		/*
816 		 * PID 2 should be kthreadd which should mostly be idle and off
817 		 * the scheduler. Let's keep dispatching it to force the kernel
818 		 * to call this function over and over again.
819 		 */
820 		p = bpf_task_from_pid(2);
821 		if (p) {
822 			scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, slice_ns, 0);
823 			bpf_task_release(p);
824 			return;
825 		}
826 	}
827 
828 	cpuc = &qa.cpu_ctxs[scx_bpf_this_cid()];
829 
830 	for (i = 0; i < 5; i++) {
831 		/* Advance the dispatch cursor and pick the fifo. */
832 		if (!cpuc->dsp_cnt) {
833 			cpuc->dsp_idx = (cpuc->dsp_idx + 1) % 5;
834 			cpuc->dsp_cnt = 1 << cpuc->dsp_idx;
835 		}
836 
837 		/* Dispatch or advance. */
838 		bpf_repeat(BPF_MAX_LOOPS) {
839 			task_ctx_t *taskc;
840 
841 			taskc = qmap_fifo_pop(&qa.fifos[cpuc->dsp_idx]);
842 			if (!taskc)
843 				break;
844 
845 			p = scx_bpf_tid_to_task(taskc->tid);
846 			if (!p)
847 				continue;
848 
849 			if (taskc->highpri)
850 				__sync_fetch_and_sub(&qa.nr_highpri_queued, 1);
851 
852 			update_core_sched_head_seq(p);
853 			__sync_fetch_and_add(&qa.nr_dispatched, 1);
854 
855 			scx_bpf_dsq_insert(p, SHARED_DSQ, slice_ns, 0);
856 
857 			/*
858 			 * scx_qmap uses a global BPF queue that any CPU's
859 			 * dispatch can pop from. If this CPU popped a task that
860 			 * can't run here, it gets stranded on SHARED_DSQ after
861 			 * consume_dispatch_q() skips it. Kick the task's home
862 			 * CPU so it drains SHARED_DSQ.
863 			 *
864 			 * There's a race between the pop and the flush of the
865 			 * buffered dsq_insert:
866 			 *
867 			 *  CPU 0 (dispatching)      CPU 1 (home, idle)
868 			 *  ~~~~~~~~~~~~~~~~~~~      ~~~~~~~~~~~~~~~~~~~
869 			 *  pop from BPF queue
870 			 *  dsq_insert(buffered)
871 			 *                           balance:
872 			 *                             SHARED_DSQ empty
873 			 *                             BPF queue empty
874 			 *                             -> goes idle
875 			 *  flush -> on SHARED
876 			 *  kick CPU 1
877 			 *                           wakes, drains task
878 			 *
879 			 * The kick prevents indefinite stalls but a per-CPU
880 			 * kthread like ksoftirqd can be briefly stranded when
881 			 * its home CPU enters idle with softirq pending,
882 			 * triggering:
883 			 *
884 			 *  "NOHZ tick-stop error: local softirq work is pending, handler #N!!!"
885 			 *
886 			 * from report_idle_softirq(). The kick lands shortly
887 			 * after and the home CPU drains the task. This could be
888 			 * avoided by e.g. dispatching pinned tasks to local or
889 			 * global DSQs, but the current code is left as-is to
890 			 * document this class of issue -- other schedulers
891 			 * seeing similar warnings can use this as a reference.
892 			 */
893 			if (!cmask_test(cid, &taskc->cpus_allowed))
894 				scx_bpf_kick_cid(scx_bpf_task_cid(p), 0);
895 			batch--;
896 			cpuc->dsp_cnt--;
897 			if (!batch || !scx_bpf_dispatch_nr_slots()) {
898 				if (scan_shared_dsq(false) ||
899 				    scx_bpf_dsq_move_to_local(SHARED_DSQ, needs_immed(cid)))
900 					return;
901 				goto prev;
902 			}
903 			if (!cpuc->dsp_cnt)
904 				break;
905 		}
906 
907 		cpuc->dsp_cnt = 0;
908 	}
909 
910 	if (scan_shared_dsq(false))
911 		return;
912 prev:
913 	/*
914 	 * No other tasks. @prev will keep running. Update its core_sched_seq as
915 	 * if the task were enqueued and dispatched immediately.
916 	 *
917 	 * No @prev to keep running means the CPU goes idle. If its claim was
918 	 * never used, that is not a transition and ops.update_idle() stays
919 	 * silent. Restore the claim here.
920 	 */
921 	if (prev) {
922 		taskc = lookup_task_ctx(prev);
923 		if (!taskc)
924 			return;
925 
926 		taskc->core_sched_seq =
927 			qa.core_sched_tail_seqs[weight_to_idx(prev->scx.weight)]++;
928 	} else {
929 		cmask_set(cid, &qa.idle_cids.mask);
930 	}
931 }
932 
933 void BPF_STRUCT_OPS(qmap_tick, struct task_struct *p)
934 {
935 	struct cpu_ctx __arena *cpuc = &qa.cpu_ctxs[scx_bpf_this_cid()];
936 	int idx;
937 
938 	/*
939 	 * Use the running avg of weights to select the target cpuperf level.
940 	 * This is a demonstration of the cpuperf feature rather than a
941 	 * practical strategy to regulate CPU frequency.
942 	 */
943 	cpuc->avg_weight = cpuc->avg_weight * 3 / 4 + p->scx.weight / 4;
944 	idx = weight_to_idx(cpuc->avg_weight);
945 	cpuc->cpuperf_target = qidx_to_cpuperf_target[idx];
946 
947 	scx_bpf_cidperf_set(scx_bpf_task_cid(p), cpuc->cpuperf_target);
948 }
949 
950 /*
951  * The distance from the head of the queue scaled by the weight of the queue.
952  * The lower the number, the older the task and the higher the priority.
953  */
954 static s64 task_qdist(struct task_struct *p, task_ctx_t *taskc)
955 {
956 	int idx = weight_to_idx(p->scx.weight);
957 	s64 qdist;
958 
959 	qdist = taskc->core_sched_seq - qa.core_sched_head_seqs[idx];
960 
961 	/*
962 	 * As queue index increments, the priority doubles. The queue w/ index 3
963 	 * is dispatched twice more frequently than 2. Reflect the difference by
964 	 * scaling qdists accordingly. Note that the shift amount needs to be
965 	 * flipped depending on the sign to avoid flipping priority direction.
966 	 */
967 	if (qdist >= 0)
968 		return qdist << (4 - idx);
969 	else
970 		return qdist << idx;
971 }
972 
973 /*
974  * This is called to determine the task ordering when core-sched is picking
975  * tasks to execute on SMT siblings and should encode about the same ordering as
976  * the regular scheduling path. Use the priority-scaled distances from the head
977  * of the queues to compare the two tasks which should be consistent with the
978  * dispatch path behavior.
979  */
980 bool BPF_STRUCT_OPS(qmap_core_sched_before,
981 		    struct task_struct *a, struct task_struct *b)
982 {
983 	task_ctx_t *taskc_a = lookup_task_ctx(a);
984 	task_ctx_t *taskc_b = lookup_task_ctx(b);
985 
986 	/*
987 	 * A task delegated to a sub-scheduler has no task_ctx here. Order such
988 	 * pairs by the kernel's default ordering - a running task after every
989 	 * waiting task, then by runnable_at.
990 	 */
991 	if (!taskc_a || !taskc_b) {
992 		if (a->on_cpu != b->on_cpu)
993 			return b->on_cpu;
994 		return time_before(a->scx.runnable_at, b->scx.runnable_at);
995 	}
996 
997 	return task_qdist(a, taskc_a) < task_qdist(b, taskc_b);
998 }
999 
1000 /*
1001  * sched_switch tracepoint and cpu_release handlers are no longer needed.
1002  * With SCX_OPS_ALWAYS_ENQ_IMMED, wakeup_preempt_scx() reenqueues IMMED
1003  * tasks when a higher-priority scheduling class takes the CPU.
1004  */
1005 
1006 s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init_task, struct task_struct *p,
1007 			     struct scx_init_task_args *args)
1008 {
1009 	struct task_ctx_stor_val *v;
1010 	task_ctx_t *taskc;
1011 
1012 	if (qa.inject_mode == QMAP_INJ_INIT_FAIL &&
1013 	    !bpf_strncmp(p->comm, 6, "qmfail"))
1014 		return -ENOMEM;
1015 
1016 	if (p->tgid == disallow_tgid)
1017 		p->scx.disallow = true;
1018 
1019 	/* pop a slab entry off the free list */
1020 	if (qmap_spin_lock(&qa_task_lock))
1021 		return -EBUSY;
1022 	taskc = qa.task_free_head;
1023 	if (taskc)
1024 		qa.task_free_head = taskc->next_free;
1025 	bpf_res_spin_unlock(&qa_task_lock);
1026 	if (!taskc) {
1027 		scx_bpf_error("task_ctx slab exhausted (max_tasks=%u)", max_tasks);
1028 		return -ENOMEM;
1029 	}
1030 
1031 	taskc->next_free = NULL;
1032 	taskc->q_next = NULL;
1033 	taskc->q_prev = NULL;
1034 	taskc->fifo = NULL;
1035 	taskc->tid = p->scx.tid;
1036 	taskc->pid = p->pid;
1037 	taskc->force_local = false;
1038 	taskc->highpri = false;
1039 	taskc->core_sched_seq = 0;
1040 	cmask_init(&taskc->cpus_allowed, 0, scx_bpf_nr_cids());
1041 
1042 	v = bpf_task_storage_get(&task_ctx_stor, p, NULL,
1043 				 BPF_LOCAL_STORAGE_GET_F_CREATE);
1044 	if (!v) {
1045 		/* push back to the free list */
1046 		if (!qmap_spin_lock(&qa_task_lock)) {
1047 			taskc->next_free = qa.task_free_head;
1048 			qa.task_free_head = taskc;
1049 			bpf_res_spin_unlock(&qa_task_lock);
1050 		}
1051 		return -ENOMEM;
1052 	}
1053 	v->taskc = taskc;
1054 	return 0;
1055 }
1056 
1057 void BPF_STRUCT_OPS(qmap_exit_task, struct task_struct *p,
1058 		    struct scx_exit_task_args *args)
1059 {
1060 	struct task_ctx_stor_val *v;
1061 	task_ctx_t *taskc;
1062 
1063 	v = bpf_task_storage_get(&task_ctx_stor, p, NULL, 0);
1064 	if (!v || !v->taskc)
1065 		return;
1066 	taskc = v->taskc;
1067 	v->taskc = NULL;
1068 
1069 	if (qmap_spin_lock(&qa_task_lock))
1070 		return;
1071 	taskc->next_free = qa.task_free_head;
1072 	qa.task_free_head = taskc;
1073 	bpf_res_spin_unlock(&qa_task_lock);
1074 }
1075 
1076 void BPF_STRUCT_OPS(qmap_dump, struct scx_dump_ctx *dctx)
1077 {
1078 	task_ctx_t *taskc;
1079 	s32 i;
1080 
1081 	QMAP_TOUCH_ARENA();
1082 
1083 	if (suppress_dump)
1084 		return;
1085 
1086 	/*
1087 	 * Walk the queue lists without locking - kfunc calls (scx_bpf_dump)
1088 	 * aren't in the verifier's kfunc_spin_allowed() list so we can't hold
1089 	 * a lock and dump. Best-effort; racing may print stale tids but the
1090 	 * walk is bounded by bpf_repeat() so it always terminates.
1091 	 */
1092 	bpf_for(i, 0, 5) {
1093 		scx_bpf_dump("QMAP FIFO[%d]:", i);
1094 		taskc = qa.fifos[i].head;
1095 		bpf_repeat(4096) {
1096 			if (!taskc)
1097 				break;
1098 			scx_bpf_dump(" %d:%llu", taskc->pid, taskc->tid);
1099 			taskc = taskc->q_next;
1100 		}
1101 		scx_bpf_dump("\n");
1102 	}
1103 }
1104 
1105 void BPF_STRUCT_OPS(qmap_dump_cid, struct scx_dump_ctx *dctx, s32 cid, bool idle)
1106 {
1107 	struct cpu_ctx __arena *cpuc = &qa.cpu_ctxs[cid];
1108 
1109 	if (suppress_dump || idle)
1110 		return;
1111 
1112 	scx_bpf_dump("QMAP: dsp_idx=%llu dsp_cnt=%llu avg_weight=%u cpuperf_target=%u",
1113 		     cpuc->dsp_idx, cpuc->dsp_cnt, cpuc->avg_weight,
1114 		     cpuc->cpuperf_target);
1115 }
1116 
1117 void BPF_STRUCT_OPS(qmap_dump_task, struct scx_dump_ctx *dctx, struct task_struct *p)
1118 {
1119 	struct task_ctx_stor_val *v;
1120 	task_ctx_t *taskc;
1121 
1122 	QMAP_TOUCH_ARENA();
1123 
1124 	if (suppress_dump)
1125 		return;
1126 	v = bpf_task_storage_get(&task_ctx_stor, p, NULL, 0);
1127 	if (!v || !v->taskc)
1128 		return;
1129 	taskc = v->taskc;
1130 
1131 	scx_bpf_dump("QMAP: force_local=%d core_sched_seq=%llu",
1132 		     taskc->force_local, taskc->core_sched_seq);
1133 }
1134 
1135 s32 BPF_STRUCT_OPS(qmap_cpuctl_init, struct cgroup *cgrp, struct scx_cgroup_init_args *args)
1136 {
1137 	QMAP_TOUCH_ARENA();
1138 
1139 	if (print_msgs)
1140 		bpf_printk("CGRP INIT %llu weight=%u period=%lu quota=%ld burst=%lu",
1141 			   cgrp->kn->id, args->weight, args->bw_period_us,
1142 			   args->bw_quota_us, args->bw_burst_us);
1143 
1144 	if (qa.inject_mode == QMAP_INJ_CGRP_INIT_FAIL) {
1145 		char name[7] = {};
1146 
1147 		bpf_probe_read_kernel_str(name, sizeof(name), cgrp->kn->name);
1148 		if (!bpf_strncmp(name, 6, "qmfail"))
1149 			return -ENOMEM;
1150 	}
1151 
1152 	return 0;
1153 }
1154 
1155 static void redistribute(void);
1156 
1157 void BPF_STRUCT_OPS(qmap_cpuctl_set_weight, struct cgroup *cgrp, u32 weight)
1158 {
1159 	u64 cgid = cgrp->kn->id;
1160 	s32 i;
1161 
1162 	QMAP_TOUCH_ARENA();
1163 
1164 	if (print_msgs)
1165 		bpf_printk("CGRP SET %llu weight=%u", cgid, weight);
1166 
1167 	/*
1168 	 * Knobs belong to the parent, so this op carries the child subs'
1169 	 * attach point weights. Adjust the matching sub's share of the cid
1170 	 * partition. Other cgroups don't participate in the split.
1171 	 */
1172 	for (i = 0; i < MAX_SUB_SCHEDS; i++) {
1173 		if (qa.sub_sched_ctxs[i].cgroup_id != cgid)
1174 			continue;
1175 		if (qa.sub_sched_ctxs[i].weight != weight) {
1176 			qa.sub_sched_ctxs[i].weight = weight;
1177 			redistribute();
1178 		}
1179 		break;
1180 	}
1181 }
1182 
1183 void BPF_STRUCT_OPS(qmap_cpuctl_set_bandwidth, struct cgroup *cgrp, u64 period_us,
1184 		    u64 quota_us, u64 burst_us)
1185 {
1186 	if (print_msgs)
1187 		bpf_printk("CGRP SET %llu period=%lu quota=%ld burst=%lu",
1188 			   cgrp->kn->id, period_us, quota_us, burst_us);
1189 }
1190 
1191 void BPF_STRUCT_OPS(qmap_cpuctl_move, struct task_struct *p, struct cgroup *from,
1192 		    struct cgroup *to)
1193 {
1194 	if (print_msgs)
1195 		bpf_printk("CGRP MOVE %d %llu -> %llu",
1196 			   p->pid, from->kn->id, to->kn->id);
1197 }
1198 
1199 void BPF_STRUCT_OPS(qmap_update_idle, s32 cid, bool idle)
1200 {
1201 	QMAP_TOUCH_ARENA();
1202 
1203 	/*
1204 	 * The kernel delivers update_idle() for every cid this node holds
1205 	 * SCX_CAP_BASE on. Track every cid's idle state regardless of
1206 	 * delegation: the direct-dispatch pick masks idle_cids with usable_cids
1207 	 * at selection, so a cid already idle when it returns to self needs no
1208 	 * reseed here.
1209 	 */
1210 	if (idle)
1211 		cmask_set(cid, &qa.idle_cids.mask);
1212 	else
1213 		cmask_clear(cid, &qa.idle_cids.mask);
1214 }
1215 
1216 void BPF_STRUCT_OPS(qmap_set_cmask, struct task_struct *p,
1217 		    const struct scx_cmask *cmask_in)
1218 {
1219 	struct scx_cmask __arena *cmask = (struct scx_cmask __arena *)(long)cmask_in;
1220 	task_ctx_t *taskc;
1221 
1222 	taskc = lookup_task_ctx(p);
1223 	if (!taskc)
1224 		return;
1225 	cmask_copy(&taskc->cpus_allowed, cmask);
1226 }
1227 
1228 struct monitor_timer {
1229 	struct bpf_timer timer;
1230 };
1231 
1232 struct {
1233 	__uint(type, BPF_MAP_TYPE_ARRAY);
1234 	__uint(max_entries, 1);
1235 	__type(key, u32);
1236 	__type(value, struct monitor_timer);
1237 } monitor_timer SEC(".maps");
1238 
1239 /*
1240  * Aggregate cidperf across the first nr_online_cids cids. Post-hotplug
1241  * the first-N-are-online invariant drifts, so some cap/cur values may
1242  * be stale. For this demo monitor that's fine; the scheduler exits on
1243  * the enable-time hotplug_seq mismatch and userspace restarts, which
1244  * rebuilds the layout.
1245  */
1246 static void monitor_cpuperf(void)
1247 {
1248 	u32 nr_online = scx_bpf_nr_online_cids();
1249 	u64 cap_sum = 0, cur_sum = 0, cur_min = SCX_CPUPERF_ONE, cur_max = 0;
1250 	u64 target_sum = 0, target_min = SCX_CPUPERF_ONE, target_max = 0;
1251 	s32 cid;
1252 
1253 	QMAP_TOUCH_ARENA();
1254 
1255 	bpf_for(cid, 0, nr_online) {
1256 		struct cpu_ctx __arena *cpuc = &qa.cpu_ctxs[cid];
1257 		u32 cap = scx_bpf_cidperf_cap(cid);
1258 		u32 cur = scx_bpf_cidperf_cur(cid);
1259 		u32 target;
1260 
1261 		cur_min = cur < cur_min ? cur : cur_min;
1262 		cur_max = cur > cur_max ? cur : cur_max;
1263 
1264 		cur_sum += (u64)cur * cap / SCX_CPUPERF_ONE;
1265 		cap_sum += cap;
1266 
1267 		target = cpuc->cpuperf_target;
1268 		target_sum += target;
1269 		target_min = target < target_min ? target : target_min;
1270 		target_max = target > target_max ? target : target_max;
1271 	}
1272 
1273 	if (!nr_online || !cap_sum)
1274 		return;
1275 
1276 	qa.cpuperf_min = cur_min;
1277 	qa.cpuperf_avg = cur_sum * SCX_CPUPERF_ONE / cap_sum;
1278 	qa.cpuperf_max = cur_max;
1279 
1280 	qa.cpuperf_target_min = target_min;
1281 	qa.cpuperf_target_avg = target_sum / nr_online;
1282 	qa.cpuperf_target_max = target_max;
1283 }
1284 
1285 /*
1286  * Dump the currently queued tasks in the shared DSQ to demonstrate the usage of
1287  * scx_bpf_dsq_nr_queued() and DSQ iterator. Raise the dispatch batch count to
1288  * see meaningful dumps in the trace pipe.
1289  */
1290 static void dump_shared_dsq(void)
1291 {
1292 	struct task_struct *p;
1293 	s32 nr;
1294 
1295 	if (!(nr = scx_bpf_dsq_nr_queued(SHARED_DSQ)))
1296 		return;
1297 
1298 	bpf_printk("Dumping %d tasks in SHARED_DSQ in reverse order", nr);
1299 
1300 	bpf_rcu_read_lock();
1301 	bpf_for_each(scx_dsq, p, SHARED_DSQ, SCX_DSQ_ITER_REV)
1302 		bpf_printk("%s[%d]", p->comm, p->pid);
1303 	bpf_rcu_read_unlock();
1304 }
1305 
1306 static int monitor_timerfn(void *map, int *key, struct bpf_timer *timer)
1307 {
1308 	bpf_rcu_read_lock();
1309 	scan_shared_dsq(true);
1310 	bpf_rcu_read_unlock();
1311 
1312 	monitor_cpuperf();
1313 
1314 	if (print_dsqs_and_events) {
1315 		struct scx_event_stats events;
1316 
1317 		dump_shared_dsq();
1318 
1319 		__COMPAT_scx_bpf_events(&events, sizeof(events));
1320 
1321 		bpf_printk("%35s: %lld", "SCX_EV_SELECT_CPU_FALLBACK",
1322 			   scx_read_event(&events, SCX_EV_SELECT_CPU_FALLBACK));
1323 		bpf_printk("%35s: %lld", "SCX_EV_DISPATCH_LOCAL_DSQ_OFFLINE",
1324 			   scx_read_event(&events, SCX_EV_DISPATCH_LOCAL_DSQ_OFFLINE));
1325 		bpf_printk("%35s: %lld", "SCX_EV_DISPATCH_KEEP_LAST",
1326 			   scx_read_event(&events, SCX_EV_DISPATCH_KEEP_LAST));
1327 		bpf_printk("%35s: %lld", "SCX_EV_ENQ_SKIP_EXITING",
1328 			   scx_read_event(&events, SCX_EV_ENQ_SKIP_EXITING));
1329 		bpf_printk("%35s: %lld", "SCX_EV_REFILL_SLICE_DFL",
1330 			   scx_read_event(&events, SCX_EV_REFILL_SLICE_DFL));
1331 		bpf_printk("%35s: %lld", "SCX_EV_BYPASS_DURATION",
1332 			   scx_read_event(&events, SCX_EV_BYPASS_DURATION));
1333 		bpf_printk("%35s: %lld", "SCX_EV_BYPASS_DISPATCH",
1334 			   scx_read_event(&events, SCX_EV_BYPASS_DISPATCH));
1335 		bpf_printk("%35s: %lld", "SCX_EV_BYPASS_ACTIVATE",
1336 			   scx_read_event(&events, SCX_EV_BYPASS_ACTIVATE));
1337 	}
1338 
1339 	if (bpf_timer_start(timer, ONE_SEC_IN_NS, 0))
1340 		scx_bpf_error("failed to re-arm stats timer");
1341 	return 0;
1342 }
1343 
1344 struct lowpri_timer {
1345 	struct bpf_timer timer;
1346 };
1347 
1348 struct {
1349 	__uint(type, BPF_MAP_TYPE_ARRAY);
1350 	__uint(max_entries, 1);
1351 	__type(key, u32);
1352 	__type(value, struct lowpri_timer);
1353 } lowpri_timer SEC(".maps");
1354 
1355 /*
1356  * Nice 19 tasks are put into the lowpri DSQ. Every 10ms, reenq is triggered and
1357  * the tasks are transferred to SHARED_DSQ.
1358  */
1359 static int lowpri_timerfn(void *map, int *key, struct bpf_timer *timer)
1360 {
1361 	scx_bpf_dsq_reenq(LOWPRI_DSQ, 0);
1362 	if (bpf_timer_start(timer, LOWPRI_INTV_NS, 0))
1363 		scx_bpf_error("failed to re-arm lowpri timer");
1364 	return 0;
1365 }
1366 
1367 struct round_robin_timer {
1368 	struct bpf_timer timer;
1369 };
1370 
1371 struct {
1372 	__uint(type, BPF_MAP_TYPE_ARRAY);
1373 	__uint(max_entries, 1);
1374 	__type(key, u32);
1375 	__type(value, struct round_robin_timer);
1376 } round_robin_timer SEC(".maps");
1377 
1378 enum part_pending_flags {
1379 	PART_REFRESH = BIT_U64(0),
1380 	PART_REDISTRIBUTE = BIT_U64(1),
1381 };
1382 
1383 /*
1384  * Partition update synchronization. qa.part can be written from concurrent
1385  * contexts. This single-runner guard admits one writer at a time without
1386  * holding a lock across the grant/revoke kfuncs. part_pending coalesces
1387  * refresh and repartition requests that arrive while it is held.
1388  *
1389  * They live in .bss, not the arena: rr_advance() runs from a bpf_timer
1390  * callback, where the verifier rejects atomic ops on arena memory.
1391  */
1392 static u64 part_busy;
1393 static u64 part_pending;
1394 
1395 static bool part_try_start(void)
1396 {
1397 	/* set busy, report whether it was previously clear (we acquired it) */
1398 	return !__sync_fetch_and_or(&part_busy, 1);
1399 }
1400 
1401 static void part_end(void)
1402 {
1403 	__sync_fetch_and_and(&part_busy, 0);
1404 }
1405 
1406 /*
1407  * compute_partition() scratch.
1408  *
1409  * The excl-held cids are handed out in cid order: position 0..nr_excl-1 over
1410  * the held cids is split into contiguous ranges, one per participant that gets
1411  * at least one excl cid. Range k is owned by cp_range_owner[k] and ends at the
1412  * cumulative position cp_range_end[k].
1413  */
1414 static s32 cp_range_owner[MAX_PARTS];	/* exclusive range k: its owner id ... */
1415 static s32 cp_range_end[MAX_PARTS];	/* ... and the cumulative position it ends at */
1416 
1417 /* a participant in the partition: self or an attached child */
1418 struct participant {
1419 	s32 slot;	/* child slot, or CID_SELF */
1420 	u32 weight;	/* cpu.weight */
1421 };
1422 
1423 /**
1424  * place_one - assign one excl-held cid to its owner
1425  * @cid: the excl-held cid to place
1426  * @n: its position among the excl-held cids, in [0, nr_excl)
1427  * @total_excl:	how many positions are owned exclusively (the rest are shared)
1428  *
1429  * Position @n below @total_excl is owned exclusively. It falls in the range
1430  * whose cumulative end it is under, owned by cp_range_owner[]. A position at or
1431  * above @total_excl is the rounding leftover which joins the shared pool.
1432  *
1433  * A separate __noinline function to help verification.
1434  */
1435 __noinline int place_one(s32 cid, s32 n, s32 total_excl)
1436 {
1437 	s32 owner = CID_SELF, i, s;
1438 
1439 	if (cid < 0 || cid >= SCX_QMAP_MAX_CPUS || n < 0 || n >= SCX_QMAP_MAX_CPUS ||
1440 	    total_excl < 0) {
1441 		scx_bpf_error("-ERANGE");
1442 		return 0;
1443 	}
1444 
1445 	if (n < total_excl) {
1446 		for (i = 0; i < MAX_PARTS; i++) {
1447 			if (n < cp_range_end[i]) {
1448 				owner = cp_range_owner[i];
1449 				break;
1450 			}
1451 		}
1452 		qa.part.cid_owner[cid] = owner;
1453 	} else {
1454 		s = n - total_excl;
1455 		if (s < 0 || s >= MAX_PARTS) {
1456 			scx_bpf_error("-ERANGE");
1457 			return 0;
1458 		}
1459 		qa.part.shared_cids[s] = cid;
1460 		/* time-shared: dispatch resolves the live holder via rr_pos */
1461 		qa.part.cid_owner[cid] = CID_SHARED;
1462 	}
1463 	return 0;
1464 }
1465 
1466 /**
1467  * compute_partition - build the cid partition from this node's held caps
1468  *
1469  * Decide each cid's owner, the shared pool and the rr rotation. __noinline to
1470  * help verification. See the comment at the top of the file.
1471  */
1472 __noinline void compute_partition(void)
1473 {
1474 	s32 nr_cids = qa.nr_cids;
1475 	s32 nr_excl, total_excl = 0, nr_rr = 0;
1476 	s32 sum_w, i, cid, n = 0, share, self_w;
1477 	u64 cgid_snap[MAX_SUB_SCHEDS];
1478 	s32 w_snap[MAX_SUB_SCHEDS];
1479 
1480 	if (nr_cids > SCX_QMAP_MAX_CPUS) {
1481 		scx_bpf_error("-ERANGE");
1482 		return;
1483 	}
1484 
1485 	/* find out the cids we hold */
1486 	scx_bpf_sub_caps(0, SCX_CAP_ENQ, &qa.held_excl.mask);
1487 	scx_bpf_sub_caps(0, SCX_CAP_ENQ_IMMED, &qa.held_shared.mask);
1488 	/* held only as ENQ_IMMED */
1489 	cmask_andnot(&qa.held_shared.mask, &qa.held_shared.mask, &qa.held_excl.mask);
1490 
1491 	qa.part.nr_shared = 0;
1492 	qa.part.nr_rr = 0;
1493 	qa.part.rr_pos = 0;
1494 
1495 	nr_excl = cmask_weight(&qa.held_excl.mask);
1496 	qa.part.nr_excl = nr_excl;
1497 
1498 	/* no excl cid: held_shared stays self-local, the rest unheld */
1499 	if (!nr_excl) {
1500 		bpf_for(cid, 0, nr_cids) {
1501 			if (cmask_test(cid, &qa.held_shared.mask))
1502 				qa.part.cid_owner[cid] = CID_SELF;
1503 			else
1504 				qa.part.cid_owner[cid] = CID_NONE;
1505 		}
1506 		return;
1507 	}
1508 
1509 	/*
1510 	 * Snapshot membership and weights so the sum_w and share loops agree. A
1511 	 * mid-compute change would otherwise wrap nr_shared negative. The self
1512 	 * weight is fixed at the default: a cgroup's weight is its parent's
1513 	 * knob, not the scheduler's own business.
1514 	 */
1515 	self_w = 100;
1516 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1517 		cgid_snap[i] = qa.sub_sched_ctxs[i].cgroup_id;
1518 		w_snap[i] = cgid_snap[i] ? (qa.sub_sched_ctxs[i].weight ?: 100) : 0;
1519 	}
1520 
1521 	/*
1522 	 * Participants are self plus each child. Give each a fixed range/rr
1523 	 * slot: self at slot 0, child i at slot i+1.
1524 	 *
1525 	 * sum_w totals every participant's weight.
1526 	 */
1527 	sum_w = self_w;
1528 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1529 		barrier_var(sum_w);
1530 		sum_w += w_snap[i];
1531 	}
1532 
1533 	/*
1534 	 * Split [0, nr_excl) into one contiguous range per participant, each
1535 	 * the floor of its weight share. cp_range_owner[]/cp_range_end[] record
1536 	 * each range's owner and cumulative end, total_excl counts the
1537 	 * exclusive slots, and the rest (nr_excl - total_excl) are shared.
1538 	 * rr_slots[] lists every participant for the round-robin.
1539 	 */
1540 	share = (u64)nr_excl * self_w / sum_w;
1541 	total_excl += share;
1542 	cp_range_owner[0] = CID_SELF;
1543 	cp_range_end[0] = total_excl;
1544 	qa.part.rr_slots[nr_rr++] = 0;		/* self holds slot 0 (cgid 0 = no grant) */
1545 
1546 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1547 		u64 cgid = cgid_snap[i];
1548 		s32 w = w_snap[i];
1549 
1550 		barrier_var(total_excl);
1551 		share = (u64)nr_excl * w / sum_w;
1552 		total_excl += share;
1553 		cp_range_owner[i + 1] = cgid ? i : CID_NONE;
1554 		cp_range_end[i + 1] = total_excl;
1555 
1556 		if (cgid) {
1557 			barrier_var(nr_rr);
1558 			if (nr_rr < 0 || nr_rr >= MAX_PARTS) {
1559 				scx_bpf_error("-ERANGE");
1560 				return;
1561 			}
1562 			qa.part.rr_slots[nr_rr++] = cgid;
1563 		}
1564 	}
1565 
1566 	/* assign each cid: held-excl by position, the rest self/none */
1567 	bpf_for(cid, 0, nr_cids) {
1568 		if (cmask_test(cid, &qa.held_excl.mask)) {
1569 			place_one(cid, n, total_excl);
1570 			n++;
1571 			barrier_var(n);
1572 		} else if (cmask_test(cid, &qa.held_shared.mask)) {
1573 			qa.part.cid_owner[cid] = CID_SELF;	/* time-share, self-local */
1574 		} else {
1575 			qa.part.cid_owner[cid] = CID_NONE;	/* not held */
1576 		}
1577 	}
1578 
1579 	qa.part.nr_shared = nr_excl - total_excl;
1580 	qa.part.nr_rr = nr_rr;
1581 }
1582 
1583 /*
1584  * Charge elapsed wall time to each cid's current owner. Runs under the
1585  * partition guard before every ownership change and from the stats flush, so
1586  * alloc_ns[] reflects the layout that was in effect. Shared-pool time is
1587  * charged to the live round-robin holder.
1588  */
1589 static __noinline void account_alloc(void)
1590 {
1591 	u64 now = bpf_ktime_get_ns();
1592 	s32 rr_owner = CID_SELF;
1593 	s32 nr_cids = qa.nr_cids;
1594 	u64 delta;
1595 	s32 cid, i;
1596 
1597 	if (nr_cids < 0 || nr_cids > SCX_QMAP_MAX_CPUS) {
1598 		scx_bpf_error("-ERANGE");
1599 		return;
1600 	}
1601 
1602 	/* first call starts the clock */
1603 	if (!qa.alloc_ts) {
1604 		qa.alloc_ts = now;
1605 		return;
1606 	}
1607 	delta = now - qa.alloc_ts;
1608 	qa.alloc_ts = now;
1609 	qa.alloc_window_ns += delta;
1610 
1611 	/* resolve the live shared-pool holder to an owner id */
1612 	if (qa.part.nr_shared && qa.part.nr_rr) {
1613 		u32 pos = qa.part.rr_pos;
1614 		u64 cgid = pos < MAX_PARTS ? qa.part.rr_slots[pos] : 0;
1615 
1616 		if (cgid) {
1617 			rr_owner = CID_NONE;
1618 			bpf_for(i, 0, MAX_SUB_SCHEDS)
1619 				if (qa.sub_sched_ctxs[i].cgroup_id == cgid)
1620 					rr_owner = i;
1621 		}
1622 	}
1623 
1624 	bpf_for(cid, 0, nr_cids) {
1625 		s32 owner = qa.part.cid_owner[cid];
1626 
1627 		if (owner == CID_SHARED)
1628 			owner = rr_owner;
1629 		if (owner >= 0 && owner < MAX_SUB_SCHEDS)
1630 			qa.alloc_ns[owner] += delta;
1631 		else if (owner == CID_SELF)
1632 			qa.self_alloc_ns += delta;
1633 	}
1634 }
1635 
1636 /*
1637  * usable_cids = self_cids & avail_cids. The inputs have separate writers,
1638  * apply_partition() and qmap_sub_ecaps_updated(), so the result is rebuilt in
1639  * full under the partition guard, in scratch first so that readers never see
1640  * self_cids alone.
1641  */
1642 static void refresh_usable(void)
1643 {
1644 	cmask_and(&qa.usable_scratch.mask, &qa.self_cids.mask, &qa.avail_cids.mask);
1645 	cmask_copy(&qa.usable_cids.mask, &qa.usable_scratch.mask);
1646 }
1647 
1648 /*
1649  * apply_partition - execute the plan compute_partition() built
1650  *
1651  * Turn the owner map into the per-child, shared and self cmasks and issue the
1652  * grant/revoke kfuncs as a delta against each child's previous grant. If no
1653  * excl cid, evict every child.
1654  */
1655 __noinline void apply_partition(void)
1656 {
1657 	s32 nr_cids = qa.nr_cids;
1658 	s32 nr_shared = qa.part.nr_shared;
1659 	s32 i, cid;
1660 
1661 	if (nr_cids < 0 || nr_cids > SCX_QMAP_MAX_CPUS ||
1662 	    nr_shared < 0 || nr_shared > MAX_PARTS) {
1663 		scx_bpf_error("-ERANGE");
1664 		return;
1665 	}
1666 
1667 	/* no excl cpu: run own tasks on the held shares, evict children */
1668 	if (!qa.part.nr_excl) {
1669 		cmask_copy(&qa.self_cids.mask, &qa.held_shared.mask);
1670 		refresh_usable();
1671 		bpf_for(i, 0, MAX_SUB_SCHEDS)
1672 			if (qa.sub_sched_ctxs[i].cgroup_id)
1673 				scx_bpf_sub_kill(qa.sub_sched_ctxs[i].cgroup_id,
1674 						 "parent holds no excl cpu to distribute");
1675 		return;
1676 	}
1677 
1678 	/*
1679 	 * Snapshot the old pool. The per-child revoke below clears ENQ_IMMED on
1680 	 * the previously-granted pool, so a cid that left the pool (now a
1681 	 * sibling's excl) doesn't keep a stale ENQ_IMMED on its last holder.
1682 	 */
1683 	cmask_copy(&qa.prev_rr_cids.mask, &qa.rr_cids.mask);
1684 
1685 	/* turn the owner map into the rr pool, per-child excl, and self sets */
1686 	cmask_init(&qa.rr_cids.mask, 0, nr_cids);
1687 	cmask_init(&qa.self_cids.mask, 0, nr_cids);
1688 
1689 	/* snapshot each child's grant, then rebuild the new sets below */
1690 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1691 		cmask_copy(&qa.sub_sched_ctxs[i].prev_granted.mask,
1692 			   &qa.sub_sched_ctxs[i].granted_cids.mask);
1693 		cmask_init(&qa.sub_sched_ctxs[i].granted_cids.mask, 0, nr_cids);
1694 	}
1695 
1696 	bpf_for(i, 0, nr_shared)
1697 		cmask_set(qa.part.shared_cids[i], &qa.rr_cids.mask);
1698 	bpf_for(cid, 0, nr_cids) {
1699 		s32 o = qa.part.cid_owner[cid];
1700 
1701 		if (cmask_test(cid, &qa.rr_cids.mask))
1702 			continue;
1703 		if (o >= 0 && o < MAX_SUB_SCHEDS)
1704 			cmask_set(cid, &qa.sub_sched_ctxs[o].granted_cids.mask);
1705 		else if (o == CID_SELF)
1706 			cmask_set(cid, &qa.self_cids.mask);
1707 	}
1708 	refresh_usable();
1709 
1710 	/*
1711 	 * Apply each child's exclusive cids as a delta against its previous
1712 	 * grant. Separately clear the previous shared grant (ENQ_IMMED on the
1713 	 * old pool), covering cids still pooled and cids that left for a
1714 	 * sibling's excl. The current holder is granted the new pool below.
1715 	 */
1716 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
1717 		struct sub_sched_ctx __arena *ssc = &qa.sub_sched_ctxs[i];
1718 		u64 cgid = ssc->cgroup_id;
1719 
1720 		if (!cgid)
1721 			continue;
1722 
1723 		cmask_andnot(&qa.to_revoke_cids.mask, &ssc->prev_granted.mask, &ssc->granted_cids.mask);
1724 		cmask_andnot(&qa.to_grant_cids.mask, &ssc->granted_cids.mask, &ssc->prev_granted.mask);
1725 
1726 		scx_bpf_sub_revoke(cgid, SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1727 				   &qa.prev_rr_cids.mask);
1728 		scx_bpf_sub_revoke(cgid, SCX_CAP_ENQ | SCX_CAP_PREEMPT |
1729 				   SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1730 				   &qa.to_revoke_cids.mask);
1731 		scx_bpf_sub_grant(cgid, SCX_CAP_ENQ | SCX_CAP_PREEMPT |
1732 				  SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1733 				  &qa.to_grant_cids.mask, NULL);
1734 	}
1735 
1736 	/* the current holder of the shared pool gets ENQ_IMMED on all of it */
1737 	if (nr_shared) {
1738 		s32 pos = qa.part.rr_pos;
1739 		u64 holder_cgid;
1740 
1741 		if (pos < 0 || pos >= MAX_PARTS) {
1742 			scx_bpf_error("-ERANGE");
1743 			return;
1744 		}
1745 
1746 		holder_cgid = qa.part.rr_slots[pos];	/* 0 = self, nothing to grant */
1747 		if (holder_cgid)
1748 			scx_bpf_sub_grant(holder_cgid,
1749 					  SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1750 					  &qa.rr_cids.mask, NULL);
1751 	}
1752 }
1753 
1754 /**
1755  * execute_partition - Run pending partition updates
1756  *
1757  * The rr timer is the backstop if the loop reaches its iteration limit.
1758  */
1759 static void execute_partition(void)
1760 {
1761 	u64 pending;
1762 	s32 i;
1763 
1764 	bpf_for(i, 0, 1024) {
1765 		if (!part_try_start())
1766 			break;
1767 
1768 		pending = __sync_fetch_and_and(&part_pending, 0);
1769 		if (pending & PART_REDISTRIBUTE) {
1770 			/* charge elapsed time before repartitioning */
1771 			account_alloc();
1772 			compute_partition();
1773 			apply_partition();
1774 		} else if (pending & PART_REFRESH) {
1775 			refresh_usable();
1776 		}
1777 
1778 		/*
1779 		 * Requests are published before trying the guard. Releasing it
1780 		 * before checking pending work ensures a racing request is
1781 		 * either observed here or handled by a caller that acquires the
1782 		 * guard.
1783 		 */
1784 		part_end();
1785 		if (!__sync_fetch_and_or(&part_pending, 0))
1786 			break;
1787 	}
1788 }
1789 
1790 static void redistribute(void)
1791 {
1792 	__sync_fetch_and_or(&part_pending, PART_REDISTRIBUTE);
1793 	execute_partition();
1794 }
1795 
1796 /*
1797  * Owner id for a @sched value of ops.sub_cid_sched_updated(). A child is
1798  * attached before its first task runs and its tasks are re-homed before it
1799  * detaches, so a child's cgroup id always has its slot.
1800  */
1801 static s32 cid_sched_owner(u64 sched)
1802 {
1803 	s32 i;
1804 
1805 	if (sched == SCX_CID_SCHED_NONE)
1806 		return CID_NONE;
1807 	if (sched == SCX_CID_SCHED_SELF)
1808 		return CID_SELF;
1809 	bpf_for(i, 0, MAX_SUB_SCHEDS)
1810 		if (qa.sub_sched_ctxs[i].cgroup_id == sched)
1811 			return i;
1812 	return CID_NONE;
1813 }
1814 
1815 /* used_ns[] is summed from every cpu, hence the atomic adds */
1816 static void cid_sched_charge(s32 cid, u64 now)
1817 {
1818 	s32 owner = qa.cid_sched[cid];
1819 	u64 delta = now - qa.cid_sched_since[cid];
1820 
1821 	if (owner >= 0 && owner < MAX_SUB_SCHEDS)
1822 		__sync_fetch_and_add(&qa.used_ns[owner], delta);
1823 	else if (owner == CID_SELF)
1824 		__sync_fetch_and_add(&qa.self_used_ns, delta);
1825 	qa.cid_sched_since[cid] = now;
1826 }
1827 
1828 void BPF_STRUCT_OPS(qmap_sub_cid_sched_updated, s32 cid, u64 sched)
1829 {
1830 	if (cid < 0 || cid >= SCX_QMAP_MAX_CPUS)
1831 		return;
1832 
1833 	cid_sched_charge(cid, bpf_ktime_get_ns());
1834 	qa.cid_sched[cid] = cid_sched_owner(sched);
1835 }
1836 
1837 /*
1838  * Snapshot the used time for the stats display: the closed intervals plus the
1839  * ones still open. The reads race the notifications on other cpus, so an
1840  * interval closing in between can be missing from one snapshot or counted in
1841  * two. The next snapshot evens it out and the display floors a negative
1842  * difference at zero.
1843  */
1844 static void snapshot_used(void)
1845 {
1846 	u64 now = bpf_ktime_get_ns();
1847 	s32 nr_cids = qa.nr_cids;
1848 	s32 cid, i;
1849 
1850 	if (nr_cids < 0 || nr_cids > SCX_QMAP_MAX_CPUS)
1851 		return;
1852 
1853 	bpf_for(i, 0, MAX_SUB_SCHEDS)
1854 		qa.used_snap_ns[i] = qa.used_ns[i];
1855 	qa.self_used_snap_ns = qa.self_used_ns;
1856 
1857 	bpf_for(cid, 0, nr_cids) {
1858 		s32 owner = qa.cid_sched[cid];
1859 		u64 since = qa.cid_sched_since[cid];
1860 
1861 		/* restarted after @now by a notification on another cpu */
1862 		if (since > now)
1863 			continue;
1864 		if (owner >= 0 && owner < MAX_SUB_SCHEDS)
1865 			qa.used_snap_ns[owner] += now - since;
1866 		else if (owner == CID_SELF)
1867 			qa.self_used_snap_ns += now - since;
1868 	}
1869 }
1870 
1871 /*
1872  * Userspace pokes this (PROG_RUN) to bring alloc_ns[] and the used snapshot
1873  * current before reading them for the stats display. Skipping the alloc part
1874  * when the partition guard is held is fine - alloc_ts is untouched, so the
1875  * elapsed time is charged next time.
1876  */
1877 SEC("syscall")
1878 int flush_alloc(void *ctx)
1879 {
1880 	snapshot_used();
1881 	if (part_try_start()) {
1882 		account_alloc();
1883 		part_end();
1884 		execute_partition();
1885 	}
1886 	return 0;
1887 }
1888 
1889 /*
1890  * Hand the shared pool to the next participant in the rotation. Self's turn
1891  * just revokes the pool back to this sched. A child's turn grants it ENQ_IMMED
1892  * on the entire pool. As only excl-held cids are time-shared, a wall-clock
1893  * rotation works. Driven by the round-robin timer.
1894  */
1895 static void rr_advance(void)
1896 {
1897 	s32 nr_shared, old_pos, new_pos;
1898 	u64 old_cgid, new_cgid;
1899 	u32 nr_rr;		/* unsigned for % */
1900 
1901 	/* a redistribute holds the partition and rebuilds the pool, so skip */
1902 	if (!part_try_start())
1903 		return;
1904 
1905 	nr_rr = qa.part.nr_rr;
1906 	nr_shared = qa.part.nr_shared;
1907 
1908 	if (nr_shared < 0 || nr_shared > MAX_PARTS) {
1909 		scx_bpf_error("-ERANGE");
1910 		return;
1911 	}
1912 
1913 	if (nr_shared && nr_rr >= 2) {
1914 		/* close out the outgoing holder's pool time */
1915 		account_alloc();
1916 
1917 		old_pos = qa.part.rr_pos;
1918 		new_pos = (old_pos + 1) % nr_rr;
1919 		old_cgid = qa.part.rr_slots[old_pos];
1920 		new_cgid = qa.part.rr_slots[new_pos];
1921 		qa.part.rr_pos = new_pos;
1922 
1923 		/*
1924 		 * Move the ENQ_IMMED cap to the next participant. The shared
1925 		 * cids stay marked CID_SHARED. qmap_dispatch() resolves the
1926 		 * live holder via rr_pos without the guard, so a dispatch
1927 		 * racing this handoff may reenqueue a task once. Harmless for a
1928 		 * time-share.
1929 		 */
1930 		if (old_cgid)
1931 			scx_bpf_sub_revoke(old_cgid,
1932 					   SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1933 					   &qa.rr_cids.mask);
1934 		if (new_cgid)
1935 			scx_bpf_sub_grant(new_cgid,
1936 					  SCX_CAP_ENQ_IMMED | SCX_CAP_PERF,
1937 					  &qa.rr_cids.mask, NULL);
1938 	}
1939 
1940 	part_end();
1941 
1942 	execute_partition();
1943 }
1944 
1945 /* advance the time-shared cid pool every round_robin_ns */
1946 static int round_robin_timerfn(void *map, int *key, struct bpf_timer *timer)
1947 {
1948 	rr_advance();
1949 	if (bpf_timer_start(timer, round_robin_ns, 0))
1950 		scx_bpf_error("failed to re-arm round-robin timer");
1951 	return 0;
1952 }
1953 
1954 /*
1955  * Custom cid layout for the cid-override test. On invalid input the kfunc
1956  * scx_error()s and aborts the scheduler.
1957  */
1958 s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init_cids)
1959 {
1960 	u32 nr_cpu_ids = scx_bpf_nr_cpu_ids();
1961 
1962 	if (!cid_override_mode)
1963 		return 0;
1964 
1965 	/* the arena arrays are sized SCX_QMAP_MAX_CPUS */
1966 	if (nr_cpu_ids > SCX_QMAP_MAX_CPUS) {
1967 		scx_bpf_error("nr_cpu_ids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
1968 			      nr_cpu_ids, SCX_QMAP_MAX_CPUS);
1969 		return -EINVAL;
1970 	}
1971 
1972 	scx_bpf_cid_override(qa.cid_override_cpu_to_cid, nr_cpu_ids,
1973 			     qa.cid_override_shard_start, cid_override_nr_shards);
1974 	return 0;
1975 }
1976 
1977 s32 BPF_STRUCT_OPS_SLEEPABLE(qmap_init)
1978 {
1979 	u8 __arena *slab;
1980 	u32 nr_pages, key = 0, i;
1981 	u32 nr_cids, nr_cpu_ids;
1982 	struct bpf_timer *timer;
1983 	s32 ret;
1984 
1985 	nr_cids = scx_bpf_nr_cids();
1986 	nr_cpu_ids = scx_bpf_nr_cpu_ids();
1987 
1988 	if (nr_cids > SCX_QMAP_MAX_CPUS) {
1989 		scx_bpf_error("nr_cids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
1990 			      nr_cids, SCX_QMAP_MAX_CPUS);
1991 		return -EINVAL;
1992 	}
1993 	if (nr_cpu_ids > SCX_QMAP_MAX_CPUS) {
1994 		scx_bpf_error("nr_cpu_ids=%u exceeds SCX_QMAP_MAX_CPUS=%d",
1995 			      nr_cpu_ids, SCX_QMAP_MAX_CPUS);
1996 		return -EINVAL;
1997 	}
1998 
1999 	/*
2000 	 * Allocate the task_ctx slab in arena and thread the entire slab onto
2001 	 * the free list. max_tasks is set by userspace before load. Each entry
2002 	 * is TASK_CTX_STRIDE bytes - task_ctx's trailing cpus_allowed flex
2003 	 * array extends into the stride tail.
2004 	 */
2005 	if (!max_tasks) {
2006 		scx_bpf_error("max_tasks must be > 0");
2007 		return -EINVAL;
2008 	}
2009 
2010 	nr_pages = (max_tasks * TASK_CTX_STRIDE + PAGE_SIZE - 1) / PAGE_SIZE;
2011 	slab = bpf_arena_alloc_pages(&arena, NULL, nr_pages, NUMA_NO_NODE, 0);
2012 	if (!slab) {
2013 		scx_bpf_error("failed to allocate task_ctx slab");
2014 		return -ENOMEM;
2015 	}
2016 	qa.task_ctxs = (task_ctx_t *)slab;
2017 
2018 	bpf_for(i, 0, 5)
2019 		qa.fifos[i].idx = i;
2020 
2021 	bpf_for(i, 0, max_tasks) {
2022 		task_ctx_t *cur = (task_ctx_t *)(slab + i * TASK_CTX_STRIDE);
2023 		task_ctx_t *next = (i + 1 < max_tasks) ?
2024 			(task_ctx_t *)(slab + (i + 1) * TASK_CTX_STRIDE) : NULL;
2025 		cur->next_free = next;
2026 	}
2027 	qa.task_free_head = (task_ctx_t *)slab;
2028 
2029 	/* cache the cid count, trusted to be <= SCX_QMAP_MAX_CPUS hereafter */
2030 	qa.nr_cids = nr_cids;
2031 
2032 	bpf_for(i, 0, nr_cids)
2033 		qa.cid_sched[i] = CID_NONE;
2034 
2035 	/* cmasks are embedded in qa, so they only need initializing */
2036 	cmask_init(&qa.idle_cids.mask, 0, nr_cids);
2037 	cmask_init(&qa.rr_cids.mask, 0, nr_cids);
2038 	cmask_init(&qa.prev_rr_cids.mask, 0, nr_cids);
2039 	cmask_init(&qa.self_cids.mask, 0, nr_cids);
2040 	cmask_init(&qa.avail_cids.mask, 0, nr_cids);
2041 	cmask_init(&qa.usable_cids.mask, 0, nr_cids);
2042 	cmask_init(&qa.to_revoke_cids.mask, 0, nr_cids);
2043 	cmask_init(&qa.to_grant_cids.mask, 0, nr_cids);
2044 	cmask_init(&qa.usable_scratch.mask, 0, nr_cids);
2045 	cmask_init(&qa.held_excl.mask, 0, nr_cids);
2046 	cmask_init(&qa.held_shared.mask, 0, nr_cids);
2047 
2048 	scx_bpf_sub_caps(0, SCX_CAP_ENQ, &qa.held_excl.mask);
2049 	scx_bpf_sub_caps(0, SCX_CAP_ENQ_IMMED, &qa.held_shared.mask);
2050 	cmask_andnot(&qa.held_shared.mask, &qa.held_shared.mask, &qa.held_excl.mask);
2051 
2052 	bpf_for(i, 0, MAX_SUB_SCHEDS) {
2053 		cmask_init(&qa.sub_sched_ctxs[i].granted_cids.mask, 0, nr_cids);
2054 		cmask_init(&qa.sub_sched_ctxs[i].prev_granted.mask, 0, nr_cids);
2055 	}
2056 
2057 	/*
2058 	 * The root starts holding every cid and gets no ecaps notifications, so
2059 	 * its avail set is fixed here. qmap_sub_ecaps_updated() maintains the
2060 	 * per-cid state as effective caps settle, and redistribute() rebuilds
2061 	 * owner and self from held caps. A non-root node starts with nothing.
2062 	 */
2063 	bpf_for(i, 0, nr_cids) {
2064 		if (!sub_cgroup_id) {
2065 			cmask_set(i, &qa.self_cids.mask);
2066 			cmask_set(i, &qa.avail_cids.mask);
2067 			cmask_set(i, &qa.usable_cids.mask);
2068 			qa.part.cid_owner[i] = CID_SELF;
2069 		} else {
2070 			qa.part.cid_owner[i] = CID_NONE;
2071 		}
2072 	}
2073 	qa.part.nr_shared = 0;
2074 
2075 	ret = scx_bpf_create_dsq(SHARED_DSQ, -1);
2076 	if (ret) {
2077 		scx_bpf_error("failed to create DSQ %d (%d)", SHARED_DSQ, ret);
2078 		return ret;
2079 	}
2080 
2081 	ret = scx_bpf_create_dsq(HIGHPRI_DSQ, -1);
2082 	if (ret) {
2083 		scx_bpf_error("failed to create DSQ %d (%d)", HIGHPRI_DSQ, ret);
2084 		return ret;
2085 	}
2086 
2087 	ret = scx_bpf_create_dsq(LOWPRI_DSQ, -1);
2088 	if (ret)
2089 		return ret;
2090 
2091 	timer = bpf_map_lookup_elem(&monitor_timer, &key);
2092 	if (!timer)
2093 		return -ESRCH;
2094 	bpf_timer_init(timer, &monitor_timer, CLOCK_MONOTONIC);
2095 	bpf_timer_set_callback(timer, monitor_timerfn);
2096 	ret = bpf_timer_start(timer, ONE_SEC_IN_NS, 0);
2097 	if (ret)
2098 		return ret;
2099 
2100 	if (__COMPAT_has_generic_reenq()) {
2101 		/* see lowpri_timerfn() */
2102 		timer = bpf_map_lookup_elem(&lowpri_timer, &key);
2103 		if (!timer)
2104 			return -ESRCH;
2105 		bpf_timer_init(timer, &lowpri_timer, CLOCK_MONOTONIC);
2106 		bpf_timer_set_callback(timer, lowpri_timerfn);
2107 		ret = bpf_timer_start(timer, LOWPRI_INTV_NS, 0);
2108 		if (ret)
2109 			return ret;
2110 	}
2111 
2112 	/* sub-sched: drive the boundary-cid round-robin from a bpf timer */
2113 	timer = bpf_map_lookup_elem(&round_robin_timer, &key);
2114 	if (!timer)
2115 		return -ESRCH;
2116 	bpf_timer_init(timer, &round_robin_timer, CLOCK_MONOTONIC);
2117 	bpf_timer_set_callback(timer, round_robin_timerfn);
2118 	ret = bpf_timer_start(timer, round_robin_ns, 0);
2119 	if (ret)
2120 		return ret;
2121 
2122 	return 0;
2123 }
2124 
2125 void BPF_STRUCT_OPS(qmap_exit, struct scx_exit_info *ei)
2126 {
2127 	UEI_RECORD(uei, ei);
2128 }
2129 
2130 /*
2131  * Seed a new sub slot with the cgroup's current weight. The kernel delivers
2132  * ops.cpuctl_set_weight() only on value-changing writes, so a weight set
2133  * before the sub attached would otherwise go unnoticed.
2134  */
2135 static u32 cgrp_cur_weight(u64 cgid)
2136 {
2137 	struct cgroup_subsys_state *css;
2138 	struct cgroup *cgrp;
2139 	u32 weight = 100;
2140 
2141 	cgrp = bpf_cgroup_from_id(cgid);
2142 	if (!cgrp)
2143 		return weight;
2144 
2145 	css = BPF_CORE_READ(cgrp, subsys[cpu_cgrp_id]);
2146 	if (css) {
2147 		struct task_group *tg = container_of(css, struct task_group, css);
2148 		u32 w = BPF_CORE_READ(tg, scx.weight);
2149 
2150 		if (w)
2151 			weight = w;
2152 	}
2153 	bpf_cgroup_release(cgrp);
2154 	return weight;
2155 }
2156 
2157 s32 BPF_STRUCT_OPS(qmap_sub_attach, struct scx_sub_attach_args *args)
2158 {
2159 	s32 i;
2160 
2161 	/* as long as there is at least one excl cpu, children can attach */
2162 	if (!cmask_weight(&qa.held_excl.mask))
2163 		return -ENOSPC;
2164 
2165 	for (i = 0; i < MAX_SUB_SCHEDS; i++) {
2166 		if (qa.sub_sched_ctxs[i].cgroup_id)
2167 			continue;
2168 
2169 		qa.sub_sched_ctxs[i].cgroup_id = args->ops->sub_cgroup_id;
2170 		qa.sub_sched_ctxs[i].weight = cgrp_cur_weight(args->ops->sub_cgroup_id);
2171 		qa.nr_sub_scheds++;
2172 		bpf_printk("attaching sub-sched[%d] on %s", i, args->cgroup_path);
2173 		redistribute();
2174 		return 0;
2175 	}
2176 
2177 	return -ENOSPC;
2178 }
2179 
2180 void BPF_STRUCT_OPS(qmap_sub_detach, struct scx_sub_detach_args *args)
2181 {
2182 	u64 cgid = args->ops->sub_cgroup_id;
2183 	s32 nr_cids = qa.nr_cids;
2184 	s32 pos = qa.part.rr_pos;
2185 	u64 holder_cgid;
2186 	bool reset_pool;
2187 	s32 i, cid;
2188 
2189 	if (nr_cids < 0 || nr_cids > SCX_QMAP_MAX_CPUS || pos < 0 || pos >= MAX_PARTS) {
2190 		scx_bpf_error("-ERANGE");
2191 		return;
2192 	}
2193 
2194 	/*
2195 	 * The child's caps are gone without a report, see ops.sub_detach().
2196 	 * cpuperf targets persist until the next write. Reset the child's excl
2197 	 * cids, and the pool unless another child holds it: that child's revoke
2198 	 * report or its own detach resets it then.
2199 	 */
2200 	holder_cgid = qa.part.rr_slots[pos];
2201 	reset_pool = !holder_cgid || holder_cgid == cgid;
2202 
2203 	for (i = 0; i < MAX_SUB_SCHEDS; i++) {
2204 		if (qa.sub_sched_ctxs[i].cgroup_id != cgid)
2205 			continue;
2206 
2207 		bpf_arena_for(cid, 0, nr_cids)
2208 			if (cmask_test(cid, &qa.sub_sched_ctxs[i].granted_cids.mask) ||
2209 			    (reset_pool && cmask_test(cid, &qa.rr_cids.mask)))
2210 				scx_bpf_cidperf_set(cid, SCX_CPUPERF_ONE);
2211 
2212 		qa.sub_sched_ctxs[i].cgroup_id = 0;
2213 		qa.sub_sched_ctxs[i].weight = 100;
2214 		cmask_init(&qa.sub_sched_ctxs[i].granted_cids.mask, 0, qa.nr_cids);
2215 		qa.nr_sub_scheds--;
2216 		bpf_printk("detaching sub-sched[%d] on %s", i, args->cgroup_path);
2217 		redistribute();
2218 		break;
2219 	}
2220 }
2221 
2222 void BPF_STRUCT_OPS(qmap_sub_caps_updated, const struct scx_cmask *cmask, u64 caps)
2223 {
2224 	/* our held caps changed, redistribute */
2225 	redistribute();
2226 }
2227 
2228 void BPF_STRUCT_OPS(qmap_sub_ecaps_updated, s32 cid, u64 before, u64 after)
2229 {
2230 	/*
2231 	 * Effective caps updated. Track which cids hold shared caps so a self
2232 	 * task placed there enqueues IMMED, and which cids have ENQ_IMMED in
2233 	 * effect at all (avail, see the header comment).
2234 	 */
2235 	if (after & SCX_CAP_ENQ_IMMED) {
2236 		qa.cid_shared[cid] = (after & SCX_CAP_ENQ) ? 0 : 1;
2237 		cmask_set(cid, &qa.avail_cids.mask);
2238 	} else {
2239 		qa.cid_shared[cid] = 0;
2240 		cmask_clear(cid, &qa.avail_cids.mask);
2241 	}
2242 
2243 	__sync_fetch_and_or(&part_pending, PART_REFRESH);
2244 	execute_partition();
2245 }
2246 
2247 void BPF_STRUCT_OPS(qmap_sub_child_ecaps_updated, u64 cgroup_id, s32 cid, u64 before,
2248 		    u64 after)
2249 {
2250 	__sync_fetch_and_add(&qa.nr_child_ecaps, 1);
2251 
2252 	/* a child's last target must not outlive its PERF cap */
2253 	if ((before & ~after) & SCX_CAP_PERF)
2254 		scx_bpf_cidperf_set(cid, SCX_CPUPERF_ONE);
2255 }
2256 
2257 SCX_OPS_CID_DEFINE(qmap_ops,
2258 	       .flags			= SCX_OPS_ENQ_EXITING | SCX_OPS_TID_TO_TASK,
2259 	       .select_cid		= (void *)qmap_select_cid,
2260 	       .enqueue			= (void *)qmap_enqueue,
2261 	       .dequeue			= (void *)qmap_dequeue,
2262 	       .dispatch		= (void *)qmap_dispatch,
2263 	       .tick			= (void *)qmap_tick,
2264 	       .core_sched_before	= (void *)qmap_core_sched_before,
2265 	       .set_cmask		= (void *)qmap_set_cmask,
2266 	       .update_idle		= (void *)qmap_update_idle,
2267 	       .init_task		= (void *)qmap_init_task,
2268 	       .exit_task		= (void *)qmap_exit_task,
2269 	       .dump			= (void *)qmap_dump,
2270 	       .dump_cid		= (void *)qmap_dump_cid,
2271 	       .dump_task		= (void *)qmap_dump_task,
2272 	       .cpuctl_init		= (void *)qmap_cpuctl_init,
2273 	       .cpuctl_set_weight	= (void *)qmap_cpuctl_set_weight,
2274 	       .cpuctl_set_bandwidth	= (void *)qmap_cpuctl_set_bandwidth,
2275 	       .cpuctl_move		= (void *)qmap_cpuctl_move,
2276 	       .sub_attach		= (void *)qmap_sub_attach,
2277 	       .sub_detach		= (void *)qmap_sub_detach,
2278 	       .sub_caps_updated	= (void *)qmap_sub_caps_updated,
2279 	       .sub_ecaps_updated	= (void *)qmap_sub_ecaps_updated,
2280 	       .sub_cid_sched_updated	= (void *)qmap_sub_cid_sched_updated,
2281 	       .sub_child_ecaps_updated	= (void *)qmap_sub_child_ecaps_updated,
2282 	       .init_cids		= (void *)qmap_init_cids,
2283 	       .init			= (void *)qmap_init,
2284 	       .exit			= (void *)qmap_exit,
2285 	       .timeout_ms		= 5000U,
2286 	       .name			= "qmap");
2287