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