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