1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
// SPDX-License-Identifier: GPL-2.0
/*
* A BPF program for testing DSQ operations and peek in particular.
*
* Copyright (c) 2025 Meta Platforms, Inc. and affiliates.
* Copyright (c) 2025 Ryan Newton <ryan.newton@alum.mit.edu>
*/
#include <scx/common.bpf.h>
#include <scx/compat.bpf.h>
char _license[] SEC("license") = "GPL";
UEI_DEFINE(uei); /* Error handling */
#define MAX_SAMPLES 100
#define MAX_CPUS 512
#define DSQ_POOL_SIZE 8
int max_samples = MAX_SAMPLES;
int max_cpus = MAX_CPUS;
int dsq_pool_size = DSQ_POOL_SIZE;
/* Global variables to store test results */
int dsq_peek_result1 = -1;
long dsq_inserted_pid = -1;
int insert_test_cpu = -1; /* Set to the cpu that performs the test */
long dsq_peek_result2 = -1;
long dsq_peek_result2_pid = -1;
long dsq_peek_result2_expected = -1;
int test_dsq_id = 1234; /* Use a simple ID like create_dsq example */
int real_dsq_id = 1235; /* DSQ for normal operation */
int enqueue_count = -1;
int dispatch_count = -1;
bool debug_ksym_exists;
/* DSQ pool for stress testing */
int dsq_pool_base_id = 2000;
int phase1_complete = -1;
long total_peek_attempts = -1;
long successful_peeks = -1;
/* BPF map for sharing peek results with userspace */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, MAX_SAMPLES);
__type(key, u32);
__type(value, long);
} peek_results SEC(".maps");
static int get_random_dsq_id(void)
{
u64 time = bpf_ktime_get_ns();
return dsq_pool_base_id + (time % DSQ_POOL_SIZE);
}
static void record_peek_result(long pid)
{
u32 slot_key;
long *slot_pid_ptr;
int ix;
if (pid <= 0)
return;
/* Find an empty slot or one with the same PID */
bpf_for(ix, 0, 10) {
slot_key = (pid + ix) % MAX_SAMPLES;
slot_pid_ptr = bpf_map_lookup_elem(&peek_results, &slot_key);
if (!slot_pid_ptr)
continue;
if (*slot_pid_ptr == -1 || *slot_pid_ptr == pid) {
*slot_pid_ptr = pid;
break;
}
}
}
/* Scan all DSQs in the pool and try to move a task to local */
static int scan_dsq_pool(void)
{
struct task_struct *task;
int moved = 0;
int i;
bpf_for(i, 0, DSQ_POOL_SIZE) {
int dsq_id = dsq_pool_base_id + i;
total_peek_attempts++;
task = __COMPAT_scx_bpf_dsq_peek(dsq_id);
if (task) {
successful_peeks++;
record_peek_result(task->pid);
/* Try to move this task to local */
if (!moved && scx_bpf_dsq_move_to_local(dsq_id) == 0) {
moved = 1;
break;
}
}
}
return moved;
}
/* Struct_ops scheduler for testing DSQ peek operations */
void BPF_STRUCT_OPS(peek_dsq_enqueue, struct task_struct *p, u64 enq_flags)
{
struct task_struct *peek_result;
int last_insert_test_cpu, cpu;
enqueue_count++;
cpu = bpf_get_smp_processor_id();
last_insert_test_cpu = __sync_val_compare_and_swap(&insert_test_cpu, -1, cpu);
/* Phase 1: Simple insert-then-peek test (only on first task) */
if (last_insert_test_cpu == -1) {
bpf_printk("peek_dsq_enqueue beginning phase 1 peek test on cpu %d", cpu);
/* Test 1: Peek empty DSQ - should return NULL */
peek_result = __COMPAT_scx_bpf_dsq_peek(test_dsq_id);
dsq_peek_result1 = (long)peek_result; /* Should be 0 (NULL) */
/* Test 2: Insert task into test DSQ for testing in dispatch callback */
dsq_inserted_pid = p->pid;
scx_bpf_dsq_insert(p, test_dsq_id, 0, enq_flags);
dsq_peek_result2_expected = (long)p; /* Expected the task we just inserted */
} else if (!phase1_complete) {
/* Still in phase 1, use real DSQ */
scx_bpf_dsq_insert(p, real_dsq_id, 0, enq_flags);
} else {
/* Phase 2: Random DSQ insertion for stress testing */
int random_dsq_id = get_random_dsq_id();
scx_bpf_dsq_insert(p, random_dsq_id, 0, enq_flags);
}
}
void BPF_STRUCT_OPS(peek_dsq_dispatch, s32 cpu, struct task_struct *prev)
{
dispatch_count++;
/* Phase 1: Complete the simple peek test if we inserted a task but
* haven't tested peek yet
*/
if (insert_test_cpu == cpu && dsq_peek_result2 == -1) {
struct task_struct *peek_result;
bpf_printk("peek_dsq_dispatch completing phase 1 peek test on cpu %d", cpu);
/* Test 3: Peek DSQ after insert - should return the task we inserted */
peek_result = __COMPAT_scx_bpf_dsq_peek(test_dsq_id);
/* Store the PID of the peeked task for comparison */
dsq_peek_result2 = (long)peek_result;
dsq_peek_result2_pid = peek_result ? peek_result->pid : -1;
/* Now consume the task since we've peeked at it */
scx_bpf_dsq_move_to_local(test_dsq_id);
/* Mark phase 1 as complete */
phase1_complete = 1;
bpf_printk("Phase 1 complete, starting phase 2 stress testing");
} else if (!phase1_complete) {
/* Still in phase 1, use real DSQ */
scx_bpf_dsq_move_to_local(real_dsq_id);
} else {
/* Phase 2: Scan all DSQs in the pool and try to move a task */
if (!scan_dsq_pool()) {
/* No tasks found in DSQ pool, fall back to real DSQ */
scx_bpf_dsq_move_to_local(real_dsq_id);
}
}
}
s32 BPF_STRUCT_OPS_SLEEPABLE(peek_dsq_init)
{
s32 err;
int i;
/* Always set debug values so we can see which version we're using */
debug_ksym_exists = bpf_ksym_exists(scx_bpf_dsq_peek) ? 1 : 0;
/* Initialize state first */
insert_test_cpu = -1;
enqueue_count = 0;
dispatch_count = 0;
phase1_complete = 0;
total_peek_attempts = 0;
successful_peeks = 0;
/* Create the test and real DSQs */
err = scx_bpf_create_dsq(test_dsq_id, -1);
if (err) {
scx_bpf_error("Failed to create DSQ %d: %d", test_dsq_id, err);
return err;
}
err = scx_bpf_create_dsq(real_dsq_id, -1);
if (err) {
scx_bpf_error("Failed to create DSQ %d: %d", test_dsq_id, err);
return err;
}
/* Create the DSQ pool for stress testing */
bpf_for(i, 0, DSQ_POOL_SIZE) {
int dsq_id = dsq_pool_base_id + i;
err = scx_bpf_create_dsq(dsq_id, -1);
if (err) {
scx_bpf_error("Failed to create DSQ pool entry %d: %d", dsq_id, err);
return err;
}
}
/* Initialize the peek results map */
bpf_for(i, 0, MAX_SAMPLES) {
u32 key = i;
long pid = -1;
bpf_map_update_elem(&peek_results, &key, &pid, BPF_ANY);
}
return 0;
}
void BPF_STRUCT_OPS(peek_dsq_exit, struct scx_exit_info *ei)
{
int i;
/* Destroy the primary DSQs */
scx_bpf_destroy_dsq(test_dsq_id);
scx_bpf_destroy_dsq(real_dsq_id);
/* Destroy the DSQ pool */
bpf_for(i, 0, DSQ_POOL_SIZE) {
int dsq_id = dsq_pool_base_id + i;
scx_bpf_destroy_dsq(dsq_id);
}
UEI_RECORD(uei, ei);
}
SEC(".struct_ops.link")
struct sched_ext_ops peek_dsq_ops = {
.enqueue = (void *)peek_dsq_enqueue,
.dispatch = (void *)peek_dsq_dispatch,
.init = (void *)peek_dsq_init,
.exit = (void *)peek_dsq_exit,
.name = "peek_dsq",
};
|