|
| 1 | +// Simple sched-ext scheduler implementation |
| 2 | +// This demonstrates a basic FIFO scheduler using sched_ext_ops |
| 3 | + |
| 4 | +// kfuncs declarations (extracted from BTF) |
| 5 | +extern scx_bpf_select_cpu_dfl(p: *u8, prev_cpu: i32, wake_flags: u64, direct: *bool) -> i32 |
| 6 | +extern scx_bpf_dsq_insert(p: *u8, dsq_id: u64, slice: u64, enq_flags: u64) -> void |
| 7 | +extern scx_bpf_consume(dsq_id: u64, cpu: i32, flags: u64) -> i32 |
| 8 | + |
| 9 | +// Kernel enums (extracted from BTF) |
| 10 | +enum scx_public_consts { |
| 11 | + SCX_OPS_NAME_LEN = 128, |
| 12 | + SCX_SLICE_DFL = 20000000, |
| 13 | + SCX_SLICE_INF = 18446744073709551615, |
| 14 | +} |
| 15 | + |
| 16 | +enum scx_dsq_id_flags { |
| 17 | + SCX_DSQ_FLAG_BUILTIN = 9223372036854775808, |
| 18 | + SCX_DSQ_FLAG_LOCAL_ON = 4611686018427387904, |
| 19 | + SCX_DSQ_INVALID = 9223372036854775808, |
| 20 | + SCX_DSQ_GLOBAL = 9223372036854775809, |
| 21 | + SCX_DSQ_LOCAL = 9223372036854775810, |
| 22 | + SCX_DSQ_LOCAL_ON = 13835058055282163712, |
| 23 | + SCX_DSQ_LOCAL_CPU_MASK = 4294967295, |
| 24 | +} |
| 25 | + |
| 26 | +// Define the sched_ext_ops structure (extracted from BTF) |
| 27 | +struct sched_ext_ops { |
| 28 | + select_cpu: fn(p: *u8, prev_cpu: i32, wake_flags: u64) -> i32, |
| 29 | + enqueue: fn(p: *u8, enq_flags: u64) -> void, |
| 30 | + dispatch: fn(cpu: i32, prev: *u8) -> void, |
| 31 | + runnable: fn(p: *u8, enq_flags: u64) -> void, |
| 32 | + running: fn(p: *u8) -> void, |
| 33 | + stopping: fn(p: *u8, runnable: bool) -> void, |
| 34 | + quiescent: fn(p: *u8, deq_flags: u64) -> void, |
| 35 | + init_task: fn(p: *u8, args: *u8) -> i32, |
| 36 | + exit_task: fn(p: *u8, args: *u8) -> void, |
| 37 | + enable: fn(p: *u8) -> void, |
| 38 | + init: fn() -> i32, |
| 39 | + exit: fn(info: *u8) -> void, |
| 40 | + name: *u8, |
| 41 | + timeout_ms: u64, |
| 42 | + flags: u64, |
| 43 | +} |
| 44 | + |
| 45 | +// Simple FIFO scheduler implementation |
| 46 | +@struct_ops("sched_ext_ops") |
| 47 | +impl simple_fifo_scheduler { |
| 48 | + |
| 49 | + // Select CPU for a waking task |
| 50 | + fn select_cpu(p: *u8, prev_cpu: i32, wake_flags: u64) -> i32 { |
| 51 | + // Use default CPU selection with direct dispatch if idle core found |
| 52 | + var direct: bool = false |
| 53 | + var cpu = scx_bpf_select_cpu_dfl(p, prev_cpu, wake_flags, &direct) |
| 54 | + |
| 55 | + if (direct) { |
| 56 | + // Insert directly into local DSQ, skipping enqueue |
| 57 | + scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, SCX_SLICE_DFL, 0) |
| 58 | + } |
| 59 | + |
| 60 | + return cpu |
| 61 | + } |
| 62 | + |
| 63 | + // Enqueue task into global FIFO queue |
| 64 | + fn enqueue(p: *u8, enq_flags: u64) -> void { |
| 65 | + // Simple FIFO: insert all tasks into global DSQ |
| 66 | + scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags) |
| 67 | + } |
| 68 | + |
| 69 | + // Dispatch tasks from global queue to local CPU |
| 70 | + fn dispatch(cpu: i32, prev: *u8) -> void { |
| 71 | + // Try to consume a task from the global DSQ |
| 72 | + if (scx_bpf_consume(SCX_DSQ_GLOBAL, cpu, 0) == 0) { |
| 73 | + // No tasks available, CPU will go idle |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + // Task becomes runnable |
| 78 | + fn runnable(p: *u8, enq_flags: u64) -> void { |
| 79 | + // Optional: track runnable tasks |
| 80 | + // For simple FIFO, we don't need special handling |
| 81 | + } |
| 82 | + |
| 83 | + // Task starts running |
| 84 | + fn running(p: *u8) -> void { |
| 85 | + // Optional: track running tasks |
| 86 | + // For simple FIFO, we don't need special handling |
| 87 | + } |
| 88 | + |
| 89 | + // Task stops running |
| 90 | + fn stopping(p: *u8, runnable: bool) -> void { |
| 91 | + // Optional: handle task stopping |
| 92 | + // For simple FIFO, we don't need special handling |
| 93 | + } |
| 94 | + |
| 95 | + // Task becomes quiescent |
| 96 | + fn quiescent(p: *u8, deq_flags: u64) -> void { |
| 97 | + // Optional: handle quiescent tasks |
| 98 | + // For simple FIFO, we don't need special handling |
| 99 | + } |
| 100 | + |
| 101 | + // Initialize new task |
| 102 | + fn init_task(p: *u8, args: *u8) -> i32 { |
| 103 | + // Return 0 for success |
| 104 | + return 0 |
| 105 | + } |
| 106 | + |
| 107 | + // Clean up exiting task |
| 108 | + fn exit_task(p: *u8, args: *u8) -> void { |
| 109 | + // Optional cleanup for exiting tasks |
| 110 | + } |
| 111 | + |
| 112 | + // Enable scheduler |
| 113 | + fn enable(p: *u8) -> void { |
| 114 | + // Optional: scheduler enable logic |
| 115 | + } |
| 116 | + |
| 117 | + // Initialize scheduler |
| 118 | + fn init() -> i32 { |
| 119 | + // Return 0 for successful initialization |
| 120 | + return 0 |
| 121 | + } |
| 122 | + |
| 123 | + // Exit scheduler |
| 124 | + fn exit(info: *u8) -> void { |
| 125 | + // Optional cleanup on scheduler exit |
| 126 | + } |
| 127 | + |
| 128 | + // Scheduler name |
| 129 | + name: "simple_fifo", |
| 130 | + |
| 131 | + // Timeout in milliseconds (0 = no timeout) |
| 132 | + timeout_ms: 0, |
| 133 | + |
| 134 | + // Scheduler flags |
| 135 | + flags: 0, |
| 136 | +} |
| 137 | + |
| 138 | +// Userspace main function |
| 139 | +fn main() -> i32 { |
| 140 | + // Register the sched-ext scheduler |
| 141 | + var result = register(simple_fifo_scheduler) |
| 142 | + |
| 143 | + if (result == 0) { |
| 144 | + print("Simple FIFO scheduler registered successfully") |
| 145 | + } else { |
| 146 | + print("Failed to register Simple FIFO scheduler") |
| 147 | + } |
| 148 | + |
| 149 | + return result |
| 150 | +} |
0 commit comments