136 lines · c
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_SOURCE8#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 18const 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 30static bool verbose;31static volatile int exit_req;32 33static 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 40static void sigint_handler(int dummy)41{42 exit_req = 1;43}44 45int 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);56restart: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 62 while ((opt = getopt(argc, argv, "s:c:pvh")) != -1) {63 switch (opt) {64 case 's':65 skel->rodata->slice_ns = strtoull(optarg, NULL, 0) * 1000;66 break;67 case 'c':68 skel->rodata->central_cpu = strtoul(optarg, NULL, 0);69 break;70 case 'v':71 verbose = true;72 break;73 default:74 fprintf(stderr, help_fmt, basename(argv[0]));75 return opt != 'h';76 }77 }78 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);82 83 SCX_OPS_LOAD(skel, central_ops, scx_central, uei);84 85 /*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 core89 * from a task that would benefit from avoiding preemption on one of90 * the tickless cores.91 *92 * Until BPF supports pinning the timer, it's not guaranteed that it93 * will always be invoked on the central CPU. In practice, this94 * suffices the majority of the time.95 */96 cpuset = CPU_ALLOC(skel->rodata->nr_cpu_ids);97 SCX_BUG_ON(!cpuset, "Failed to allocate cpuset");98 CPU_ZERO(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);103 CPU_FREE(cpuset);104 105 link = SCX_OPS_ATTACH(skel, central_ops, scx_central);106 107 if (!skel->data->timer_pinned)108 printf("WARNING : BPF_F_TIMER_CPU_PIN not available, timer not pinned to central\n");109 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",113 skel->bss->nr_total,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);124 fflush(stdout);125 sleep(1);126 }127 128 bpf_link__destroy(link);129 ecode = UEI_REPORT(skel, uei);130 scx_central__destroy(skel);131 132 if (UEI_ECODE_RESTART(ecode))133 goto restart;134 return 0;135}136