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 size_t cpuset_size; 54 55 libbpf_set_print(libbpf_print_fn); 56 signal(SIGINT, sigint_handler); 57 signal(SIGTERM, sigint_handler); 58 restart: 59 optind = 1; 60 skel = SCX_OPS_OPEN(central_ops, scx_central); 61 62 skel->rodata->central_cpu = 0; 63 skel->rodata->nr_cpu_ids = libbpf_num_possible_cpus(); 64 skel->rodata->slice_ns = __COMPAT_ENUM_OR_ZERO("scx_public_consts", "SCX_SLICE_DFL"); 65 66 assert(skel->rodata->nr_cpu_ids > 0); 67 assert(skel->rodata->nr_cpu_ids <= INT32_MAX); 68 69 while ((opt = getopt(argc, argv, "s:c:pvh")) != -1) { 70 switch (opt) { 71 case 's': 72 skel->rodata->slice_ns = strtoull(optarg, NULL, 0) * 1000; 73 break; 74 case 'c': { 75 u32 central_cpu = strtoul(optarg, NULL, 0); 76 if (central_cpu >= skel->rodata->nr_cpu_ids) { 77 fprintf(stderr, "invalid central CPU id value, %u given (%u max)\n", central_cpu, skel->rodata->nr_cpu_ids); 78 scx_central__destroy(skel); 79 return -1; 80 } 81 skel->rodata->central_cpu = (s32)central_cpu; 82 break; 83 } 84 case 'v': 85 verbose = true; 86 break; 87 default: 88 fprintf(stderr, help_fmt, basename(argv[0])); 89 return opt != 'h'; 90 } 91 } 92 93 /* Resize arrays so their element count is equal to cpu count. */ 94 RESIZE_ARRAY(skel, data, cpu_gimme_task, skel->rodata->nr_cpu_ids); 95 RESIZE_ARRAY(skel, data, cpu_started_at, skel->rodata->nr_cpu_ids); 96 97 SCX_OPS_LOAD(skel, central_ops, scx_central, uei); 98 99 /* 100 * Affinitize the loading thread to the central CPU, as: 101 * - That's where the BPF timer is first invoked in the BPF program. 102 * - We probably don't want this user space component to take up a core 103 * from a task that would benefit from avoiding preemption on one of 104 * the tickless cores. 105 * 106 * Until BPF supports pinning the timer, it's not guaranteed that it 107 * will always be invoked on the central CPU. In practice, this 108 * suffices the majority of the time. 109 */ 110 cpuset = CPU_ALLOC(skel->rodata->nr_cpu_ids); 111 SCX_BUG_ON(!cpuset, "Failed to allocate cpuset"); 112 cpuset_size = CPU_ALLOC_SIZE(skel->rodata->nr_cpu_ids); 113 CPU_ZERO_S(cpuset_size, cpuset); 114 CPU_SET_S(skel->rodata->central_cpu, cpuset_size, cpuset); 115 SCX_BUG_ON(sched_setaffinity(0, cpuset_size, cpuset), 116 "Failed to affinitize to central CPU %d (max %d)", 117 skel->rodata->central_cpu, skel->rodata->nr_cpu_ids - 1); 118 CPU_FREE(cpuset); 119 120 link = SCX_OPS_ATTACH(skel, central_ops, scx_central); 121 122 if (!skel->data->timer_pinned) 123 printf("WARNING : BPF_F_TIMER_CPU_PIN not available, timer not pinned to central\n"); 124 125 while (!exit_req && !UEI_EXITED(skel, uei)) { 126 printf("[SEQ %llu]\n", seq++); 127 printf("total :%10" PRIu64 " local:%10" PRIu64 " queued:%10" PRIu64 " lost:%10" PRIu64 "\n", 128 skel->bss->nr_total, 129 skel->bss->nr_locals, 130 skel->bss->nr_queued, 131 skel->bss->nr_lost_pids); 132 printf("timer :%10" PRIu64 " dispatch:%10" PRIu64 " mismatch:%10" PRIu64 " retry:%10" PRIu64 "\n", 133 skel->bss->nr_timers, 134 skel->bss->nr_dispatches, 135 skel->bss->nr_mismatches, 136 skel->bss->nr_retries); 137 printf("overflow:%10" PRIu64 "\n", 138 skel->bss->nr_overflows); 139 fflush(stdout); 140 sleep(1); 141 } 142 143 bpf_link__destroy(link); 144 ecode = UEI_REPORT(skel, uei); 145 scx_central__destroy(skel); 146 147 if (UEI_ECODE_RESTART(ecode)) 148 goto restart; 149 return 0; 150 } 151