xref: /linux/tools/testing/selftests/sched_ext/dequeue.bpf.c (revision 658ad2259b3e95aea21e548f7ca3440f620bf95f)
1*658ad225SAndrea Righi // SPDX-License-Identifier: GPL-2.0
2*658ad225SAndrea Righi /*
3*658ad225SAndrea Righi  * A scheduler that validates ops.dequeue() is called correctly:
4*658ad225SAndrea Righi  * - Tasks dispatched to terminal DSQs (local, global) bypass the BPF
5*658ad225SAndrea Righi  *   scheduler entirely: no ops.dequeue() should be called
6*658ad225SAndrea Righi  * - Tasks dispatched to user DSQs from ops.enqueue() enter BPF custody:
7*658ad225SAndrea Righi  *   ops.dequeue() must be called when they leave custody
8*658ad225SAndrea Righi  * - Every ops.enqueue() dispatch to non-terminal DSQs is followed by
9*658ad225SAndrea Righi  *   exactly one ops.dequeue() (validate 1:1 pairing and state machine)
10*658ad225SAndrea Righi  *
11*658ad225SAndrea Righi  * Copyright (c) 2026 NVIDIA Corporation.
12*658ad225SAndrea Righi  */
13*658ad225SAndrea Righi 
14*658ad225SAndrea Righi #include <scx/common.bpf.h>
15*658ad225SAndrea Righi 
16*658ad225SAndrea Righi #define SHARED_DSQ	0
17*658ad225SAndrea Righi 
18*658ad225SAndrea Righi /*
19*658ad225SAndrea Righi  * BPF internal queue.
20*658ad225SAndrea Righi  *
21*658ad225SAndrea Righi  * Tasks are stored here and consumed from ops.dispatch(), validating that
22*658ad225SAndrea Righi  * tasks on BPF internal structures still get ops.dequeue() when they
23*658ad225SAndrea Righi  * leave.
24*658ad225SAndrea Righi  */
25*658ad225SAndrea Righi struct {
26*658ad225SAndrea Righi 	__uint(type, BPF_MAP_TYPE_QUEUE);
27*658ad225SAndrea Righi 	__uint(max_entries, 32768);
28*658ad225SAndrea Righi 	__type(value, s32);
29*658ad225SAndrea Righi } global_queue SEC(".maps");
30*658ad225SAndrea Righi 
31*658ad225SAndrea Righi char _license[] SEC("license") = "GPL";
32*658ad225SAndrea Righi 
33*658ad225SAndrea Righi UEI_DEFINE(uei);
34*658ad225SAndrea Righi 
35*658ad225SAndrea Righi /*
36*658ad225SAndrea Righi  * Counters to track the lifecycle of tasks:
37*658ad225SAndrea Righi  * - enqueue_cnt: Number of times ops.enqueue() was called
38*658ad225SAndrea Righi  * - dequeue_cnt: Number of times ops.dequeue() was called (any type)
39*658ad225SAndrea Righi  * - dispatch_dequeue_cnt: Number of regular dispatch dequeues (no flag)
40*658ad225SAndrea Righi  * - change_dequeue_cnt: Number of property change dequeues
41*658ad225SAndrea Righi  * - bpf_queue_full: Number of times the BPF internal queue was full
42*658ad225SAndrea Righi  */
43*658ad225SAndrea Righi u64 enqueue_cnt, dequeue_cnt, dispatch_dequeue_cnt, change_dequeue_cnt, bpf_queue_full;
44*658ad225SAndrea Righi 
45*658ad225SAndrea Righi /*
46*658ad225SAndrea Righi  * Test scenarios:
47*658ad225SAndrea Righi  * 0) Dispatch to local DSQ from ops.select_cpu() (terminal DSQ, bypasses BPF
48*658ad225SAndrea Righi  *    scheduler, no dequeue callbacks)
49*658ad225SAndrea Righi  * 1) Dispatch to global DSQ from ops.select_cpu() (terminal DSQ, bypasses BPF
50*658ad225SAndrea Righi  *    scheduler, no dequeue callbacks)
51*658ad225SAndrea Righi  * 2) Dispatch to shared user DSQ from ops.select_cpu() (enters BPF scheduler,
52*658ad225SAndrea Righi  *    dequeue callbacks expected)
53*658ad225SAndrea Righi  * 3) Dispatch to local DSQ from ops.enqueue() (terminal DSQ, bypasses BPF
54*658ad225SAndrea Righi  *    scheduler, no dequeue callbacks)
55*658ad225SAndrea Righi  * 4) Dispatch to global DSQ from ops.enqueue() (terminal DSQ, bypasses BPF
56*658ad225SAndrea Righi  *    scheduler, no dequeue callbacks)
57*658ad225SAndrea Righi  * 5) Dispatch to shared user DSQ from ops.enqueue() (enters BPF scheduler,
58*658ad225SAndrea Righi  *    dequeue callbacks expected)
59*658ad225SAndrea Righi  * 6) BPF internal queue from ops.enqueue(): store task PIDs in ops.enqueue(),
60*658ad225SAndrea Righi  *    consume in ops.dispatch() and dispatch to local DSQ (validates dequeue
61*658ad225SAndrea Righi  *    for tasks stored in internal BPF data structures)
62*658ad225SAndrea Righi  */
63*658ad225SAndrea Righi u32 test_scenario;
64*658ad225SAndrea Righi 
65*658ad225SAndrea Righi /*
66*658ad225SAndrea Righi  * Per-task state to track lifecycle and validate workflow semantics.
67*658ad225SAndrea Righi  * State transitions:
68*658ad225SAndrea Righi  *   NONE -> ENQUEUED (on enqueue)
69*658ad225SAndrea Righi  *   NONE -> DISPATCHED (on direct dispatch to terminal DSQ)
70*658ad225SAndrea Righi  *   ENQUEUED -> DISPATCHED (on dispatch dequeue)
71*658ad225SAndrea Righi  *   DISPATCHED -> NONE (on property change dequeue or re-enqueue)
72*658ad225SAndrea Righi  *   ENQUEUED -> NONE (on property change dequeue before dispatch)
73*658ad225SAndrea Righi  */
74*658ad225SAndrea Righi enum task_state {
75*658ad225SAndrea Righi 	TASK_NONE = 0,
76*658ad225SAndrea Righi 	TASK_ENQUEUED,
77*658ad225SAndrea Righi 	TASK_DISPATCHED,
78*658ad225SAndrea Righi };
79*658ad225SAndrea Righi 
80*658ad225SAndrea Righi struct task_ctx {
81*658ad225SAndrea Righi 	enum task_state state; /* Current state in the workflow */
82*658ad225SAndrea Righi 	u64 enqueue_seq;       /* Sequence number for debugging */
83*658ad225SAndrea Righi };
84*658ad225SAndrea Righi 
85*658ad225SAndrea Righi struct {
86*658ad225SAndrea Righi 	__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
87*658ad225SAndrea Righi 	__uint(map_flags, BPF_F_NO_PREALLOC);
88*658ad225SAndrea Righi 	__type(key, int);
89*658ad225SAndrea Righi 	__type(value, struct task_ctx);
90*658ad225SAndrea Righi } task_ctx_stor SEC(".maps");
91*658ad225SAndrea Righi 
92*658ad225SAndrea Righi static struct task_ctx *try_lookup_task_ctx(struct task_struct *p)
93*658ad225SAndrea Righi {
94*658ad225SAndrea Righi 	return bpf_task_storage_get(&task_ctx_stor, p, 0, 0);
95*658ad225SAndrea Righi }
96*658ad225SAndrea Righi 
97*658ad225SAndrea Righi s32 BPF_STRUCT_OPS(dequeue_select_cpu, struct task_struct *p,
98*658ad225SAndrea Righi 		   s32 prev_cpu, u64 wake_flags)
99*658ad225SAndrea Righi {
100*658ad225SAndrea Righi 	struct task_ctx *tctx;
101*658ad225SAndrea Righi 
102*658ad225SAndrea Righi 	tctx = try_lookup_task_ctx(p);
103*658ad225SAndrea Righi 	if (!tctx)
104*658ad225SAndrea Righi 		return prev_cpu;
105*658ad225SAndrea Righi 
106*658ad225SAndrea Righi 	switch (test_scenario) {
107*658ad225SAndrea Righi 	case 0:
108*658ad225SAndrea Righi 		/*
109*658ad225SAndrea Righi 		 * Direct dispatch to the local DSQ.
110*658ad225SAndrea Righi 		 *
111*658ad225SAndrea Righi 		 * Task bypasses BPF scheduler entirely: no enqueue
112*658ad225SAndrea Righi 		 * tracking, no ops.dequeue() callbacks.
113*658ad225SAndrea Righi 		 */
114*658ad225SAndrea Righi 		scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, SCX_SLICE_DFL, 0);
115*658ad225SAndrea Righi 		tctx->state = TASK_DISPATCHED;
116*658ad225SAndrea Righi 		break;
117*658ad225SAndrea Righi 	case 1:
118*658ad225SAndrea Righi 		/*
119*658ad225SAndrea Righi 		 * Direct dispatch to the global DSQ.
120*658ad225SAndrea Righi 		 *
121*658ad225SAndrea Righi 		 * Task bypasses BPF scheduler entirely: no enqueue
122*658ad225SAndrea Righi 		 * tracking, no ops.dequeue() callbacks.
123*658ad225SAndrea Righi 		 */
124*658ad225SAndrea Righi 		scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, 0);
125*658ad225SAndrea Righi 		tctx->state = TASK_DISPATCHED;
126*658ad225SAndrea Righi 		break;
127*658ad225SAndrea Righi 	case 2:
128*658ad225SAndrea Righi 		/*
129*658ad225SAndrea Righi 		 * Dispatch to a shared user DSQ.
130*658ad225SAndrea Righi 		 *
131*658ad225SAndrea Righi 		 * Task enters BPF scheduler management: track
132*658ad225SAndrea Righi 		 * enqueue/dequeue lifecycle and validate state
133*658ad225SAndrea Righi 		 * transitions.
134*658ad225SAndrea Righi 		 */
135*658ad225SAndrea Righi 		if (tctx->state == TASK_ENQUEUED)
136*658ad225SAndrea Righi 			scx_bpf_error("%d (%s): enqueue while in ENQUEUED state seq=%llu",
137*658ad225SAndrea Righi 				      p->pid, p->comm, tctx->enqueue_seq);
138*658ad225SAndrea Righi 
139*658ad225SAndrea Righi 		scx_bpf_dsq_insert(p, SHARED_DSQ, SCX_SLICE_DFL, 0);
140*658ad225SAndrea Righi 
141*658ad225SAndrea Righi 		__sync_fetch_and_add(&enqueue_cnt, 1);
142*658ad225SAndrea Righi 
143*658ad225SAndrea Righi 		tctx->state = TASK_ENQUEUED;
144*658ad225SAndrea Righi 		tctx->enqueue_seq++;
145*658ad225SAndrea Righi 		break;
146*658ad225SAndrea Righi 	}
147*658ad225SAndrea Righi 
148*658ad225SAndrea Righi 	return prev_cpu;
149*658ad225SAndrea Righi }
150*658ad225SAndrea Righi 
151*658ad225SAndrea Righi void BPF_STRUCT_OPS(dequeue_enqueue, struct task_struct *p, u64 enq_flags)
152*658ad225SAndrea Righi {
153*658ad225SAndrea Righi 	struct task_ctx *tctx;
154*658ad225SAndrea Righi 	s32 pid = p->pid;
155*658ad225SAndrea Righi 
156*658ad225SAndrea Righi 	tctx = try_lookup_task_ctx(p);
157*658ad225SAndrea Righi 	if (!tctx)
158*658ad225SAndrea Righi 		return;
159*658ad225SAndrea Righi 
160*658ad225SAndrea Righi 	switch (test_scenario) {
161*658ad225SAndrea Righi 	case 3:
162*658ad225SAndrea Righi 		/*
163*658ad225SAndrea Righi 		 * Direct dispatch to the local DSQ.
164*658ad225SAndrea Righi 		 *
165*658ad225SAndrea Righi 		 * Task bypasses BPF scheduler entirely: no enqueue
166*658ad225SAndrea Righi 		 * tracking, no ops.dequeue() callbacks.
167*658ad225SAndrea Righi 		 */
168*658ad225SAndrea Righi 		scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, SCX_SLICE_DFL, enq_flags);
169*658ad225SAndrea Righi 		tctx->state = TASK_DISPATCHED;
170*658ad225SAndrea Righi 		break;
171*658ad225SAndrea Righi 	case 4:
172*658ad225SAndrea Righi 		/*
173*658ad225SAndrea Righi 		 * Direct dispatch to the global DSQ.
174*658ad225SAndrea Righi 		 *
175*658ad225SAndrea Righi 		 * Task bypasses BPF scheduler entirely: no enqueue
176*658ad225SAndrea Righi 		 * tracking, no ops.dequeue() callbacks.
177*658ad225SAndrea Righi 		 */
178*658ad225SAndrea Righi 		scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
179*658ad225SAndrea Righi 		tctx->state = TASK_DISPATCHED;
180*658ad225SAndrea Righi 		break;
181*658ad225SAndrea Righi 	case 5:
182*658ad225SAndrea Righi 		/*
183*658ad225SAndrea Righi 		 * Dispatch to shared user DSQ.
184*658ad225SAndrea Righi 		 *
185*658ad225SAndrea Righi 		 * Task enters BPF scheduler management: track
186*658ad225SAndrea Righi 		 * enqueue/dequeue lifecycle and validate state
187*658ad225SAndrea Righi 		 * transitions.
188*658ad225SAndrea Righi 		 */
189*658ad225SAndrea Righi 		if (tctx->state == TASK_ENQUEUED)
190*658ad225SAndrea Righi 			scx_bpf_error("%d (%s): enqueue while in ENQUEUED state seq=%llu",
191*658ad225SAndrea Righi 				      p->pid, p->comm, tctx->enqueue_seq);
192*658ad225SAndrea Righi 
193*658ad225SAndrea Righi 		scx_bpf_dsq_insert(p, SHARED_DSQ, SCX_SLICE_DFL, enq_flags);
194*658ad225SAndrea Righi 
195*658ad225SAndrea Righi 		__sync_fetch_and_add(&enqueue_cnt, 1);
196*658ad225SAndrea Righi 
197*658ad225SAndrea Righi 		tctx->state = TASK_ENQUEUED;
198*658ad225SAndrea Righi 		tctx->enqueue_seq++;
199*658ad225SAndrea Righi 		break;
200*658ad225SAndrea Righi 	case 6:
201*658ad225SAndrea Righi 		/*
202*658ad225SAndrea Righi 		 * Store task in BPF internal queue.
203*658ad225SAndrea Righi 		 *
204*658ad225SAndrea Righi 		 * Task enters BPF scheduler management: track
205*658ad225SAndrea Righi 		 * enqueue/dequeue lifecycle and validate state
206*658ad225SAndrea Righi 		 * transitions.
207*658ad225SAndrea Righi 		 */
208*658ad225SAndrea Righi 		if (tctx->state == TASK_ENQUEUED)
209*658ad225SAndrea Righi 			scx_bpf_error("%d (%s): enqueue while in ENQUEUED state seq=%llu",
210*658ad225SAndrea Righi 				      p->pid, p->comm, tctx->enqueue_seq);
211*658ad225SAndrea Righi 
212*658ad225SAndrea Righi 		if (bpf_map_push_elem(&global_queue, &pid, 0)) {
213*658ad225SAndrea Righi 			scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
214*658ad225SAndrea Righi 			__sync_fetch_and_add(&bpf_queue_full, 1);
215*658ad225SAndrea Righi 
216*658ad225SAndrea Righi 			tctx->state = TASK_DISPATCHED;
217*658ad225SAndrea Righi 		} else {
218*658ad225SAndrea Righi 			__sync_fetch_and_add(&enqueue_cnt, 1);
219*658ad225SAndrea Righi 
220*658ad225SAndrea Righi 			tctx->state = TASK_ENQUEUED;
221*658ad225SAndrea Righi 			tctx->enqueue_seq++;
222*658ad225SAndrea Righi 		}
223*658ad225SAndrea Righi 		break;
224*658ad225SAndrea Righi 	default:
225*658ad225SAndrea Righi 		/* For all other scenarios, dispatch to the global DSQ */
226*658ad225SAndrea Righi 		scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
227*658ad225SAndrea Righi 		tctx->state = TASK_DISPATCHED;
228*658ad225SAndrea Righi 		break;
229*658ad225SAndrea Righi 	}
230*658ad225SAndrea Righi 
231*658ad225SAndrea Righi 	scx_bpf_kick_cpu(scx_bpf_task_cpu(p), SCX_KICK_IDLE);
232*658ad225SAndrea Righi }
233*658ad225SAndrea Righi 
234*658ad225SAndrea Righi void BPF_STRUCT_OPS(dequeue_dequeue, struct task_struct *p, u64 deq_flags)
235*658ad225SAndrea Righi {
236*658ad225SAndrea Righi 	struct task_ctx *tctx;
237*658ad225SAndrea Righi 
238*658ad225SAndrea Righi 	__sync_fetch_and_add(&dequeue_cnt, 1);
239*658ad225SAndrea Righi 
240*658ad225SAndrea Righi 	tctx = try_lookup_task_ctx(p);
241*658ad225SAndrea Righi 	if (!tctx)
242*658ad225SAndrea Righi 		return;
243*658ad225SAndrea Righi 
244*658ad225SAndrea Righi 	/*
245*658ad225SAndrea Righi 	 * For scenarios 0, 1, 3, and 4 (terminal DSQs: local and global),
246*658ad225SAndrea Righi 	 * ops.dequeue() should never be called because tasks bypass the
247*658ad225SAndrea Righi 	 * BPF scheduler entirely. If we get here, it's a kernel bug.
248*658ad225SAndrea Righi 	 */
249*658ad225SAndrea Righi 	if (test_scenario == 0 || test_scenario == 3) {
250*658ad225SAndrea Righi 		scx_bpf_error("%d (%s): dequeue called for local DSQ scenario",
251*658ad225SAndrea Righi 			      p->pid, p->comm);
252*658ad225SAndrea Righi 		return;
253*658ad225SAndrea Righi 	}
254*658ad225SAndrea Righi 
255*658ad225SAndrea Righi 	if (test_scenario == 1 || test_scenario == 4) {
256*658ad225SAndrea Righi 		scx_bpf_error("%d (%s): dequeue called for global DSQ scenario",
257*658ad225SAndrea Righi 			      p->pid, p->comm);
258*658ad225SAndrea Righi 		return;
259*658ad225SAndrea Righi 	}
260*658ad225SAndrea Righi 
261*658ad225SAndrea Righi 	if (deq_flags & SCX_DEQ_SCHED_CHANGE) {
262*658ad225SAndrea Righi 		/*
263*658ad225SAndrea Righi 		 * Property change interrupting the workflow. Valid from
264*658ad225SAndrea Righi 		 * both ENQUEUED and DISPATCHED states. Transitions task
265*658ad225SAndrea Righi 		 * back to NONE state.
266*658ad225SAndrea Righi 		 */
267*658ad225SAndrea Righi 		__sync_fetch_and_add(&change_dequeue_cnt, 1);
268*658ad225SAndrea Righi 
269*658ad225SAndrea Righi 		/* Validate state transition */
270*658ad225SAndrea Righi 		if (tctx->state != TASK_ENQUEUED && tctx->state != TASK_DISPATCHED)
271*658ad225SAndrea Righi 			scx_bpf_error("%d (%s): invalid property change dequeue state=%d seq=%llu",
272*658ad225SAndrea Righi 				      p->pid, p->comm, tctx->state, tctx->enqueue_seq);
273*658ad225SAndrea Righi 
274*658ad225SAndrea Righi 		/*
275*658ad225SAndrea Righi 		 * Transition back to NONE: task outside scheduler control.
276*658ad225SAndrea Righi 		 *
277*658ad225SAndrea Righi 		 * Scenario 6: dispatch() checks tctx->state after popping a
278*658ad225SAndrea Righi 		 * PID, if the task is in state NONE, it was dequeued by
279*658ad225SAndrea Righi 		 * property change and must not be dispatched (this
280*658ad225SAndrea Righi 		 * prevents "target CPU not allowed").
281*658ad225SAndrea Righi 		 */
282*658ad225SAndrea Righi 		tctx->state = TASK_NONE;
283*658ad225SAndrea Righi 	} else {
284*658ad225SAndrea Righi 		/*
285*658ad225SAndrea Righi 		 * Regular dispatch dequeue: kernel is moving the task from
286*658ad225SAndrea Righi 		 * BPF custody to a terminal DSQ. Normally we come from
287*658ad225SAndrea Righi 		 * ENQUEUED state. We can also see TASK_NONE if the task
288*658ad225SAndrea Righi 		 * was dequeued by property change (SCX_DEQ_SCHED_CHANGE)
289*658ad225SAndrea Righi 		 * while it was already on a DSQ (dispatched but not yet
290*658ad225SAndrea Righi 		 * consumed); in that case we just leave state as NONE.
291*658ad225SAndrea Righi 		 */
292*658ad225SAndrea Righi 		__sync_fetch_and_add(&dispatch_dequeue_cnt, 1);
293*658ad225SAndrea Righi 
294*658ad225SAndrea Righi 		/*
295*658ad225SAndrea Righi 		 * Must be ENQUEUED (normal path) or NONE (already dequeued
296*658ad225SAndrea Righi 		 * by property change while on a DSQ).
297*658ad225SAndrea Righi 		 */
298*658ad225SAndrea Righi 		if (tctx->state != TASK_ENQUEUED && tctx->state != TASK_NONE)
299*658ad225SAndrea Righi 			scx_bpf_error("%d (%s): dispatch dequeue from state %d seq=%llu",
300*658ad225SAndrea Righi 				      p->pid, p->comm, tctx->state, tctx->enqueue_seq);
301*658ad225SAndrea Righi 
302*658ad225SAndrea Righi 		if (tctx->state == TASK_ENQUEUED)
303*658ad225SAndrea Righi 			tctx->state = TASK_DISPATCHED;
304*658ad225SAndrea Righi 
305*658ad225SAndrea Righi 		/* NONE: leave as-is, task was already property-change dequeued */
306*658ad225SAndrea Righi 	}
307*658ad225SAndrea Righi }
308*658ad225SAndrea Righi 
309*658ad225SAndrea Righi void BPF_STRUCT_OPS(dequeue_dispatch, s32 cpu, struct task_struct *prev)
310*658ad225SAndrea Righi {
311*658ad225SAndrea Righi 	if (test_scenario == 6) {
312*658ad225SAndrea Righi 		struct task_ctx *tctx;
313*658ad225SAndrea Righi 		struct task_struct *p;
314*658ad225SAndrea Righi 		s32 pid;
315*658ad225SAndrea Righi 
316*658ad225SAndrea Righi 		if (bpf_map_pop_elem(&global_queue, &pid))
317*658ad225SAndrea Righi 			return;
318*658ad225SAndrea Righi 
319*658ad225SAndrea Righi 		p = bpf_task_from_pid(pid);
320*658ad225SAndrea Righi 		if (!p)
321*658ad225SAndrea Righi 			return;
322*658ad225SAndrea Righi 
323*658ad225SAndrea Righi 		/*
324*658ad225SAndrea Righi 		 * If the task was dequeued by property change
325*658ad225SAndrea Righi 		 * (ops.dequeue() set tctx->state = TASK_NONE), skip
326*658ad225SAndrea Righi 		 * dispatch.
327*658ad225SAndrea Righi 		 */
328*658ad225SAndrea Righi 		tctx = try_lookup_task_ctx(p);
329*658ad225SAndrea Righi 		if (!tctx || tctx->state == TASK_NONE) {
330*658ad225SAndrea Righi 			bpf_task_release(p);
331*658ad225SAndrea Righi 			return;
332*658ad225SAndrea Righi 		}
333*658ad225SAndrea Righi 
334*658ad225SAndrea Righi 		/*
335*658ad225SAndrea Righi 		 * Dispatch to this CPU's local DSQ if allowed, otherwise
336*658ad225SAndrea Righi 		 * fallback to the global DSQ.
337*658ad225SAndrea Righi 		 */
338*658ad225SAndrea Righi 		if (bpf_cpumask_test_cpu(cpu, p->cpus_ptr))
339*658ad225SAndrea Righi 			scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | cpu, SCX_SLICE_DFL, 0);
340*658ad225SAndrea Righi 		else
341*658ad225SAndrea Righi 			scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, 0);
342*658ad225SAndrea Righi 
343*658ad225SAndrea Righi 		bpf_task_release(p);
344*658ad225SAndrea Righi 	} else {
345*658ad225SAndrea Righi 		scx_bpf_dsq_move_to_local(SHARED_DSQ);
346*658ad225SAndrea Righi 	}
347*658ad225SAndrea Righi }
348*658ad225SAndrea Righi 
349*658ad225SAndrea Righi s32 BPF_STRUCT_OPS(dequeue_init_task, struct task_struct *p,
350*658ad225SAndrea Righi 		   struct scx_init_task_args *args)
351*658ad225SAndrea Righi {
352*658ad225SAndrea Righi 	struct task_ctx *tctx;
353*658ad225SAndrea Righi 
354*658ad225SAndrea Righi 	tctx = bpf_task_storage_get(&task_ctx_stor, p, 0,
355*658ad225SAndrea Righi 				   BPF_LOCAL_STORAGE_GET_F_CREATE);
356*658ad225SAndrea Righi 	if (!tctx)
357*658ad225SAndrea Righi 		return -ENOMEM;
358*658ad225SAndrea Righi 
359*658ad225SAndrea Righi 	return 0;
360*658ad225SAndrea Righi }
361*658ad225SAndrea Righi 
362*658ad225SAndrea Righi s32 BPF_STRUCT_OPS_SLEEPABLE(dequeue_init)
363*658ad225SAndrea Righi {
364*658ad225SAndrea Righi 	s32 ret;
365*658ad225SAndrea Righi 
366*658ad225SAndrea Righi 	ret = scx_bpf_create_dsq(SHARED_DSQ, -1);
367*658ad225SAndrea Righi 	if (ret)
368*658ad225SAndrea Righi 		return ret;
369*658ad225SAndrea Righi 
370*658ad225SAndrea Righi 	return 0;
371*658ad225SAndrea Righi }
372*658ad225SAndrea Righi 
373*658ad225SAndrea Righi void BPF_STRUCT_OPS(dequeue_exit, struct scx_exit_info *ei)
374*658ad225SAndrea Righi {
375*658ad225SAndrea Righi 	UEI_RECORD(uei, ei);
376*658ad225SAndrea Righi }
377*658ad225SAndrea Righi 
378*658ad225SAndrea Righi SEC(".struct_ops.link")
379*658ad225SAndrea Righi struct sched_ext_ops dequeue_ops = {
380*658ad225SAndrea Righi 	.select_cpu		= (void *)dequeue_select_cpu,
381*658ad225SAndrea Righi 	.enqueue		= (void *)dequeue_enqueue,
382*658ad225SAndrea Righi 	.dequeue		= (void *)dequeue_dequeue,
383*658ad225SAndrea Righi 	.dispatch		= (void *)dequeue_dispatch,
384*658ad225SAndrea Righi 	.init_task		= (void *)dequeue_init_task,
385*658ad225SAndrea Righi 	.init			= (void *)dequeue_init,
386*658ad225SAndrea Righi 	.exit			= (void *)dequeue_exit,
387*658ad225SAndrea Righi 	.flags			= SCX_OPS_ENQ_LAST,
388*658ad225SAndrea Righi 	.name			= "dequeue_test",
389*658ad225SAndrea Righi };
390