1 /* SPDX-License-Identifier: GPL-2.0 */
3 * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 * Copyright (c) 2023 Tejun Heo <tj@kernel.org>
5 * Copyright (c) 2023 David Vernet <dvernet@meta.com>
16 #include <scx/common.h>
17 #include "scx_flatcg.h"
18 #include "scx_flatcg.bpf.skel.h"
21 #define FILEID_KERNFS 0xfe
24 const char help_fmt
[] =
25 "A flattened cgroup hierarchy sched_ext scheduler.\n"
27 "See the top-level comment in .bpf.c for more details.\n"
29 "Usage: %s [-s SLICE_US] [-i INTERVAL] [-f] [-v]\n"
31 " -s SLICE_US Override slice duration\n"
32 " -i INTERVAL Report interval\n"
33 " -f Use FIFO scheduling instead of weighted vtime scheduling\n"
34 " -v Print libbpf debug messages\n"
35 " -h Display this help and exit\n";
38 static volatile int exit_req
;
40 static int libbpf_print_fn(enum libbpf_print_level level
, const char *format
, va_list args
)
42 if (level
== LIBBPF_DEBUG
&& !verbose
)
44 return vfprintf(stderr
, format
, args
);
47 static void sigint_handler(int dummy
)
52 static float read_cpu_util(__u64
*last_sum
, __u64
*last_idle
)
56 char *line
, *cur
= NULL
, *tok
;
57 __u64 sum
= 0, idle
= 0;
58 __u64 delta_sum
, delta_idle
;
61 fp
= fopen("/proc/stat", "r");
63 perror("fopen(\"/proc/stat\")");
67 if (!fgets(buf
, sizeof(buf
), fp
)) {
68 perror("fgets(\"/proc/stat\")");
75 for (idx
= 0; (tok
= strtok_r(line
, " \n", &cur
)); idx
++) {
83 v
= strtoull(tok
, &endp
, 0);
84 if (!endp
|| *endp
!= '\0') {
85 fprintf(stderr
, "failed to parse %dth field of /proc/stat (\"%s\")\n",
94 delta_sum
= sum
- *last_sum
;
95 delta_idle
= idle
- *last_idle
;
99 return delta_sum
? (float)(delta_sum
- delta_idle
) / delta_sum
: 0.0;
102 static void fcg_read_stats(struct scx_flatcg
*skel
, __u64
*stats
)
104 __u64 cnts
[FCG_NR_STATS
][skel
->rodata
->nr_cpus
];
107 memset(stats
, 0, sizeof(stats
[0]) * FCG_NR_STATS
);
109 for (idx
= 0; idx
< FCG_NR_STATS
; idx
++) {
112 ret
= bpf_map_lookup_elem(bpf_map__fd(skel
->maps
.stats
),
116 for (cpu
= 0; cpu
< skel
->rodata
->nr_cpus
; cpu
++)
117 stats
[idx
] += cnts
[idx
][cpu
];
121 int main(int argc
, char **argv
)
123 struct scx_flatcg
*skel
;
124 struct bpf_link
*link
;
125 struct timespec intv_ts
= { .tv_sec
= 2, .tv_nsec
= 0 };
126 bool dump_cgrps
= false;
127 __u64 last_cpu_sum
= 0, last_cpu_idle
= 0;
128 __u64 last_stats
[FCG_NR_STATS
] = {};
129 unsigned long seq
= 0;
133 libbpf_set_print(libbpf_print_fn
);
134 signal(SIGINT
, sigint_handler
);
135 signal(SIGTERM
, sigint_handler
);
137 skel
= SCX_OPS_OPEN(flatcg_ops
, scx_flatcg
);
139 skel
->rodata
->nr_cpus
= libbpf_num_possible_cpus();
141 while ((opt
= getopt(argc
, argv
, "s:i:dfvh")) != -1) {
146 v
= strtod(optarg
, NULL
);
147 skel
->rodata
->cgrp_slice_ns
= v
* 1000;
150 v
= strtod(optarg
, NULL
);
152 intv_ts
.tv_nsec
= (v
- (float)intv_ts
.tv_sec
) * 1000000000;
158 skel
->rodata
->fifo_sched
= true;
165 fprintf(stderr
, help_fmt
, basename(argv
[0]));
170 printf("slice=%.1lfms intv=%.1lfs dump_cgrps=%d",
171 (double)skel
->rodata
->cgrp_slice_ns
/ 1000000.0,
172 (double)intv_ts
.tv_sec
+ (double)intv_ts
.tv_nsec
/ 1000000000.0,
175 SCX_OPS_LOAD(skel
, flatcg_ops
, scx_flatcg
, uei
);
176 link
= SCX_OPS_ATTACH(skel
, flatcg_ops
, scx_flatcg
);
178 while (!exit_req
&& !UEI_EXITED(skel
, uei
)) {
179 __u64 acc_stats
[FCG_NR_STATS
];
180 __u64 stats
[FCG_NR_STATS
];
184 cpu_util
= read_cpu_util(&last_cpu_sum
, &last_cpu_idle
);
186 fcg_read_stats(skel
, acc_stats
);
187 for (i
= 0; i
< FCG_NR_STATS
; i
++)
188 stats
[i
] = acc_stats
[i
] - last_stats
[i
];
190 memcpy(last_stats
, acc_stats
, sizeof(acc_stats
));
192 printf("\n[SEQ %6lu cpu=%5.1lf hweight_gen=%" PRIu64
"]\n",
193 seq
++, cpu_util
* 100.0, skel
->data
->hweight_gen
);
194 printf(" act:%6llu deact:%6llu global:%6llu local:%6llu\n",
196 stats
[FCG_STAT_DEACT
],
197 stats
[FCG_STAT_GLOBAL
],
198 stats
[FCG_STAT_LOCAL
]);
199 printf("HWT cache:%6llu update:%6llu skip:%6llu race:%6llu\n",
200 stats
[FCG_STAT_HWT_CACHE
],
201 stats
[FCG_STAT_HWT_UPDATES
],
202 stats
[FCG_STAT_HWT_SKIP
],
203 stats
[FCG_STAT_HWT_RACE
]);
204 printf("ENQ skip:%6llu race:%6llu\n",
205 stats
[FCG_STAT_ENQ_SKIP
],
206 stats
[FCG_STAT_ENQ_RACE
]);
207 printf("CNS keep:%6llu expire:%6llu empty:%6llu gone:%6llu\n",
208 stats
[FCG_STAT_CNS_KEEP
],
209 stats
[FCG_STAT_CNS_EXPIRE
],
210 stats
[FCG_STAT_CNS_EMPTY
],
211 stats
[FCG_STAT_CNS_GONE
]);
212 printf("PNC next:%6llu empty:%6llu nocgrp:%6llu gone:%6llu race:%6llu fail:%6llu\n",
213 stats
[FCG_STAT_PNC_NEXT
],
214 stats
[FCG_STAT_PNC_EMPTY
],
215 stats
[FCG_STAT_PNC_NO_CGRP
],
216 stats
[FCG_STAT_PNC_GONE
],
217 stats
[FCG_STAT_PNC_RACE
],
218 stats
[FCG_STAT_PNC_FAIL
]);
219 printf("BAD remove:%6llu\n",
220 acc_stats
[FCG_STAT_BAD_REMOVAL
]);
223 nanosleep(&intv_ts
, NULL
);
226 bpf_link__destroy(link
);
227 ecode
= UEI_REPORT(skel
, uei
);
228 scx_flatcg__destroy(skel
);
230 if (UEI_ECODE_RESTART(ecode
))