init_enable_count.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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 <signal.h>
  8. #include <stdio.h>
  9. #include <unistd.h>
  10. #include <sched.h>
  11. #include <bpf/bpf.h>
  12. #include <scx/common.h>
  13. #include <sys/wait.h>
  14. #include "scx_test.h"
  15. #include "init_enable_count.bpf.skel.h"
  16. #define SCHED_EXT 7
  17. static enum scx_test_status run_test(bool global)
  18. {
  19. struct init_enable_count *skel;
  20. struct bpf_link *link;
  21. const u32 num_children = 5, num_pre_forks = 1024;
  22. int ret, i, status;
  23. struct sched_param param = {};
  24. pid_t pids[num_pre_forks];
  25. int pipe_fds[2];
  26. SCX_FAIL_IF(pipe(pipe_fds) < 0, "Failed to create pipe");
  27. skel = init_enable_count__open();
  28. SCX_FAIL_IF(!skel, "Failed to open");
  29. SCX_ENUM_INIT(skel);
  30. if (!global)
  31. skel->struct_ops.init_enable_count_ops->flags |= SCX_OPS_SWITCH_PARTIAL;
  32. SCX_FAIL_IF(init_enable_count__load(skel), "Failed to load skel");
  33. /*
  34. * Fork a bunch of children before we attach the scheduler so that we
  35. * ensure (at least in practical terms) that there are more tasks that
  36. * transition from SCHED_OTHER -> SCHED_EXT than there are tasks that
  37. * take the fork() path either below or in other processes.
  38. *
  39. * All children will block on read() on the pipe until the parent closes
  40. * the write end after attaching the scheduler, which signals all of
  41. * them to exit simultaneously. Auto-reap so we don't have to wait on
  42. * them.
  43. */
  44. signal(SIGCHLD, SIG_IGN);
  45. for (i = 0; i < num_pre_forks; i++) {
  46. pid_t pid = fork();
  47. SCX_FAIL_IF(pid < 0, "Failed to fork child");
  48. if (pid == 0) {
  49. char buf;
  50. close(pipe_fds[1]);
  51. if (read(pipe_fds[0], &buf, 1) < 0)
  52. exit(1);
  53. close(pipe_fds[0]);
  54. exit(0);
  55. }
  56. }
  57. close(pipe_fds[0]);
  58. link = bpf_map__attach_struct_ops(skel->maps.init_enable_count_ops);
  59. SCX_FAIL_IF(!link, "Failed to attach struct_ops");
  60. /* Signal all pre-forked children to exit. */
  61. close(pipe_fds[1]);
  62. signal(SIGCHLD, SIG_DFL);
  63. bpf_link__destroy(link);
  64. SCX_GE(skel->bss->init_task_cnt, num_pre_forks);
  65. SCX_GE(skel->bss->exit_task_cnt, num_pre_forks);
  66. link = bpf_map__attach_struct_ops(skel->maps.init_enable_count_ops);
  67. SCX_FAIL_IF(!link, "Failed to attach struct_ops");
  68. /* SCHED_EXT children */
  69. for (i = 0; i < num_children; i++) {
  70. pids[i] = fork();
  71. SCX_FAIL_IF(pids[i] < 0, "Failed to fork child");
  72. if (pids[i] == 0) {
  73. ret = sched_setscheduler(0, SCHED_EXT, &param);
  74. SCX_BUG_ON(ret, "Failed to set sched to sched_ext");
  75. /*
  76. * Reset to SCHED_OTHER for half of them. Counts for
  77. * everything should still be the same regardless, as
  78. * ops.disable() is invoked even if a task is still on
  79. * SCHED_EXT before it exits.
  80. */
  81. if (i % 2 == 0) {
  82. ret = sched_setscheduler(0, SCHED_OTHER, &param);
  83. SCX_BUG_ON(ret, "Failed to reset sched to normal");
  84. }
  85. exit(0);
  86. }
  87. }
  88. for (i = 0; i < num_children; i++) {
  89. SCX_FAIL_IF(waitpid(pids[i], &status, 0) != pids[i],
  90. "Failed to wait for SCX child\n");
  91. SCX_FAIL_IF(status != 0, "SCX child %d exited with status %d\n", i,
  92. status);
  93. }
  94. /* SCHED_OTHER children */
  95. for (i = 0; i < num_children; i++) {
  96. pids[i] = fork();
  97. if (pids[i] == 0)
  98. exit(0);
  99. }
  100. for (i = 0; i < num_children; i++) {
  101. SCX_FAIL_IF(waitpid(pids[i], &status, 0) != pids[i],
  102. "Failed to wait for normal child\n");
  103. SCX_FAIL_IF(status != 0, "Normal child %d exited with status %d\n", i,
  104. status);
  105. }
  106. bpf_link__destroy(link);
  107. SCX_GE(skel->bss->init_task_cnt, 2 * num_children);
  108. SCX_GE(skel->bss->exit_task_cnt, 2 * num_children);
  109. if (global) {
  110. SCX_GE(skel->bss->enable_cnt, 2 * num_children);
  111. SCX_GE(skel->bss->disable_cnt, 2 * num_children);
  112. } else {
  113. SCX_EQ(skel->bss->enable_cnt, num_children);
  114. SCX_EQ(skel->bss->disable_cnt, num_children);
  115. }
  116. /*
  117. * We forked a ton of tasks before we attached the scheduler above, so
  118. * this should be fine. Technically it could be flaky if a ton of forks
  119. * are happening at the same time in other processes, but that should
  120. * be exceedingly unlikely.
  121. */
  122. SCX_GT(skel->bss->init_transition_cnt, skel->bss->init_fork_cnt);
  123. SCX_GE(skel->bss->init_fork_cnt, 2 * num_children);
  124. init_enable_count__destroy(skel);
  125. return SCX_TEST_PASS;
  126. }
  127. static enum scx_test_status run(void *ctx)
  128. {
  129. enum scx_test_status status;
  130. status = run_test(true);
  131. if (status != SCX_TEST_PASS)
  132. return status;
  133. return run_test(false);
  134. }
  135. struct scx_test init_enable_count = {
  136. .name = "init_enable_count",
  137. .description = "Verify we correctly count the occurrences of init, "
  138. "enable, etc callbacks.",
  139. .run = run,
  140. };
  141. REGISTER_SCX_TEST(&init_enable_count)