peek_dsq.c 6.0 KB

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