1a1270fe9Sbrakmo // SPDX-License-Identifier: GPL-2.0 2a1270fe9Sbrakmo /* Copyright (c) 2019 Facebook 3a1270fe9Sbrakmo * 4a1270fe9Sbrakmo * This program is free software; you can redistribute it and/or 5a1270fe9Sbrakmo * modify it under the terms of version 2 of the GNU General Public 6a1270fe9Sbrakmo * License as published by the Free Software Foundation. 7a1270fe9Sbrakmo * 8a1270fe9Sbrakmo * Example program for Host Bandwidth Managment 9a1270fe9Sbrakmo * 10a1270fe9Sbrakmo * This program loads a cgroup skb BPF program to enforce cgroup output 11a1270fe9Sbrakmo * (egress) or input (ingress) bandwidth limits. 12a1270fe9Sbrakmo * 13a1270fe9Sbrakmo * USAGE: hbm [-d] [-l] [-n <id>] [-r <rate>] [-s] [-t <secs>] [-w] [-h] [prog] 14a1270fe9Sbrakmo * Where: 15a1270fe9Sbrakmo * -d Print BPF trace debug buffer 16a1270fe9Sbrakmo * -l Also limit flows doing loopback 17a1270fe9Sbrakmo * -n <#> To create cgroup \"/hbm#\" and attach prog 18a1270fe9Sbrakmo * Default is /hbm1 19a1270fe9Sbrakmo * -r <rate> Rate limit in Mbps 20a1270fe9Sbrakmo * -s Get HBM stats (marked, dropped, etc.) 215b4f21b2SColin Ian King * -t <time> Exit after specified seconds (default is 0) 22a1270fe9Sbrakmo * -w Work conserving flag. cgroup can increase its bandwidth 23a1270fe9Sbrakmo * beyond the rate limit specified while there is available 24a1270fe9Sbrakmo * bandwidth. Current implementation assumes there is only 25a1270fe9Sbrakmo * NIC (eth0), but can be extended to support multiple NICs. 26a1270fe9Sbrakmo * Currrently only supported for egress. 27a1270fe9Sbrakmo * -h Print this info 28a1270fe9Sbrakmo * prog BPF program file name. Name defaults to hbm_out_kern.o 29a1270fe9Sbrakmo */ 30a1270fe9Sbrakmo 31a1270fe9Sbrakmo #define _GNU_SOURCE 32a1270fe9Sbrakmo 33a1270fe9Sbrakmo #include <stdio.h> 34a1270fe9Sbrakmo #include <stdlib.h> 35a1270fe9Sbrakmo #include <assert.h> 36a1270fe9Sbrakmo #include <sys/resource.h> 37a1270fe9Sbrakmo #include <sys/time.h> 38a1270fe9Sbrakmo #include <unistd.h> 39a1270fe9Sbrakmo #include <errno.h> 40a1270fe9Sbrakmo #include <fcntl.h> 41a1270fe9Sbrakmo #include <linux/unistd.h> 42a1270fe9Sbrakmo 43a1270fe9Sbrakmo #include <linux/bpf.h> 44a1270fe9Sbrakmo #include <bpf/bpf.h> 45a1270fe9Sbrakmo 46a1270fe9Sbrakmo #include "bpf_load.h" 47a1270fe9Sbrakmo #include "bpf_rlimit.h" 48a1270fe9Sbrakmo #include "cgroup_helpers.h" 49a1270fe9Sbrakmo #include "hbm.h" 50a1270fe9Sbrakmo #include "bpf_util.h" 51a1270fe9Sbrakmo #include "bpf/bpf.h" 52a1270fe9Sbrakmo #include "bpf/libbpf.h" 53a1270fe9Sbrakmo 54a1270fe9Sbrakmo bool outFlag = true; 55a1270fe9Sbrakmo int minRate = 1000; /* cgroup rate limit in Mbps */ 56a1270fe9Sbrakmo int rate = 1000; /* can grow if rate conserving is enabled */ 57a1270fe9Sbrakmo int dur = 1; 58a1270fe9Sbrakmo bool stats_flag; 59a1270fe9Sbrakmo bool loopback_flag; 60a1270fe9Sbrakmo bool debugFlag; 61a1270fe9Sbrakmo bool work_conserving_flag; 62a1270fe9Sbrakmo 63a1270fe9Sbrakmo static void Usage(void); 64a1270fe9Sbrakmo static void read_trace_pipe2(void); 65a1270fe9Sbrakmo static void do_error(char *msg, bool errno_flag); 66a1270fe9Sbrakmo 67a1270fe9Sbrakmo #define DEBUGFS "/sys/kernel/debug/tracing/" 68a1270fe9Sbrakmo 69a1270fe9Sbrakmo struct bpf_object *obj; 70a1270fe9Sbrakmo int bpfprog_fd; 71a1270fe9Sbrakmo int cgroup_storage_fd; 72a1270fe9Sbrakmo 73a1270fe9Sbrakmo static void read_trace_pipe2(void) 74a1270fe9Sbrakmo { 75a1270fe9Sbrakmo int trace_fd; 76a1270fe9Sbrakmo FILE *outf; 77a1270fe9Sbrakmo char *outFname = "hbm_out.log"; 78a1270fe9Sbrakmo 79a1270fe9Sbrakmo trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); 80a1270fe9Sbrakmo if (trace_fd < 0) { 81a1270fe9Sbrakmo printf("Error opening trace_pipe\n"); 82a1270fe9Sbrakmo return; 83a1270fe9Sbrakmo } 84a1270fe9Sbrakmo 85a1270fe9Sbrakmo // Future support of ingress 86a1270fe9Sbrakmo // if (!outFlag) 87a1270fe9Sbrakmo // outFname = "hbm_in.log"; 88a1270fe9Sbrakmo outf = fopen(outFname, "w"); 89a1270fe9Sbrakmo 90a1270fe9Sbrakmo if (outf == NULL) 91a1270fe9Sbrakmo printf("Error creating %s\n", outFname); 92a1270fe9Sbrakmo 93a1270fe9Sbrakmo while (1) { 94a1270fe9Sbrakmo static char buf[4097]; 95a1270fe9Sbrakmo ssize_t sz; 96a1270fe9Sbrakmo 97a1270fe9Sbrakmo sz = read(trace_fd, buf, sizeof(buf) - 1); 98a1270fe9Sbrakmo if (sz > 0) { 99a1270fe9Sbrakmo buf[sz] = 0; 100a1270fe9Sbrakmo puts(buf); 101a1270fe9Sbrakmo if (outf != NULL) { 102a1270fe9Sbrakmo fprintf(outf, "%s\n", buf); 103a1270fe9Sbrakmo fflush(outf); 104a1270fe9Sbrakmo } 105a1270fe9Sbrakmo } 106a1270fe9Sbrakmo } 107a1270fe9Sbrakmo } 108a1270fe9Sbrakmo 109a1270fe9Sbrakmo static void do_error(char *msg, bool errno_flag) 110a1270fe9Sbrakmo { 111a1270fe9Sbrakmo if (errno_flag) 112a1270fe9Sbrakmo printf("ERROR: %s, errno: %d\n", msg, errno); 113a1270fe9Sbrakmo else 114a1270fe9Sbrakmo printf("ERROR: %s\n", msg); 115a1270fe9Sbrakmo exit(1); 116a1270fe9Sbrakmo } 117a1270fe9Sbrakmo 118a1270fe9Sbrakmo static int prog_load(char *prog) 119a1270fe9Sbrakmo { 120a1270fe9Sbrakmo struct bpf_prog_load_attr prog_load_attr = { 121a1270fe9Sbrakmo .prog_type = BPF_PROG_TYPE_CGROUP_SKB, 122a1270fe9Sbrakmo .file = prog, 123a1270fe9Sbrakmo .expected_attach_type = BPF_CGROUP_INET_EGRESS, 124a1270fe9Sbrakmo }; 125a1270fe9Sbrakmo int map_fd; 126a1270fe9Sbrakmo struct bpf_map *map; 127a1270fe9Sbrakmo 128a1270fe9Sbrakmo int ret = 0; 129a1270fe9Sbrakmo 130a1270fe9Sbrakmo if (access(prog, O_RDONLY) < 0) { 131a1270fe9Sbrakmo printf("Error accessing file %s: %s\n", prog, strerror(errno)); 132a1270fe9Sbrakmo return 1; 133a1270fe9Sbrakmo } 134a1270fe9Sbrakmo if (bpf_prog_load_xattr(&prog_load_attr, &obj, &bpfprog_fd)) 135a1270fe9Sbrakmo ret = 1; 136a1270fe9Sbrakmo if (!ret) { 137a1270fe9Sbrakmo map = bpf_object__find_map_by_name(obj, "queue_stats"); 138a1270fe9Sbrakmo map_fd = bpf_map__fd(map); 139a1270fe9Sbrakmo if (map_fd < 0) { 140a1270fe9Sbrakmo printf("Map not found: %s\n", strerror(map_fd)); 141a1270fe9Sbrakmo ret = 1; 142a1270fe9Sbrakmo } 143a1270fe9Sbrakmo } 144a1270fe9Sbrakmo 145a1270fe9Sbrakmo if (ret) { 146a1270fe9Sbrakmo printf("ERROR: load_bpf_file failed for: %s\n", prog); 147a1270fe9Sbrakmo printf(" Output from verifier:\n%s\n------\n", bpf_log_buf); 148a1270fe9Sbrakmo ret = -1; 149a1270fe9Sbrakmo } else { 150a1270fe9Sbrakmo ret = map_fd; 151a1270fe9Sbrakmo } 152a1270fe9Sbrakmo 153a1270fe9Sbrakmo return ret; 154a1270fe9Sbrakmo } 155a1270fe9Sbrakmo 156a1270fe9Sbrakmo static int run_bpf_prog(char *prog, int cg_id) 157a1270fe9Sbrakmo { 158a1270fe9Sbrakmo int map_fd; 159a1270fe9Sbrakmo int rc = 0; 160a1270fe9Sbrakmo int key = 0; 161a1270fe9Sbrakmo int cg1 = 0; 162a1270fe9Sbrakmo int type = BPF_CGROUP_INET_EGRESS; 163a1270fe9Sbrakmo char cg_dir[100]; 164a1270fe9Sbrakmo struct hbm_queue_stats qstats = {0}; 165a1270fe9Sbrakmo 166a1270fe9Sbrakmo sprintf(cg_dir, "/hbm%d", cg_id); 167a1270fe9Sbrakmo map_fd = prog_load(prog); 168a1270fe9Sbrakmo if (map_fd == -1) 169a1270fe9Sbrakmo return 1; 170a1270fe9Sbrakmo 171a1270fe9Sbrakmo if (setup_cgroup_environment()) { 172a1270fe9Sbrakmo printf("ERROR: setting cgroup environment\n"); 173a1270fe9Sbrakmo goto err; 174a1270fe9Sbrakmo } 175a1270fe9Sbrakmo cg1 = create_and_get_cgroup(cg_dir); 176a1270fe9Sbrakmo if (!cg1) { 177a1270fe9Sbrakmo printf("ERROR: create_and_get_cgroup\n"); 178a1270fe9Sbrakmo goto err; 179a1270fe9Sbrakmo } 180a1270fe9Sbrakmo if (join_cgroup(cg_dir)) { 181a1270fe9Sbrakmo printf("ERROR: join_cgroup\n"); 182a1270fe9Sbrakmo goto err; 183a1270fe9Sbrakmo } 184a1270fe9Sbrakmo 185a1270fe9Sbrakmo qstats.rate = rate; 186a1270fe9Sbrakmo qstats.stats = stats_flag ? 1 : 0; 187a1270fe9Sbrakmo qstats.loopback = loopback_flag ? 1 : 0; 188a1270fe9Sbrakmo if (bpf_map_update_elem(map_fd, &key, &qstats, BPF_ANY)) { 189a1270fe9Sbrakmo printf("ERROR: Could not update map element\n"); 190a1270fe9Sbrakmo goto err; 191a1270fe9Sbrakmo } 192a1270fe9Sbrakmo 193a1270fe9Sbrakmo if (!outFlag) 194a1270fe9Sbrakmo type = BPF_CGROUP_INET_INGRESS; 195a1270fe9Sbrakmo if (bpf_prog_attach(bpfprog_fd, cg1, type, 0)) { 196a1270fe9Sbrakmo printf("ERROR: bpf_prog_attach fails!\n"); 197a1270fe9Sbrakmo log_err("Attaching prog"); 198a1270fe9Sbrakmo goto err; 199a1270fe9Sbrakmo } 200a1270fe9Sbrakmo 201a1270fe9Sbrakmo if (work_conserving_flag) { 202a1270fe9Sbrakmo struct timeval t0, t_last, t_new; 203a1270fe9Sbrakmo FILE *fin; 204a1270fe9Sbrakmo unsigned long long last_eth_tx_bytes, new_eth_tx_bytes; 205a1270fe9Sbrakmo signed long long last_cg_tx_bytes, new_cg_tx_bytes; 206a1270fe9Sbrakmo signed long long delta_time, delta_bytes, delta_rate; 207a1270fe9Sbrakmo int delta_ms; 208a1270fe9Sbrakmo #define DELTA_RATE_CHECK 10000 /* in us */ 209a1270fe9Sbrakmo #define RATE_THRESHOLD 9500000000 /* 9.5 Gbps */ 210a1270fe9Sbrakmo 211a1270fe9Sbrakmo bpf_map_lookup_elem(map_fd, &key, &qstats); 212a1270fe9Sbrakmo if (gettimeofday(&t0, NULL) < 0) 213a1270fe9Sbrakmo do_error("gettimeofday failed", true); 214a1270fe9Sbrakmo t_last = t0; 215a1270fe9Sbrakmo fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", "r"); 216a1270fe9Sbrakmo if (fscanf(fin, "%llu", &last_eth_tx_bytes) != 1) 217a1270fe9Sbrakmo do_error("fscanf fails", false); 218a1270fe9Sbrakmo fclose(fin); 219a1270fe9Sbrakmo last_cg_tx_bytes = qstats.bytes_total; 220a1270fe9Sbrakmo while (true) { 221a1270fe9Sbrakmo usleep(DELTA_RATE_CHECK); 222a1270fe9Sbrakmo if (gettimeofday(&t_new, NULL) < 0) 223a1270fe9Sbrakmo do_error("gettimeofday failed", true); 224a1270fe9Sbrakmo delta_ms = (t_new.tv_sec - t0.tv_sec) * 1000 + 225a1270fe9Sbrakmo (t_new.tv_usec - t0.tv_usec)/1000; 226a1270fe9Sbrakmo if (delta_ms > dur * 1000) 227a1270fe9Sbrakmo break; 228a1270fe9Sbrakmo delta_time = (t_new.tv_sec - t_last.tv_sec) * 1000000 + 229a1270fe9Sbrakmo (t_new.tv_usec - t_last.tv_usec); 230a1270fe9Sbrakmo if (delta_time == 0) 231a1270fe9Sbrakmo continue; 232a1270fe9Sbrakmo t_last = t_new; 233a1270fe9Sbrakmo fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", 234a1270fe9Sbrakmo "r"); 235a1270fe9Sbrakmo if (fscanf(fin, "%llu", &new_eth_tx_bytes) != 1) 236a1270fe9Sbrakmo do_error("fscanf fails", false); 237a1270fe9Sbrakmo fclose(fin); 238a1270fe9Sbrakmo printf(" new_eth_tx_bytes:%llu\n", 239a1270fe9Sbrakmo new_eth_tx_bytes); 240a1270fe9Sbrakmo bpf_map_lookup_elem(map_fd, &key, &qstats); 241a1270fe9Sbrakmo new_cg_tx_bytes = qstats.bytes_total; 242a1270fe9Sbrakmo delta_bytes = new_eth_tx_bytes - last_eth_tx_bytes; 243a1270fe9Sbrakmo last_eth_tx_bytes = new_eth_tx_bytes; 244a1270fe9Sbrakmo delta_rate = (delta_bytes * 8000000) / delta_time; 245a1270fe9Sbrakmo printf("%5d - eth_rate:%.1fGbps cg_rate:%.3fGbps", 246a1270fe9Sbrakmo delta_ms, delta_rate/1000000000.0, 247a1270fe9Sbrakmo rate/1000.0); 248a1270fe9Sbrakmo if (delta_rate < RATE_THRESHOLD) { 249a1270fe9Sbrakmo /* can increase cgroup rate limit, but first 250a1270fe9Sbrakmo * check if we are using the current limit. 251a1270fe9Sbrakmo * Currently increasing by 6.25%, unknown 252a1270fe9Sbrakmo * if that is the optimal rate. 253a1270fe9Sbrakmo */ 254a1270fe9Sbrakmo int rate_diff100; 255a1270fe9Sbrakmo 256a1270fe9Sbrakmo delta_bytes = new_cg_tx_bytes - 257a1270fe9Sbrakmo last_cg_tx_bytes; 258a1270fe9Sbrakmo last_cg_tx_bytes = new_cg_tx_bytes; 259a1270fe9Sbrakmo delta_rate = (delta_bytes * 8000000) / 260a1270fe9Sbrakmo delta_time; 261a1270fe9Sbrakmo printf(" rate:%.3fGbps", 262a1270fe9Sbrakmo delta_rate/1000000000.0); 263a1270fe9Sbrakmo rate_diff100 = (((long long)rate)*1000000 - 264a1270fe9Sbrakmo delta_rate) * 100 / 265a1270fe9Sbrakmo (((long long) rate) * 1000000); 266a1270fe9Sbrakmo printf(" rdiff:%d", rate_diff100); 267a1270fe9Sbrakmo if (rate_diff100 <= 3) { 268a1270fe9Sbrakmo rate += (rate >> 4); 269a1270fe9Sbrakmo if (rate > RATE_THRESHOLD / 1000000) 270a1270fe9Sbrakmo rate = RATE_THRESHOLD / 1000000; 271a1270fe9Sbrakmo qstats.rate = rate; 272a1270fe9Sbrakmo printf(" INC\n"); 273a1270fe9Sbrakmo } else { 274a1270fe9Sbrakmo printf("\n"); 275a1270fe9Sbrakmo } 276a1270fe9Sbrakmo } else { 277a1270fe9Sbrakmo /* Need to decrease cgroup rate limit. 278a1270fe9Sbrakmo * Currently decreasing by 12.5%, unknown 279a1270fe9Sbrakmo * if that is optimal 280a1270fe9Sbrakmo */ 281a1270fe9Sbrakmo printf(" DEC\n"); 282a1270fe9Sbrakmo rate -= (rate >> 3); 283a1270fe9Sbrakmo if (rate < minRate) 284a1270fe9Sbrakmo rate = minRate; 285a1270fe9Sbrakmo qstats.rate = rate; 286a1270fe9Sbrakmo } 287a1270fe9Sbrakmo if (bpf_map_update_elem(map_fd, &key, &qstats, BPF_ANY)) 288a1270fe9Sbrakmo do_error("update map element fails", false); 289a1270fe9Sbrakmo } 290a1270fe9Sbrakmo } else { 291a1270fe9Sbrakmo sleep(dur); 292a1270fe9Sbrakmo } 293a1270fe9Sbrakmo // Get stats! 294a1270fe9Sbrakmo if (stats_flag && bpf_map_lookup_elem(map_fd, &key, &qstats)) { 295a1270fe9Sbrakmo char fname[100]; 296a1270fe9Sbrakmo FILE *fout; 297a1270fe9Sbrakmo 298a1270fe9Sbrakmo if (!outFlag) 299a1270fe9Sbrakmo sprintf(fname, "hbm.%d.in", cg_id); 300a1270fe9Sbrakmo else 301a1270fe9Sbrakmo sprintf(fname, "hbm.%d.out", cg_id); 302a1270fe9Sbrakmo fout = fopen(fname, "w"); 303a1270fe9Sbrakmo fprintf(fout, "id:%d\n", cg_id); 304a1270fe9Sbrakmo fprintf(fout, "ERROR: Could not lookup queue_stats\n"); 305a1270fe9Sbrakmo } else if (stats_flag && qstats.lastPacketTime > 306a1270fe9Sbrakmo qstats.firstPacketTime) { 307a1270fe9Sbrakmo long long delta_us = (qstats.lastPacketTime - 308a1270fe9Sbrakmo qstats.firstPacketTime)/1000; 309a1270fe9Sbrakmo unsigned int rate_mbps = ((qstats.bytes_total - 310a1270fe9Sbrakmo qstats.bytes_dropped) * 8 / 311a1270fe9Sbrakmo delta_us); 312a1270fe9Sbrakmo double percent_pkts, percent_bytes; 313a1270fe9Sbrakmo char fname[100]; 314a1270fe9Sbrakmo FILE *fout; 315a1270fe9Sbrakmo 316a1270fe9Sbrakmo // Future support of ingress 317a1270fe9Sbrakmo // if (!outFlag) 318a1270fe9Sbrakmo // sprintf(fname, "hbm.%d.in", cg_id); 319a1270fe9Sbrakmo // else 320a1270fe9Sbrakmo sprintf(fname, "hbm.%d.out", cg_id); 321a1270fe9Sbrakmo fout = fopen(fname, "w"); 322a1270fe9Sbrakmo fprintf(fout, "id:%d\n", cg_id); 323a1270fe9Sbrakmo fprintf(fout, "rate_mbps:%d\n", rate_mbps); 324a1270fe9Sbrakmo fprintf(fout, "duration:%.1f secs\n", 325a1270fe9Sbrakmo (qstats.lastPacketTime - qstats.firstPacketTime) / 326a1270fe9Sbrakmo 1000000000.0); 327a1270fe9Sbrakmo fprintf(fout, "packets:%d\n", (int)qstats.pkts_total); 328a1270fe9Sbrakmo fprintf(fout, "bytes_MB:%d\n", (int)(qstats.bytes_total / 329a1270fe9Sbrakmo 1000000)); 330a1270fe9Sbrakmo fprintf(fout, "pkts_dropped:%d\n", (int)qstats.pkts_dropped); 331a1270fe9Sbrakmo fprintf(fout, "bytes_dropped_MB:%d\n", 332a1270fe9Sbrakmo (int)(qstats.bytes_dropped / 333a1270fe9Sbrakmo 1000000)); 334a1270fe9Sbrakmo // Marked Pkts and Bytes 335a1270fe9Sbrakmo percent_pkts = (qstats.pkts_marked * 100.0) / 336a1270fe9Sbrakmo (qstats.pkts_total + 1); 337a1270fe9Sbrakmo percent_bytes = (qstats.bytes_marked * 100.0) / 338a1270fe9Sbrakmo (qstats.bytes_total + 1); 339a1270fe9Sbrakmo fprintf(fout, "pkts_marked_percent:%6.2f\n", percent_pkts); 340a1270fe9Sbrakmo fprintf(fout, "bytes_marked_percent:%6.2f\n", percent_bytes); 341a1270fe9Sbrakmo 342a1270fe9Sbrakmo // Dropped Pkts and Bytes 343a1270fe9Sbrakmo percent_pkts = (qstats.pkts_dropped * 100.0) / 344a1270fe9Sbrakmo (qstats.pkts_total + 1); 345a1270fe9Sbrakmo percent_bytes = (qstats.bytes_dropped * 100.0) / 346a1270fe9Sbrakmo (qstats.bytes_total + 1); 347a1270fe9Sbrakmo fprintf(fout, "pkts_dropped_percent:%6.2f\n", percent_pkts); 348a1270fe9Sbrakmo fprintf(fout, "bytes_dropped_percent:%6.2f\n", percent_bytes); 349a1270fe9Sbrakmo fclose(fout); 350a1270fe9Sbrakmo } 351a1270fe9Sbrakmo 352a1270fe9Sbrakmo if (debugFlag) 353a1270fe9Sbrakmo read_trace_pipe2(); 354a1270fe9Sbrakmo return rc; 355a1270fe9Sbrakmo err: 356a1270fe9Sbrakmo rc = 1; 357a1270fe9Sbrakmo 358a1270fe9Sbrakmo if (cg1) 359a1270fe9Sbrakmo close(cg1); 360a1270fe9Sbrakmo cleanup_cgroup_environment(); 361a1270fe9Sbrakmo 362a1270fe9Sbrakmo return rc; 363a1270fe9Sbrakmo } 364a1270fe9Sbrakmo 365a1270fe9Sbrakmo static void Usage(void) 366a1270fe9Sbrakmo { 367a1270fe9Sbrakmo printf("This program loads a cgroup skb BPF program to enforce\n" 368a1270fe9Sbrakmo "cgroup output (egress) bandwidth limits.\n\n" 369a1270fe9Sbrakmo "USAGE: hbm [-o] [-d] [-l] [-n <id>] [-r <rate>] [-s]\n" 370a1270fe9Sbrakmo " [-t <secs>] [-w] [-h] [prog]\n" 371a1270fe9Sbrakmo " Where:\n" 372a1270fe9Sbrakmo " -o indicates egress direction (default)\n" 373a1270fe9Sbrakmo " -d print BPF trace debug buffer\n" 374a1270fe9Sbrakmo " -l also limit flows using loopback\n" 375a1270fe9Sbrakmo " -n <#> to create cgroup \"/hbm#\" and attach prog\n" 376a1270fe9Sbrakmo " Default is /hbm1\n" 377a1270fe9Sbrakmo " -r <rate> Rate in Mbps\n" 378a1270fe9Sbrakmo " -s Update HBM stats\n" 3795b4f21b2SColin Ian King " -t <time> Exit after specified seconds (default is 0)\n" 380a1270fe9Sbrakmo " -w Work conserving flag. cgroup can increase\n" 381a1270fe9Sbrakmo " bandwidth beyond the rate limit specified\n" 382a1270fe9Sbrakmo " while there is available bandwidth. Current\n" 383a1270fe9Sbrakmo " implementation assumes there is only eth0\n" 384a1270fe9Sbrakmo " but can be extended to support multiple NICs\n" 385a1270fe9Sbrakmo " -h print this info\n" 386a1270fe9Sbrakmo " prog BPF program file name. Name defaults to\n" 387a1270fe9Sbrakmo " hbm_out_kern.o\n"); 388a1270fe9Sbrakmo } 389a1270fe9Sbrakmo 390a1270fe9Sbrakmo int main(int argc, char **argv) 391a1270fe9Sbrakmo { 392a1270fe9Sbrakmo char *prog = "hbm_out_kern.o"; 393a1270fe9Sbrakmo int k; 394a1270fe9Sbrakmo int cg_id = 1; 395a1270fe9Sbrakmo char *optstring = "iodln:r:st:wh"; 396a1270fe9Sbrakmo 397a1270fe9Sbrakmo while ((k = getopt(argc, argv, optstring)) != -1) { 398a1270fe9Sbrakmo switch (k) { 399a1270fe9Sbrakmo case'o': 400a1270fe9Sbrakmo break; 401a1270fe9Sbrakmo case 'd': 402a1270fe9Sbrakmo debugFlag = true; 403a1270fe9Sbrakmo break; 404a1270fe9Sbrakmo case 'l': 405a1270fe9Sbrakmo loopback_flag = true; 406a1270fe9Sbrakmo break; 407a1270fe9Sbrakmo case 'n': 408a1270fe9Sbrakmo cg_id = atoi(optarg); 409a1270fe9Sbrakmo break; 410a1270fe9Sbrakmo case 'r': 411a1270fe9Sbrakmo minRate = atoi(optarg) * 1.024; 412a1270fe9Sbrakmo rate = minRate; 413a1270fe9Sbrakmo break; 414a1270fe9Sbrakmo case 's': 415a1270fe9Sbrakmo stats_flag = true; 416a1270fe9Sbrakmo break; 417a1270fe9Sbrakmo case 't': 418a1270fe9Sbrakmo dur = atoi(optarg); 419a1270fe9Sbrakmo break; 420a1270fe9Sbrakmo case 'w': 421a1270fe9Sbrakmo work_conserving_flag = true; 422a1270fe9Sbrakmo break; 423a1270fe9Sbrakmo case '?': 424a1270fe9Sbrakmo if (optopt == 'n' || optopt == 'r' || optopt == 't') 425a1270fe9Sbrakmo fprintf(stderr, 426a1270fe9Sbrakmo "Option -%c requires an argument.\n\n", 427a1270fe9Sbrakmo optopt); 428a1270fe9Sbrakmo case 'h': 429a1270fe9Sbrakmo // fallthrough 430a1270fe9Sbrakmo default: 431a1270fe9Sbrakmo Usage(); 432a1270fe9Sbrakmo return 0; 433a1270fe9Sbrakmo } 434a1270fe9Sbrakmo } 435a1270fe9Sbrakmo 436a1270fe9Sbrakmo if (optind < argc) 437a1270fe9Sbrakmo prog = argv[optind]; 438a1270fe9Sbrakmo printf("HBM prog: %s\n", prog != NULL ? prog : "NULL"); 439a1270fe9Sbrakmo 440a1270fe9Sbrakmo return run_bpf_prog(prog, cg_id); 441a1270fe9Sbrakmo } 442