1 /* SPDX-License-Identifier: GPL-2.0 */
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>
15 #include <scx/common.h>
16 #include "scx_central.bpf.skel.h"
18 const char help_fmt
[] =
19 "A central FIFO sched_ext scheduler.\n"
21 "See the top-level comment in .bpf.c for more details.\n"
23 "Usage: %s [-s SLICE_US] [-c CPU]\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";
31 static volatile int exit_req
;
33 static int libbpf_print_fn(enum libbpf_print_level level
, const char *format
, va_list args
)
35 if (level
== LIBBPF_DEBUG
&& !verbose
)
37 return vfprintf(stderr
, format
, args
);
40 static void sigint_handler(int dummy
)
45 int main(int argc
, char **argv
)
47 struct scx_central
*skel
;
48 struct bpf_link
*link
;
53 libbpf_set_print(libbpf_print_fn
);
54 signal(SIGINT
, sigint_handler
);
55 signal(SIGTERM
, sigint_handler
);
57 skel
= SCX_OPS_OPEN(central_ops
, scx_central
);
59 skel
->rodata
->central_cpu
= 0;
60 skel
->rodata
->nr_cpu_ids
= libbpf_num_possible_cpus();
62 while ((opt
= getopt(argc
, argv
, "s:c:pvh")) != -1) {
65 skel
->rodata
->slice_ns
= strtoull(optarg
, NULL
, 0) * 1000;
68 skel
->rodata
->central_cpu
= strtoul(optarg
, NULL
, 0);
74 fprintf(stderr
, help_fmt
, basename(argv
[0]));
79 /* Resize arrays so their element count is equal to cpu count. */
80 RESIZE_ARRAY(skel
, data
, cpu_gimme_task
, skel
->rodata
->nr_cpu_ids
);
81 RESIZE_ARRAY(skel
, data
, cpu_started_at
, skel
->rodata
->nr_cpu_ids
);
83 SCX_OPS_LOAD(skel
, central_ops
, scx_central
, uei
);
86 * Affinitize the loading thread to the central CPU, as:
87 * - That's where the BPF timer is first invoked in the BPF program.
88 * - We probably don't want this user space component to take up a core
89 * from a task that would benefit from avoiding preemption on one of
92 * Until BPF supports pinning the timer, it's not guaranteed that it
93 * will always be invoked on the central CPU. In practice, this
94 * suffices the majority of the time.
96 cpuset
= CPU_ALLOC(skel
->rodata
->nr_cpu_ids
);
97 SCX_BUG_ON(!cpuset
, "Failed to allocate cpuset");
99 CPU_SET(skel
->rodata
->central_cpu
, cpuset
);
100 SCX_BUG_ON(sched_setaffinity(0, sizeof(cpuset
), cpuset
),
101 "Failed to affinitize to central CPU %d (max %d)",
102 skel
->rodata
->central_cpu
, skel
->rodata
->nr_cpu_ids
- 1);
105 link
= SCX_OPS_ATTACH(skel
, central_ops
, scx_central
);
107 if (!skel
->data
->timer_pinned
)
108 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",
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
);
128 bpf_link__destroy(link
);
129 ecode
= UEI_REPORT(skel
, uei
);
130 scx_central__destroy(skel
);
132 if (UEI_ECODE_RESTART(ecode
))