| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- // 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;
- u32 ix;
- if (pid <= 0)
- return;
- /* Find an empty slot or one with the same PID */
- bpf_for(ix, 0, 10) {
- slot_key = ((u64)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",
- };
|