mirror of https://github.com/torvalds/linux.git
225 lines
6.0 KiB
C
225 lines
6.0 KiB
C
// 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 <ryan.newton@alum.mit.edu>
|
|
*/
|
|
#include <bpf/bpf.h>
|
|
#include <scx/common.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
#include <sched.h>
|
|
#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)
|