Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206// SPDX-License-Identifier: GPL-2.0/* * Verify that ops.dequeue() is called for tasks leaving BPF custody through * an SCX-internal cross-CPU migration (move_remote_task_to_local_dsq()). * * Copyright (c) 2026 Google LLC. */#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <signal.h>#include <time.h>#include <sched.h>#include <bpf/bpf.h>#include <scx/common.h>#include <sys/wait.h>#include "scx_test.h"#include "dequeue_remote.bpf.skel.h"
#define MAX_WORKERS 64#define RUN_MS 2000
static int nr_cpus;
static long long now_ms(void){ struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000LL + ts.tv_nsec / 1000000;}
/* mix of short bursts and sleeps to generate lots of enqueues and wakeups */static void worker_fn(int id){ long long end = now_ms() + RUN_MS; volatile unsigned long sum = 0;
while (now_ms() < end) { unsigned long j;
for (j = 0; j < 20000 + id * 1000; j++) sum += j; asm volatile("" : : "r"(sum)); if (id & 1) usleep(100); else sched_yield(); }
exit(0);}
static enum scx_test_status run_scenario(struct dequeue_remote *skel, bool use_move_to_local, const char *name){ enum scx_test_status ret = SCX_TEST_PASS; struct bpf_link *link; pid_t pids[MAX_WORKERS]; int nr_workers, nr_forked, i;
nr_workers = 2 * nr_cpus; if (nr_workers < 4) nr_workers = 4; if (nr_workers > MAX_WORKERS) nr_workers = MAX_WORKERS;
skel->bss->test_use_move_to_local = use_move_to_local; skel->bss->enqueue_cnt = 0; skel->bss->dequeue_cnt = 0; skel->bss->dispatch_dequeue_cnt = 0; skel->bss->change_dequeue_cnt = 0; skel->bss->remote_dispatch_cnt = 0; skel->bss->remote_running_cnt = 0; skel->bss->missed_dequeue_cnt = 0; skel->bss->core_sched_exec_dequeue_cnt = 0; memset(&skel->data->uei, 0, sizeof(skel->data->uei));
link = bpf_map__attach_struct_ops(skel->maps.dequeue_remote_ops); SCX_FAIL_IF(!link, "Failed to attach struct_ops for %s", name);
fflush(stdout); fflush(stderr);
for (nr_forked = 0; nr_forked < nr_workers; nr_forked++) { pid_t pid = fork();
if (pid < 0) { SCX_ERR("Failed to fork worker %d", nr_forked); ret = SCX_TEST_FAIL; break; } if (pid == 0) worker_fn(nr_forked); pids[nr_forked] = pid; }
/* on failure, kill the remaining workers but still reap them */ for (i = 0; i < nr_forked; i++) { int status;
if (ret != SCX_TEST_PASS) kill(pids[i], SIGKILL);
if (waitpid(pids[i], &status, 0) != pids[i]) { SCX_ERR("Failed to wait for worker %d", i); ret = SCX_TEST_FAIL; } else if (ret == SCX_TEST_PASS && status != 0) { SCX_ERR("Worker %d exited with status %d", i, status); ret = SCX_TEST_FAIL; } }
bpf_link__destroy(link);
if (ret != SCX_TEST_PASS) return ret;
printf("%s:\n", name); printf(" workers: %d\n", nr_workers); printf(" enqueues: %lu\n", (unsigned long)skel->bss->enqueue_cnt); printf(" dequeues: %lu (dispatch: %lu, property_change: %lu)\n", (unsigned long)skel->bss->dequeue_cnt, (unsigned long)skel->bss->dispatch_dequeue_cnt, (unsigned long)skel->bss->change_dequeue_cnt); if (!use_move_to_local) printf(" remote SCX_DSQ_LOCAL_ON dispatches: %lu\n", (unsigned long)skel->bss->remote_dispatch_cnt); printf(" ran on a CPU other than the enqueue CPU: %lu\n", (unsigned long)skel->bss->remote_running_cnt); printf(" ran without ops.dequeue(): %lu\n", (unsigned long)skel->bss->missed_dequeue_cnt); printf(" late SCX_DEQ_CORE_SCHED_EXEC dequeues: %lu\n", (unsigned long)skel->bss->core_sched_exec_dequeue_cnt);
if (skel->data->uei.kind != EXIT_KIND(SCX_EXIT_UNREG)) SCX_ERR("Scheduler exited with kind=%lld: %s", (long long)skel->data->uei.kind, skel->data->uei.msg); SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_UNREG));
/* the test is meaningless if no task was moved across CPUs */ SCX_GT(skel->bss->remote_running_cnt, 0); SCX_EQ(skel->bss->enqueue_cnt, skel->bss->dequeue_cnt);
return SCX_TEST_PASS;}
static enum scx_test_status setup(void **ctx){ struct dequeue_remote *skel; cpu_set_t cpus;
SCX_FAIL_IF(sched_getaffinity(0, sizeof(cpus), &cpus), "Failed to get CPU affinity"); nr_cpus = CPU_COUNT(&cpus); if (nr_cpus < 2) { fprintf(stderr, "Skipping: requires at least 2 usable CPUs\n"); return SCX_TEST_SKIP; }
skel = SCX_OPS_OPEN(dequeue_remote_ops, dequeue_remote); SCX_OPS_LOAD(skel, dequeue_remote_ops, dequeue_remote, uei);
*ctx = skel;
return SCX_TEST_PASS;}
static enum scx_test_status run(void *ctx){ struct dequeue_remote *skel = ctx; enum scx_test_status status;
status = run_scenario(skel, false, "BPF queue -> SCX_DSQ_LOCAL_ON | cpu"); if (status != SCX_TEST_PASS) return status;
status = run_scenario(skel, true, "user DSQ -> scx_bpf_dsq_move_to_local()"); if (status != SCX_TEST_PASS) return status;
return SCX_TEST_PASS;}
static void cleanup(void *ctx){ struct dequeue_remote *skel = ctx;
dequeue_remote__destroy(skel);}
struct scx_test dequeue_remote_test = { .name = "dequeue_remote", .description = "Verify ops.dequeue() on SCX-internal cross-CPU migrations", .setup = setup, .run = run, .cleanup = cleanup,};
REGISTER_SCX_TEST(&dequeue_remote_test)