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