scx_central.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
  4. * Copyright (c) 2022 Tejun Heo <tj@kernel.org>
  5. * Copyright (c) 2022 David Vernet <dvernet@meta.com>
  6. */
  7. #define _GNU_SOURCE
  8. #include <sched.h>
  9. #include <stdio.h>
  10. #include <unistd.h>
  11. #include <inttypes.h>
  12. #include <signal.h>
  13. #include <assert.h>
  14. #include <libgen.h>
  15. #include <bpf/bpf.h>
  16. #include <scx/common.h>
  17. #include "scx_central.bpf.skel.h"
  18. const char help_fmt[] =
  19. "A central FIFO sched_ext scheduler.\n"
  20. "\n"
  21. "See the top-level comment in .bpf.c for more details.\n"
  22. "\n"
  23. "Usage: %s [-s SLICE_US] [-c CPU]\n"
  24. "\n"
  25. " -s SLICE_US Override slice duration\n"
  26. " -c CPU Override the central CPU (default: 0)\n"
  27. " -v Print libbpf debug messages\n"
  28. " -h Display this help and exit\n";
  29. static bool verbose;
  30. static volatile int exit_req;
  31. static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
  32. {
  33. if (level == LIBBPF_DEBUG && !verbose)
  34. return 0;
  35. return vfprintf(stderr, format, args);
  36. }
  37. static void sigint_handler(int dummy)
  38. {
  39. exit_req = 1;
  40. }
  41. int main(int argc, char **argv)
  42. {
  43. struct scx_central *skel;
  44. struct bpf_link *link;
  45. __u64 seq = 0, ecode;
  46. __s32 opt;
  47. cpu_set_t *cpuset;
  48. size_t cpuset_size;
  49. libbpf_set_print(libbpf_print_fn);
  50. signal(SIGINT, sigint_handler);
  51. signal(SIGTERM, sigint_handler);
  52. restart:
  53. optind = 1;
  54. skel = SCX_OPS_OPEN(central_ops, scx_central);
  55. skel->rodata->central_cpu = 0;
  56. skel->rodata->nr_cpu_ids = libbpf_num_possible_cpus();
  57. skel->rodata->slice_ns = __COMPAT_ENUM_OR_ZERO("scx_public_consts", "SCX_SLICE_DFL");
  58. assert(skel->rodata->nr_cpu_ids > 0);
  59. assert(skel->rodata->nr_cpu_ids <= INT32_MAX);
  60. while ((opt = getopt(argc, argv, "s:c:vh")) != -1) {
  61. switch (opt) {
  62. case 's':
  63. skel->rodata->slice_ns = strtoull(optarg, NULL, 0) * 1000;
  64. break;
  65. case 'c': {
  66. u32 central_cpu = strtoul(optarg, NULL, 0);
  67. if (central_cpu >= skel->rodata->nr_cpu_ids) {
  68. fprintf(stderr, "invalid central CPU id value, %u given (%u max)\n", central_cpu, skel->rodata->nr_cpu_ids);
  69. scx_central__destroy(skel);
  70. return -1;
  71. }
  72. skel->rodata->central_cpu = (s32)central_cpu;
  73. break;
  74. }
  75. case 'v':
  76. verbose = true;
  77. break;
  78. default:
  79. fprintf(stderr, help_fmt, basename(argv[0]));
  80. return opt != 'h';
  81. }
  82. }
  83. /* Resize arrays so their element count is equal to cpu count. */
  84. RESIZE_ARRAY(skel, data, cpu_gimme_task, skel->rodata->nr_cpu_ids);
  85. RESIZE_ARRAY(skel, data, cpu_started_at, skel->rodata->nr_cpu_ids);
  86. SCX_OPS_LOAD(skel, central_ops, scx_central, uei);
  87. /*
  88. * Affinitize the loading thread to the central CPU, as:
  89. * - That's where the BPF timer is first invoked in the BPF program.
  90. * - We probably don't want this user space component to take up a core
  91. * from a task that would benefit from avoiding preemption on one of
  92. * the tickless cores.
  93. *
  94. * Until BPF supports pinning the timer, it's not guaranteed that it
  95. * will always be invoked on the central CPU. In practice, this
  96. * suffices the majority of the time.
  97. */
  98. cpuset = CPU_ALLOC(skel->rodata->nr_cpu_ids);
  99. SCX_BUG_ON(!cpuset, "Failed to allocate cpuset");
  100. cpuset_size = CPU_ALLOC_SIZE(skel->rodata->nr_cpu_ids);
  101. CPU_ZERO_S(cpuset_size, cpuset);
  102. CPU_SET_S(skel->rodata->central_cpu, cpuset_size, cpuset);
  103. SCX_BUG_ON(sched_setaffinity(0, cpuset_size, cpuset),
  104. "Failed to affinitize to central CPU %d (max %d)",
  105. skel->rodata->central_cpu, skel->rodata->nr_cpu_ids - 1);
  106. CPU_FREE(cpuset);
  107. link = SCX_OPS_ATTACH(skel, central_ops, scx_central);
  108. if (!skel->data->timer_pinned)
  109. printf("WARNING : BPF_F_TIMER_CPU_PIN not available, timer not pinned to central\n");
  110. while (!exit_req && !UEI_EXITED(skel, uei)) {
  111. printf("[SEQ %llu]\n", seq++);
  112. printf("total :%10" PRIu64 " local:%10" PRIu64 " queued:%10" PRIu64 " lost:%10" PRIu64 "\n",
  113. skel->bss->nr_total,
  114. skel->bss->nr_locals,
  115. skel->bss->nr_queued,
  116. skel->bss->nr_lost_pids);
  117. printf("timer :%10" PRIu64 " dispatch:%10" PRIu64 " mismatch:%10" PRIu64 " retry:%10" PRIu64 "\n",
  118. skel->bss->nr_timers,
  119. skel->bss->nr_dispatches,
  120. skel->bss->nr_mismatches,
  121. skel->bss->nr_retries);
  122. printf("overflow:%10" PRIu64 "\n",
  123. skel->bss->nr_overflows);
  124. fflush(stdout);
  125. sleep(1);
  126. }
  127. bpf_link__destroy(link);
  128. ecode = UEI_REPORT(skel, uei);
  129. scx_central__destroy(skel);
  130. if (UEI_ECODE_RESTART(ecode))
  131. goto restart;
  132. return 0;
  133. }