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 <libgen.h>
14  #include <bpf/bpf.h>
15  #include <scx/common.h>
16  #include "scx_central.bpf.skel.h"
17  
18  const 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  
30  static bool verbose;
31  static volatile int exit_req;
32  
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)33  static 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  
sigint_handler(int dummy)40  static void sigint_handler(int dummy)
41  {
42  	exit_req = 1;
43  }
44  
main(int argc,char ** argv)45  int 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);
56  restart:
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 core
89  	 *   from a task that would benefit from avoiding preemption on one of
90  	 *   the tickless cores.
91  	 *
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.
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