1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Test for DSQ operations including create, destroy, and peek operations. 4 * 5 * Copyright (c) 2025 Meta Platforms, Inc. and affiliates. 6 * Copyright (c) 2025 Ryan Newton <ryan.newton@alum.mit.edu> 7 */ 8 #include <bpf/bpf.h> 9 #include <scx/common.h> 10 #include <sys/wait.h> 11 #include <unistd.h> 12 #include <pthread.h> 13 #include <string.h> 14 #include <sched.h> 15 #include "peek_dsq.bpf.skel.h" 16 #include "scx_test.h" 17 18 #define NUM_WORKERS 4 19 20 static bool workload_running = true; 21 static pthread_t workload_threads[NUM_WORKERS]; 22 23 /** 24 * Background workload thread that sleeps and wakes rapidly to exercise 25 * the scheduler's enqueue operations and ensure DSQ operations get tested. 26 */ 27 static void *workload_thread_fn(void *arg) 28 { 29 while (workload_running) { 30 /* Sleep for a very short time to trigger scheduler activity */ 31 usleep(1000); /* 1ms sleep */ 32 /* Yield to ensure we go through the scheduler */ 33 sched_yield(); 34 } 35 return NULL; 36 } 37 38 static enum scx_test_status setup(void **ctx) 39 { 40 struct peek_dsq *skel; 41 42 skel = peek_dsq__open(); 43 SCX_FAIL_IF(!skel, "Failed to open"); 44 SCX_ENUM_INIT(skel); 45 SCX_FAIL_IF(peek_dsq__load(skel), "Failed to load skel"); 46 47 *ctx = skel; 48 49 return SCX_TEST_PASS; 50 } 51 52 static int print_observed_pids(struct bpf_map *map, int max_samples, const char *dsq_name) 53 { 54 long count = 0; 55 56 printf("Observed %s DSQ peek pids:\n", dsq_name); 57 for (int i = 0; i < max_samples; i++) { 58 long pid; 59 int err; 60 61 err = bpf_map_lookup_elem(bpf_map__fd(map), &i, &pid); 62 if (err == 0) { 63 if (pid == 0) { 64 printf(" Sample %d: NULL peek\n", i); 65 } else if (pid > 0) { 66 printf(" Sample %d: pid %ld\n", i, pid); 67 count++; 68 } 69 } else { 70 printf(" Sample %d: error reading pid (err=%d)\n", i, err); 71 } 72 } 73 printf("Observed ~%ld pids in the %s DSQ(s)\n", count, dsq_name); 74 return count; 75 } 76 77 static enum scx_test_status run(void *ctx) 78 { 79 struct peek_dsq *skel = ctx; 80 bool failed = false; 81 int seconds = 3; 82 int err; 83 84 /* Enable the scheduler to test DSQ operations */ 85 printf("Enabling scheduler to test DSQ insert operations...\n"); 86 87 struct bpf_link *link = 88 bpf_map__attach_struct_ops(skel->maps.peek_dsq_ops); 89 90 if (!link) { 91 SCX_ERR("Failed to attach struct_ops"); 92 return SCX_TEST_FAIL; 93 } 94 95 printf("Starting %d background workload threads...\n", NUM_WORKERS); 96 workload_running = true; 97 for (int i = 0; i < NUM_WORKERS; i++) { 98 err = pthread_create(&workload_threads[i], NULL, workload_thread_fn, NULL); 99 if (err) { 100 SCX_ERR("Failed to create workload thread %d: %s", i, strerror(err)); 101 /* Stop already created threads */ 102 workload_running = false; 103 for (int j = 0; j < i; j++) 104 pthread_join(workload_threads[j], NULL); 105 bpf_link__destroy(link); 106 return SCX_TEST_FAIL; 107 } 108 } 109 110 printf("Waiting for enqueue events.\n"); 111 sleep(seconds); 112 while (skel->data->enqueue_count <= 0) { 113 printf("."); 114 fflush(stdout); 115 sleep(1); 116 seconds++; 117 if (seconds >= 30) { 118 printf("\n\u2717 Timeout waiting for enqueue events\n"); 119 /* Stop workload threads and cleanup */ 120 workload_running = false; 121 for (int i = 0; i < NUM_WORKERS; i++) 122 pthread_join(workload_threads[i], NULL); 123 bpf_link__destroy(link); 124 return SCX_TEST_FAIL; 125 } 126 } 127 128 workload_running = false; 129 for (int i = 0; i < NUM_WORKERS; i++) { 130 err = pthread_join(workload_threads[i], NULL); 131 if (err) { 132 SCX_ERR("Failed to join workload thread %d: %s", i, strerror(err)); 133 bpf_link__destroy(link); 134 return SCX_TEST_FAIL; 135 } 136 } 137 printf("Background workload threads stopped.\n"); 138 139 SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_NONE)); 140 141 /* Detach the scheduler */ 142 bpf_link__destroy(link); 143 144 printf("Enqueue/dispatch count over %d seconds: %d / %d\n", seconds, 145 skel->data->enqueue_count, skel->data->dispatch_count); 146 printf("Debug: ksym_exists=%d\n", 147 skel->bss->debug_ksym_exists); 148 149 /* Check DSQ insert result */ 150 printf("DSQ insert test done on cpu: %d\n", skel->data->insert_test_cpu); 151 if (skel->data->insert_test_cpu != -1) 152 printf("\u2713 DSQ insert succeeded !\n"); 153 else { 154 printf("\u2717 DSQ insert failed or not attempted\n"); 155 failed = true; 156 } 157 158 /* Check DSQ peek results */ 159 printf(" DSQ peek result 1 (before insert): %d\n", 160 skel->data->dsq_peek_result1); 161 if (skel->data->dsq_peek_result1 == 0) 162 printf("\u2713 DSQ peek verification success: peek returned NULL!\n"); 163 else { 164 printf("\u2717 DSQ peek verification failed\n"); 165 failed = true; 166 } 167 168 printf(" DSQ peek result 2 (after insert): %ld\n", 169 skel->data->dsq_peek_result2); 170 printf(" DSQ peek result 2, expected: %ld\n", 171 skel->data->dsq_peek_result2_expected); 172 if (skel->data->dsq_peek_result2 == 173 skel->data->dsq_peek_result2_expected) 174 printf("\u2713 DSQ peek verification success: peek returned the inserted task!\n"); 175 else { 176 printf("\u2717 DSQ peek verification failed\n"); 177 failed = true; 178 } 179 180 printf(" Inserted test task -> pid: %ld\n", skel->data->dsq_inserted_pid); 181 printf(" DSQ peek result 2 -> pid: %ld\n", skel->data->dsq_peek_result2_pid); 182 183 int pid_count; 184 185 pid_count = print_observed_pids(skel->maps.peek_results, 186 skel->data->max_samples, "DSQ pool"); 187 printf("Total non-null peek observations: %ld out of %ld\n", 188 skel->data->successful_peeks, skel->data->total_peek_attempts); 189 190 if (skel->bss->debug_ksym_exists && pid_count == 0) { 191 printf("\u2717 DSQ pool test failed: no successful peeks in native mode\n"); 192 failed = true; 193 } 194 if (skel->bss->debug_ksym_exists && pid_count > 0) 195 printf("\u2713 DSQ pool test success: observed successful peeks in native mode\n"); 196 197 if (failed) 198 return SCX_TEST_FAIL; 199 else 200 return SCX_TEST_PASS; 201 } 202 203 static void cleanup(void *ctx) 204 { 205 struct peek_dsq *skel = ctx; 206 207 if (workload_running) { 208 workload_running = false; 209 for (int i = 0; i < NUM_WORKERS; i++) 210 pthread_join(workload_threads[i], NULL); 211 } 212 213 peek_dsq__destroy(skel); 214 } 215 216 struct scx_test peek_dsq = { 217 .name = "peek_dsq", 218 .description = 219 "Test DSQ create/destroy operations and future peek functionality", 220 .setup = setup, 221 .run = run, 222 .cleanup = cleanup, 223 }; 224 REGISTER_SCX_TEST(&peek_dsq) 225