peek_dsq.bpf.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * A BPF program for testing DSQ operations and peek in particular.
  4. *
  5. * Copyright (c) 2025 Meta Platforms, Inc. and affiliates.
  6. * Copyright (c) 2025 Ryan Newton <ryan.newton@alum.mit.edu>
  7. */
  8. #include <scx/common.bpf.h>
  9. #include <scx/compat.bpf.h>
  10. char _license[] SEC("license") = "GPL";
  11. UEI_DEFINE(uei); /* Error handling */
  12. #define MAX_SAMPLES 100
  13. #define MAX_CPUS 512
  14. #define DSQ_POOL_SIZE 8
  15. int max_samples = MAX_SAMPLES;
  16. int max_cpus = MAX_CPUS;
  17. int dsq_pool_size = DSQ_POOL_SIZE;
  18. /* Global variables to store test results */
  19. int dsq_peek_result1 = -1;
  20. long dsq_inserted_pid = -1;
  21. int insert_test_cpu = -1; /* Set to the cpu that performs the test */
  22. long dsq_peek_result2 = -1;
  23. long dsq_peek_result2_pid = -1;
  24. long dsq_peek_result2_expected = -1;
  25. int test_dsq_id = 1234; /* Use a simple ID like create_dsq example */
  26. int real_dsq_id = 1235; /* DSQ for normal operation */
  27. int enqueue_count = -1;
  28. int dispatch_count = -1;
  29. bool debug_ksym_exists;
  30. /* DSQ pool for stress testing */
  31. int dsq_pool_base_id = 2000;
  32. int phase1_complete = -1;
  33. long total_peek_attempts = -1;
  34. long successful_peeks = -1;
  35. /* BPF map for sharing peek results with userspace */
  36. struct {
  37. __uint(type, BPF_MAP_TYPE_ARRAY);
  38. __uint(max_entries, MAX_SAMPLES);
  39. __type(key, u32);
  40. __type(value, long);
  41. } peek_results SEC(".maps");
  42. static int get_random_dsq_id(void)
  43. {
  44. u64 time = bpf_ktime_get_ns();
  45. return dsq_pool_base_id + (time % DSQ_POOL_SIZE);
  46. }
  47. static void record_peek_result(long pid)
  48. {
  49. u32 slot_key;
  50. long *slot_pid_ptr;
  51. u32 ix;
  52. if (pid <= 0)
  53. return;
  54. /* Find an empty slot or one with the same PID */
  55. bpf_for(ix, 0, 10) {
  56. slot_key = ((u64)pid + ix) % MAX_SAMPLES;
  57. slot_pid_ptr = bpf_map_lookup_elem(&peek_results, &slot_key);
  58. if (!slot_pid_ptr)
  59. continue;
  60. if (*slot_pid_ptr == -1 || *slot_pid_ptr == pid) {
  61. *slot_pid_ptr = pid;
  62. break;
  63. }
  64. }
  65. }
  66. /* Scan all DSQs in the pool and try to move a task to local */
  67. static int scan_dsq_pool(void)
  68. {
  69. struct task_struct *task;
  70. int moved = 0;
  71. int i;
  72. bpf_for(i, 0, DSQ_POOL_SIZE) {
  73. int dsq_id = dsq_pool_base_id + i;
  74. total_peek_attempts++;
  75. task = __COMPAT_scx_bpf_dsq_peek(dsq_id);
  76. if (task) {
  77. successful_peeks++;
  78. record_peek_result(task->pid);
  79. /* Try to move this task to local */
  80. if (!moved && scx_bpf_dsq_move_to_local(dsq_id) == 0) {
  81. moved = 1;
  82. break;
  83. }
  84. }
  85. }
  86. return moved;
  87. }
  88. /* Struct_ops scheduler for testing DSQ peek operations */
  89. void BPF_STRUCT_OPS(peek_dsq_enqueue, struct task_struct *p, u64 enq_flags)
  90. {
  91. struct task_struct *peek_result;
  92. int last_insert_test_cpu, cpu;
  93. enqueue_count++;
  94. cpu = bpf_get_smp_processor_id();
  95. last_insert_test_cpu = __sync_val_compare_and_swap(&insert_test_cpu, -1, cpu);
  96. /* Phase 1: Simple insert-then-peek test (only on first task) */
  97. if (last_insert_test_cpu == -1) {
  98. bpf_printk("peek_dsq_enqueue beginning phase 1 peek test on cpu %d", cpu);
  99. /* Test 1: Peek empty DSQ - should return NULL */
  100. peek_result = __COMPAT_scx_bpf_dsq_peek(test_dsq_id);
  101. dsq_peek_result1 = (long)peek_result; /* Should be 0 (NULL) */
  102. /* Test 2: Insert task into test DSQ for testing in dispatch callback */
  103. dsq_inserted_pid = p->pid;
  104. scx_bpf_dsq_insert(p, test_dsq_id, 0, enq_flags);
  105. dsq_peek_result2_expected = (long)p; /* Expected the task we just inserted */
  106. } else if (!phase1_complete) {
  107. /* Still in phase 1, use real DSQ */
  108. scx_bpf_dsq_insert(p, real_dsq_id, 0, enq_flags);
  109. } else {
  110. /* Phase 2: Random DSQ insertion for stress testing */
  111. int random_dsq_id = get_random_dsq_id();
  112. scx_bpf_dsq_insert(p, random_dsq_id, 0, enq_flags);
  113. }
  114. }
  115. void BPF_STRUCT_OPS(peek_dsq_dispatch, s32 cpu, struct task_struct *prev)
  116. {
  117. dispatch_count++;
  118. /* Phase 1: Complete the simple peek test if we inserted a task but
  119. * haven't tested peek yet
  120. */
  121. if (insert_test_cpu == cpu && dsq_peek_result2 == -1) {
  122. struct task_struct *peek_result;
  123. bpf_printk("peek_dsq_dispatch completing phase 1 peek test on cpu %d", cpu);
  124. /* Test 3: Peek DSQ after insert - should return the task we inserted */
  125. peek_result = __COMPAT_scx_bpf_dsq_peek(test_dsq_id);
  126. /* Store the PID of the peeked task for comparison */
  127. dsq_peek_result2 = (long)peek_result;
  128. dsq_peek_result2_pid = peek_result ? peek_result->pid : -1;
  129. /* Now consume the task since we've peeked at it */
  130. scx_bpf_dsq_move_to_local(test_dsq_id);
  131. /* Mark phase 1 as complete */
  132. phase1_complete = 1;
  133. bpf_printk("Phase 1 complete, starting phase 2 stress testing");
  134. } else if (!phase1_complete) {
  135. /* Still in phase 1, use real DSQ */
  136. scx_bpf_dsq_move_to_local(real_dsq_id);
  137. } else {
  138. /* Phase 2: Scan all DSQs in the pool and try to move a task */
  139. if (!scan_dsq_pool()) {
  140. /* No tasks found in DSQ pool, fall back to real DSQ */
  141. scx_bpf_dsq_move_to_local(real_dsq_id);
  142. }
  143. }
  144. }
  145. s32 BPF_STRUCT_OPS_SLEEPABLE(peek_dsq_init)
  146. {
  147. s32 err;
  148. int i;
  149. /* Always set debug values so we can see which version we're using */
  150. debug_ksym_exists = bpf_ksym_exists(scx_bpf_dsq_peek) ? 1 : 0;
  151. /* Initialize state first */
  152. insert_test_cpu = -1;
  153. enqueue_count = 0;
  154. dispatch_count = 0;
  155. phase1_complete = 0;
  156. total_peek_attempts = 0;
  157. successful_peeks = 0;
  158. /* Create the test and real DSQs */
  159. err = scx_bpf_create_dsq(test_dsq_id, -1);
  160. if (err) {
  161. scx_bpf_error("Failed to create DSQ %d: %d", test_dsq_id, err);
  162. return err;
  163. }
  164. err = scx_bpf_create_dsq(real_dsq_id, -1);
  165. if (err) {
  166. scx_bpf_error("Failed to create DSQ %d: %d", test_dsq_id, err);
  167. return err;
  168. }
  169. /* Create the DSQ pool for stress testing */
  170. bpf_for(i, 0, DSQ_POOL_SIZE) {
  171. int dsq_id = dsq_pool_base_id + i;
  172. err = scx_bpf_create_dsq(dsq_id, -1);
  173. if (err) {
  174. scx_bpf_error("Failed to create DSQ pool entry %d: %d", dsq_id, err);
  175. return err;
  176. }
  177. }
  178. /* Initialize the peek results map */
  179. bpf_for(i, 0, MAX_SAMPLES) {
  180. u32 key = i;
  181. long pid = -1;
  182. bpf_map_update_elem(&peek_results, &key, &pid, BPF_ANY);
  183. }
  184. return 0;
  185. }
  186. void BPF_STRUCT_OPS(peek_dsq_exit, struct scx_exit_info *ei)
  187. {
  188. int i;
  189. /* Destroy the primary DSQs */
  190. scx_bpf_destroy_dsq(test_dsq_id);
  191. scx_bpf_destroy_dsq(real_dsq_id);
  192. /* Destroy the DSQ pool */
  193. bpf_for(i, 0, DSQ_POOL_SIZE) {
  194. int dsq_id = dsq_pool_base_id + i;
  195. scx_bpf_destroy_dsq(dsq_id);
  196. }
  197. UEI_RECORD(uei, ei);
  198. }
  199. SEC(".struct_ops.link")
  200. struct sched_ext_ops peek_dsq_ops = {
  201. .enqueue = (void *)peek_dsq_enqueue,
  202. .dispatch = (void *)peek_dsq_dispatch,
  203. .init = (void *)peek_dsq_init,
  204. .exit = (void *)peek_dsq_exit,
  205. .name = "peek_dsq",
  206. };