|  | /* SPDX-License-Identifier: GPL-2.0 */ | 
|  | /* | 
|  | * Copyright (c) 2022 Meta Platforms, Inc. and affiliates. | 
|  | * Copyright (c) 2022 Tejun Heo <tj@kernel.org> | 
|  | * Copyright (c) 2022 David Vernet <dvernet@meta.com> | 
|  | */ | 
|  | #define _GNU_SOURCE | 
|  | #include <sched.h> | 
|  | #include <stdio.h> | 
|  | #include <unistd.h> | 
|  | #include <inttypes.h> | 
|  | #include <signal.h> | 
|  | #include <assert.h> | 
|  | #include <libgen.h> | 
|  | #include <bpf/bpf.h> | 
|  | #include <scx/common.h> | 
|  | #include "scx_central.bpf.skel.h" | 
|  |  | 
|  | const char help_fmt[] = | 
|  | "A central FIFO sched_ext scheduler.\n" | 
|  | "\n" | 
|  | "See the top-level comment in .bpf.c for more details.\n" | 
|  | "\n" | 
|  | "Usage: %s [-s SLICE_US] [-c CPU]\n" | 
|  | "\n" | 
|  | "  -s SLICE_US   Override slice duration\n" | 
|  | "  -c CPU        Override the central CPU (default: 0)\n" | 
|  | "  -v            Print libbpf debug messages\n" | 
|  | "  -h            Display this help and exit\n"; | 
|  |  | 
|  | static bool verbose; | 
|  | static volatile int exit_req; | 
|  |  | 
|  | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) | 
|  | { | 
|  | if (level == LIBBPF_DEBUG && !verbose) | 
|  | return 0; | 
|  | return vfprintf(stderr, format, args); | 
|  | } | 
|  |  | 
|  | static void sigint_handler(int dummy) | 
|  | { | 
|  | exit_req = 1; | 
|  | } | 
|  |  | 
|  | int main(int argc, char **argv) | 
|  | { | 
|  | struct scx_central *skel; | 
|  | struct bpf_link *link; | 
|  | __u64 seq = 0, ecode; | 
|  | __s32 opt; | 
|  | cpu_set_t *cpuset; | 
|  |  | 
|  | libbpf_set_print(libbpf_print_fn); | 
|  | signal(SIGINT, sigint_handler); | 
|  | signal(SIGTERM, sigint_handler); | 
|  | restart: | 
|  | skel = SCX_OPS_OPEN(central_ops, scx_central); | 
|  |  | 
|  | skel->rodata->central_cpu = 0; | 
|  | skel->rodata->nr_cpu_ids = libbpf_num_possible_cpus(); | 
|  | skel->rodata->slice_ns = __COMPAT_ENUM_OR_ZERO("scx_public_consts", "SCX_SLICE_DFL"); | 
|  |  | 
|  | assert(skel->rodata->nr_cpu_ids <= INT32_MAX); | 
|  |  | 
|  | while ((opt = getopt(argc, argv, "s:c:pvh")) != -1) { | 
|  | switch (opt) { | 
|  | case 's': | 
|  | skel->rodata->slice_ns = strtoull(optarg, NULL, 0) * 1000; | 
|  | break; | 
|  | case 'c': { | 
|  | u32 central_cpu = strtoul(optarg, NULL, 0); | 
|  | if (central_cpu >= skel->rodata->nr_cpu_ids) { | 
|  | fprintf(stderr, "invalid central CPU id value, %u given (%u max)\n", central_cpu, skel->rodata->nr_cpu_ids); | 
|  | return -1; | 
|  | } | 
|  | skel->rodata->central_cpu = (s32)central_cpu; | 
|  | break; | 
|  | } | 
|  | case 'v': | 
|  | verbose = true; | 
|  | break; | 
|  | default: | 
|  | fprintf(stderr, help_fmt, basename(argv[0])); | 
|  | return opt != 'h'; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Resize arrays so their element count is equal to cpu count. */ | 
|  | RESIZE_ARRAY(skel, data, cpu_gimme_task, skel->rodata->nr_cpu_ids); | 
|  | RESIZE_ARRAY(skel, data, cpu_started_at, skel->rodata->nr_cpu_ids); | 
|  |  | 
|  | SCX_OPS_LOAD(skel, central_ops, scx_central, uei); | 
|  |  | 
|  | /* | 
|  | * Affinitize the loading thread to the central CPU, as: | 
|  | * - That's where the BPF timer is first invoked in the BPF program. | 
|  | * - We probably don't want this user space component to take up a core | 
|  | *   from a task that would benefit from avoiding preemption on one of | 
|  | *   the tickless cores. | 
|  | * | 
|  | * Until BPF supports pinning the timer, it's not guaranteed that it | 
|  | * will always be invoked on the central CPU. In practice, this | 
|  | * suffices the majority of the time. | 
|  | */ | 
|  | cpuset = CPU_ALLOC(skel->rodata->nr_cpu_ids); | 
|  | SCX_BUG_ON(!cpuset, "Failed to allocate cpuset"); | 
|  | CPU_ZERO_S(CPU_ALLOC_SIZE(skel->rodata->nr_cpu_ids), cpuset); | 
|  | CPU_SET(skel->rodata->central_cpu, cpuset); | 
|  | SCX_BUG_ON(sched_setaffinity(0, sizeof(*cpuset), cpuset), | 
|  | "Failed to affinitize to central CPU %d (max %d)", | 
|  | skel->rodata->central_cpu, skel->rodata->nr_cpu_ids - 1); | 
|  | CPU_FREE(cpuset); | 
|  |  | 
|  | link = SCX_OPS_ATTACH(skel, central_ops, scx_central); | 
|  |  | 
|  | if (!skel->data->timer_pinned) | 
|  | printf("WARNING : BPF_F_TIMER_CPU_PIN not available, timer not pinned to central\n"); | 
|  |  | 
|  | while (!exit_req && !UEI_EXITED(skel, uei)) { | 
|  | printf("[SEQ %llu]\n", seq++); | 
|  | printf("total   :%10" PRIu64 "    local:%10" PRIu64 "   queued:%10" PRIu64 "  lost:%10" PRIu64 "\n", | 
|  | skel->bss->nr_total, | 
|  | skel->bss->nr_locals, | 
|  | skel->bss->nr_queued, | 
|  | skel->bss->nr_lost_pids); | 
|  | printf("timer   :%10" PRIu64 " dispatch:%10" PRIu64 " mismatch:%10" PRIu64 " retry:%10" PRIu64 "\n", | 
|  | skel->bss->nr_timers, | 
|  | skel->bss->nr_dispatches, | 
|  | skel->bss->nr_mismatches, | 
|  | skel->bss->nr_retries); | 
|  | printf("overflow:%10" PRIu64 "\n", | 
|  | skel->bss->nr_overflows); | 
|  | fflush(stdout); | 
|  | sleep(1); | 
|  | } | 
|  |  | 
|  | bpf_link__destroy(link); | 
|  | ecode = UEI_REPORT(skel, uei); | 
|  | scx_central__destroy(skel); | 
|  |  | 
|  | if (UEI_ECODE_RESTART(ecode)) | 
|  | goto restart; | 
|  | return 0; | 
|  | } |