init_enable_count.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
  4. * Copyright (c) 2023 David Vernet <dvernet@meta.com>
  5. * Copyright (c) 2023 Tejun Heo <tj@kernel.org>
  6. */
  7. #include <stdio.h>
  8. #include <unistd.h>
  9. #include <sched.h>
  10. #include <bpf/bpf.h>
  11. #include <scx/common.h>
  12. #include <sys/wait.h>
  13. #include "scx_test.h"
  14. #include "init_enable_count.bpf.skel.h"
  15. #define SCHED_EXT 7
  16. static struct init_enable_count *
  17. open_load_prog(bool global)
  18. {
  19. struct init_enable_count *skel;
  20. skel = init_enable_count__open();
  21. SCX_BUG_ON(!skel, "Failed to open skel");
  22. if (!global)
  23. skel->struct_ops.init_enable_count_ops->flags |= SCX_OPS_SWITCH_PARTIAL;
  24. SCX_BUG_ON(init_enable_count__load(skel), "Failed to load skel");
  25. return skel;
  26. }
  27. static enum scx_test_status run_test(bool global)
  28. {
  29. struct init_enable_count *skel;
  30. struct bpf_link *link;
  31. const u32 num_children = 5, num_pre_forks = 1024;
  32. int ret, i, status;
  33. struct sched_param param = {};
  34. pid_t pids[num_pre_forks];
  35. skel = open_load_prog(global);
  36. /*
  37. * Fork a bunch of children before we attach the scheduler so that we
  38. * ensure (at least in practical terms) that there are more tasks that
  39. * transition from SCHED_OTHER -> SCHED_EXT than there are tasks that
  40. * take the fork() path either below or in other processes.
  41. */
  42. for (i = 0; i < num_pre_forks; i++) {
  43. pids[i] = fork();
  44. SCX_FAIL_IF(pids[i] < 0, "Failed to fork child");
  45. if (pids[i] == 0) {
  46. sleep(1);
  47. exit(0);
  48. }
  49. }
  50. link = bpf_map__attach_struct_ops(skel->maps.init_enable_count_ops);
  51. SCX_FAIL_IF(!link, "Failed to attach struct_ops");
  52. for (i = 0; i < num_pre_forks; i++) {
  53. SCX_FAIL_IF(waitpid(pids[i], &status, 0) != pids[i],
  54. "Failed to wait for pre-forked child\n");
  55. SCX_FAIL_IF(status != 0, "Pre-forked child %d exited with status %d\n", i,
  56. status);
  57. }
  58. bpf_link__destroy(link);
  59. SCX_GE(skel->bss->init_task_cnt, num_pre_forks);
  60. SCX_GE(skel->bss->exit_task_cnt, num_pre_forks);
  61. link = bpf_map__attach_struct_ops(skel->maps.init_enable_count_ops);
  62. SCX_FAIL_IF(!link, "Failed to attach struct_ops");
  63. /* SCHED_EXT children */
  64. for (i = 0; i < num_children; i++) {
  65. pids[i] = fork();
  66. SCX_FAIL_IF(pids[i] < 0, "Failed to fork child");
  67. if (pids[i] == 0) {
  68. ret = sched_setscheduler(0, SCHED_EXT, &param);
  69. SCX_BUG_ON(ret, "Failed to set sched to sched_ext");
  70. /*
  71. * Reset to SCHED_OTHER for half of them. Counts for
  72. * everything should still be the same regardless, as
  73. * ops.disable() is invoked even if a task is still on
  74. * SCHED_EXT before it exits.
  75. */
  76. if (i % 2 == 0) {
  77. ret = sched_setscheduler(0, SCHED_OTHER, &param);
  78. SCX_BUG_ON(ret, "Failed to reset sched to normal");
  79. }
  80. exit(0);
  81. }
  82. }
  83. for (i = 0; i < num_children; i++) {
  84. SCX_FAIL_IF(waitpid(pids[i], &status, 0) != pids[i],
  85. "Failed to wait for SCX child\n");
  86. SCX_FAIL_IF(status != 0, "SCX child %d exited with status %d\n", i,
  87. status);
  88. }
  89. /* SCHED_OTHER children */
  90. for (i = 0; i < num_children; i++) {
  91. pids[i] = fork();
  92. if (pids[i] == 0)
  93. exit(0);
  94. }
  95. for (i = 0; i < num_children; i++) {
  96. SCX_FAIL_IF(waitpid(pids[i], &status, 0) != pids[i],
  97. "Failed to wait for normal child\n");
  98. SCX_FAIL_IF(status != 0, "Normal child %d exited with status %d\n", i,
  99. status);
  100. }
  101. bpf_link__destroy(link);
  102. SCX_GE(skel->bss->init_task_cnt, 2 * num_children);
  103. SCX_GE(skel->bss->exit_task_cnt, 2 * num_children);
  104. if (global) {
  105. SCX_GE(skel->bss->enable_cnt, 2 * num_children);
  106. SCX_GE(skel->bss->disable_cnt, 2 * num_children);
  107. } else {
  108. SCX_EQ(skel->bss->enable_cnt, num_children);
  109. SCX_EQ(skel->bss->disable_cnt, num_children);
  110. }
  111. /*
  112. * We forked a ton of tasks before we attached the scheduler above, so
  113. * this should be fine. Technically it could be flaky if a ton of forks
  114. * are happening at the same time in other processes, but that should
  115. * be exceedingly unlikely.
  116. */
  117. SCX_GT(skel->bss->init_transition_cnt, skel->bss->init_fork_cnt);
  118. SCX_GE(skel->bss->init_fork_cnt, 2 * num_children);
  119. init_enable_count__destroy(skel);
  120. return SCX_TEST_PASS;
  121. }
  122. static enum scx_test_status run(void *ctx)
  123. {
  124. enum scx_test_status status;
  125. status = run_test(true);
  126. if (status != SCX_TEST_PASS)
  127. return status;
  128. return run_test(false);
  129. }
  130. struct scx_test init_enable_count = {
  131. .name = "init_enable_count",
  132. .description = "Verify we do the correct amount of counting of init, "
  133. "enable, etc callbacks.",
  134. .run = run,
  135. };
  136. REGISTER_SCX_TEST(&init_enable_count)