// SPDX-License-Identifier: GPL-2.0 /* * Test for DSQ operations including create, destroy, and peek operations. * * Copyright (c) 2025 Meta Platforms, Inc. and affiliates. * Copyright (c) 2025 Ryan Newton */ #include #include #include #include #include #include #include #include "peek_dsq.bpf.skel.h" #include "scx_test.h" #define NUM_WORKERS 4 static bool workload_running = true; static pthread_t workload_threads[NUM_WORKERS]; /** * Background workload thread that sleeps and wakes rapidly to exercise * the scheduler's enqueue operations and ensure DSQ operations get tested. */ static void *workload_thread_fn(void *arg) { while (workload_running) { /* Sleep for a very short time to trigger scheduler activity */ usleep(1000); /* 1ms sleep */ /* Yield to ensure we go through the scheduler */ sched_yield(); } return NULL; } static enum scx_test_status setup(void **ctx) { struct peek_dsq *skel; skel = peek_dsq__open(); SCX_FAIL_IF(!skel, "Failed to open"); SCX_ENUM_INIT(skel); SCX_FAIL_IF(peek_dsq__load(skel), "Failed to load skel"); *ctx = skel; return SCX_TEST_PASS; } static int print_observed_pids(struct bpf_map *map, int max_samples, const char *dsq_name) { long count = 0; printf("Observed %s DSQ peek pids:\n", dsq_name); for (int i = 0; i < max_samples; i++) { long pid; int err; err = bpf_map_lookup_elem(bpf_map__fd(map), &i, &pid); if (err == 0) { if (pid == 0) { printf(" Sample %d: NULL peek\n", i); } else if (pid > 0) { printf(" Sample %d: pid %ld\n", i, pid); count++; } } else { printf(" Sample %d: error reading pid (err=%d)\n", i, err); } } printf("Observed ~%ld pids in the %s DSQ(s)\n", count, dsq_name); return count; } static enum scx_test_status run(void *ctx) { struct peek_dsq *skel = ctx; bool failed = false; int seconds = 3; int err; /* Enable the scheduler to test DSQ operations */ printf("Enabling scheduler to test DSQ insert operations...\n"); struct bpf_link *link = bpf_map__attach_struct_ops(skel->maps.peek_dsq_ops); if (!link) { SCX_ERR("Failed to attach struct_ops"); return SCX_TEST_FAIL; } printf("Starting %d background workload threads...\n", NUM_WORKERS); workload_running = true; for (int i = 0; i < NUM_WORKERS; i++) { err = pthread_create(&workload_threads[i], NULL, workload_thread_fn, NULL); if (err) { SCX_ERR("Failed to create workload thread %d: %s", i, strerror(err)); /* Stop already created threads */ workload_running = false; for (int j = 0; j < i; j++) pthread_join(workload_threads[j], NULL); bpf_link__destroy(link); return SCX_TEST_FAIL; } } printf("Waiting for enqueue events.\n"); sleep(seconds); while (skel->data->enqueue_count <= 0) { printf("."); fflush(stdout); sleep(1); seconds++; if (seconds >= 30) { printf("\n\u2717 Timeout waiting for enqueue events\n"); /* Stop workload threads and cleanup */ workload_running = false; for (int i = 0; i < NUM_WORKERS; i++) pthread_join(workload_threads[i], NULL); bpf_link__destroy(link); return SCX_TEST_FAIL; } } workload_running = false; for (int i = 0; i < NUM_WORKERS; i++) { err = pthread_join(workload_threads[i], NULL); if (err) { SCX_ERR("Failed to join workload thread %d: %s", i, strerror(err)); bpf_link__destroy(link); return SCX_TEST_FAIL; } } printf("Background workload threads stopped.\n"); SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_NONE)); /* Detach the scheduler */ bpf_link__destroy(link); printf("Enqueue/dispatch count over %d seconds: %d / %d\n", seconds, skel->data->enqueue_count, skel->data->dispatch_count); printf("Debug: ksym_exists=%d\n", skel->bss->debug_ksym_exists); /* Check DSQ insert result */ printf("DSQ insert test done on cpu: %d\n", skel->data->insert_test_cpu); if (skel->data->insert_test_cpu != -1) printf("\u2713 DSQ insert succeeded !\n"); else { printf("\u2717 DSQ insert failed or not attempted\n"); failed = true; } /* Check DSQ peek results */ printf(" DSQ peek result 1 (before insert): %d\n", skel->data->dsq_peek_result1); if (skel->data->dsq_peek_result1 == 0) printf("\u2713 DSQ peek verification success: peek returned NULL!\n"); else { printf("\u2717 DSQ peek verification failed\n"); failed = true; } printf(" DSQ peek result 2 (after insert): %ld\n", skel->data->dsq_peek_result2); printf(" DSQ peek result 2, expected: %ld\n", skel->data->dsq_peek_result2_expected); if (skel->data->dsq_peek_result2 == skel->data->dsq_peek_result2_expected) printf("\u2713 DSQ peek verification success: peek returned the inserted task!\n"); else { printf("\u2717 DSQ peek verification failed\n"); failed = true; } printf(" Inserted test task -> pid: %ld\n", skel->data->dsq_inserted_pid); printf(" DSQ peek result 2 -> pid: %ld\n", skel->data->dsq_peek_result2_pid); int pid_count; pid_count = print_observed_pids(skel->maps.peek_results, skel->data->max_samples, "DSQ pool"); printf("Total non-null peek observations: %ld out of %ld\n", skel->data->successful_peeks, skel->data->total_peek_attempts); if (skel->bss->debug_ksym_exists && pid_count == 0) { printf("\u2717 DSQ pool test failed: no successful peeks in native mode\n"); failed = true; } if (skel->bss->debug_ksym_exists && pid_count > 0) printf("\u2713 DSQ pool test success: observed successful peeks in native mode\n"); if (failed) return SCX_TEST_FAIL; else return SCX_TEST_PASS; } static void cleanup(void *ctx) { struct peek_dsq *skel = ctx; if (workload_running) { workload_running = false; for (int i = 0; i < NUM_WORKERS; i++) pthread_join(workload_threads[i], NULL); } peek_dsq__destroy(skel); } struct scx_test peek_dsq = { .name = "peek_dsq", .description = "Test DSQ create/destroy operations and future peek functionality", .setup = setup, .run = run, .cleanup = cleanup, }; REGISTER_SCX_TEST(&peek_dsq)