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 19ffd81558Sbrakmo * --no_cn Do not return cn notifications 20a1270fe9Sbrakmo * -r <rate> Rate limit in Mbps 21a1270fe9Sbrakmo * -s Get HBM stats (marked, dropped, etc.) 225b4f21b2SColin Ian King * -t <time> Exit after specified seconds (default is 0) 23a1270fe9Sbrakmo * -w Work conserving flag. cgroup can increase its bandwidth 24a1270fe9Sbrakmo * beyond the rate limit specified while there is available 25a1270fe9Sbrakmo * bandwidth. Current implementation assumes there is only 26a1270fe9Sbrakmo * NIC (eth0), but can be extended to support multiple NICs. 27a1270fe9Sbrakmo * Currrently only supported for egress. 28a1270fe9Sbrakmo * -h Print this info 29a1270fe9Sbrakmo * prog BPF program file name. Name defaults to hbm_out_kern.o 30a1270fe9Sbrakmo */ 31a1270fe9Sbrakmo 32a1270fe9Sbrakmo #define _GNU_SOURCE 33a1270fe9Sbrakmo 34a1270fe9Sbrakmo #include <stdio.h> 35a1270fe9Sbrakmo #include <stdlib.h> 36a1270fe9Sbrakmo #include <assert.h> 37a1270fe9Sbrakmo #include <sys/resource.h> 38a1270fe9Sbrakmo #include <sys/time.h> 39a1270fe9Sbrakmo #include <unistd.h> 40a1270fe9Sbrakmo #include <errno.h> 41a1270fe9Sbrakmo #include <fcntl.h> 42a1270fe9Sbrakmo #include <linux/unistd.h> 43544d6adfSYonghong Song #include <linux/compiler.h> 44a1270fe9Sbrakmo 45a1270fe9Sbrakmo #include <linux/bpf.h> 46a1270fe9Sbrakmo #include <bpf/bpf.h> 47ffd81558Sbrakmo #include <getopt.h> 48a1270fe9Sbrakmo 49a1270fe9Sbrakmo #include "bpf_rlimit.h" 50a1270fe9Sbrakmo #include "cgroup_helpers.h" 51a1270fe9Sbrakmo #include "hbm.h" 52a1270fe9Sbrakmo #include "bpf_util.h" 537cf245a3SToke Høiland-Jørgensen #include <bpf/libbpf.h> 54a1270fe9Sbrakmo 55a1270fe9Sbrakmo bool outFlag = true; 56a1270fe9Sbrakmo int minRate = 1000; /* cgroup rate limit in Mbps */ 57a1270fe9Sbrakmo int rate = 1000; /* can grow if rate conserving is enabled */ 58a1270fe9Sbrakmo int dur = 1; 59a1270fe9Sbrakmo bool stats_flag; 60a1270fe9Sbrakmo bool loopback_flag; 61a1270fe9Sbrakmo bool debugFlag; 62a1270fe9Sbrakmo bool work_conserving_flag; 63ffd81558Sbrakmo bool no_cn_flag; 6471634d7fSbrakmo bool edt_flag; 65a1270fe9Sbrakmo 66a1270fe9Sbrakmo static void Usage(void); 67a1270fe9Sbrakmo static void read_trace_pipe2(void); 68a1270fe9Sbrakmo static void do_error(char *msg, bool errno_flag); 69a1270fe9Sbrakmo 70a1270fe9Sbrakmo #define DEBUGFS "/sys/kernel/debug/tracing/" 71a1270fe9Sbrakmo 72c5815ac7SDaniel T. Lee static struct bpf_program *bpf_prog; 73c5815ac7SDaniel T. Lee static struct bpf_object *obj; 74c5815ac7SDaniel T. Lee static int queue_stats_fd; 75a1270fe9Sbrakmo 76a1270fe9Sbrakmo static void read_trace_pipe2(void) 77a1270fe9Sbrakmo { 78a1270fe9Sbrakmo int trace_fd; 79a1270fe9Sbrakmo FILE *outf; 80a1270fe9Sbrakmo char *outFname = "hbm_out.log"; 81a1270fe9Sbrakmo 82a1270fe9Sbrakmo trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); 83a1270fe9Sbrakmo if (trace_fd < 0) { 84a1270fe9Sbrakmo printf("Error opening trace_pipe\n"); 85a1270fe9Sbrakmo return; 86a1270fe9Sbrakmo } 87a1270fe9Sbrakmo 88a1270fe9Sbrakmo // Future support of ingress 89a1270fe9Sbrakmo // if (!outFlag) 90a1270fe9Sbrakmo // outFname = "hbm_in.log"; 91a1270fe9Sbrakmo outf = fopen(outFname, "w"); 92a1270fe9Sbrakmo 93a1270fe9Sbrakmo if (outf == NULL) 94a1270fe9Sbrakmo printf("Error creating %s\n", outFname); 95a1270fe9Sbrakmo 96a1270fe9Sbrakmo while (1) { 97a1270fe9Sbrakmo static char buf[4097]; 98a1270fe9Sbrakmo ssize_t sz; 99a1270fe9Sbrakmo 100a1270fe9Sbrakmo sz = read(trace_fd, buf, sizeof(buf) - 1); 101a1270fe9Sbrakmo if (sz > 0) { 102a1270fe9Sbrakmo buf[sz] = 0; 103a1270fe9Sbrakmo puts(buf); 104a1270fe9Sbrakmo if (outf != NULL) { 105a1270fe9Sbrakmo fprintf(outf, "%s\n", buf); 106a1270fe9Sbrakmo fflush(outf); 107a1270fe9Sbrakmo } 108a1270fe9Sbrakmo } 109a1270fe9Sbrakmo } 110a1270fe9Sbrakmo } 111a1270fe9Sbrakmo 112a1270fe9Sbrakmo static void do_error(char *msg, bool errno_flag) 113a1270fe9Sbrakmo { 114a1270fe9Sbrakmo if (errno_flag) 115a1270fe9Sbrakmo printf("ERROR: %s, errno: %d\n", msg, errno); 116a1270fe9Sbrakmo else 117a1270fe9Sbrakmo printf("ERROR: %s\n", msg); 118a1270fe9Sbrakmo exit(1); 119a1270fe9Sbrakmo } 120a1270fe9Sbrakmo 121a1270fe9Sbrakmo static int prog_load(char *prog) 122a1270fe9Sbrakmo { 123*7490d592SKui-Feng Lee struct bpf_program *pos; 124*7490d592SKui-Feng Lee const char *sec_name; 125*7490d592SKui-Feng Lee 126c5815ac7SDaniel T. Lee obj = bpf_object__open_file(prog, NULL); 127c5815ac7SDaniel T. Lee if (libbpf_get_error(obj)) { 128c5815ac7SDaniel T. Lee printf("ERROR: opening BPF object file failed\n"); 129a1270fe9Sbrakmo return 1; 130a1270fe9Sbrakmo } 131c5815ac7SDaniel T. Lee 132c5815ac7SDaniel T. Lee /* load BPF program */ 133c5815ac7SDaniel T. Lee if (bpf_object__load(obj)) { 134c5815ac7SDaniel T. Lee printf("ERROR: loading BPF object file failed\n"); 135c5815ac7SDaniel T. Lee goto err; 136a1270fe9Sbrakmo } 137a1270fe9Sbrakmo 138*7490d592SKui-Feng Lee bpf_object__for_each_program(pos, obj) { 139*7490d592SKui-Feng Lee sec_name = bpf_program__section_name(pos); 140*7490d592SKui-Feng Lee if (sec_name && !strcmp(sec_name, "cgroup_skb/egress")) { 141*7490d592SKui-Feng Lee bpf_prog = pos; 142*7490d592SKui-Feng Lee break; 143*7490d592SKui-Feng Lee } 144*7490d592SKui-Feng Lee } 145c5815ac7SDaniel T. Lee if (!bpf_prog) { 146c5815ac7SDaniel T. Lee printf("ERROR: finding a prog in obj file failed\n"); 147c5815ac7SDaniel T. Lee goto err; 148a1270fe9Sbrakmo } 149a1270fe9Sbrakmo 150c5815ac7SDaniel T. Lee queue_stats_fd = bpf_object__find_map_fd_by_name(obj, "queue_stats"); 151c5815ac7SDaniel T. Lee if (queue_stats_fd < 0) { 152c5815ac7SDaniel T. Lee printf("ERROR: finding a map in obj file failed\n"); 153c5815ac7SDaniel T. Lee goto err; 154c5815ac7SDaniel T. Lee } 155c5815ac7SDaniel T. Lee 156c5815ac7SDaniel T. Lee return 0; 157c5815ac7SDaniel T. Lee 158c5815ac7SDaniel T. Lee err: 159c5815ac7SDaniel T. Lee bpf_object__close(obj); 160c5815ac7SDaniel T. Lee return 1; 161a1270fe9Sbrakmo } 162a1270fe9Sbrakmo 163a1270fe9Sbrakmo static int run_bpf_prog(char *prog, int cg_id) 164a1270fe9Sbrakmo { 165c5815ac7SDaniel T. Lee struct hbm_queue_stats qstats = {0}; 166c5815ac7SDaniel T. Lee char cg_dir[100], cg_pin_path[100]; 167c5815ac7SDaniel T. Lee struct bpf_link *link = NULL; 168a1270fe9Sbrakmo int key = 0; 169a1270fe9Sbrakmo int cg1 = 0; 170c5815ac7SDaniel T. Lee int rc = 0; 171a1270fe9Sbrakmo 172a1270fe9Sbrakmo sprintf(cg_dir, "/hbm%d", cg_id); 173c5815ac7SDaniel T. Lee rc = prog_load(prog); 174c5815ac7SDaniel T. Lee if (rc != 0) 175c5815ac7SDaniel T. Lee return rc; 176a1270fe9Sbrakmo 177a1270fe9Sbrakmo if (setup_cgroup_environment()) { 178a1270fe9Sbrakmo printf("ERROR: setting cgroup environment\n"); 179a1270fe9Sbrakmo goto err; 180a1270fe9Sbrakmo } 181a1270fe9Sbrakmo cg1 = create_and_get_cgroup(cg_dir); 182a1270fe9Sbrakmo if (!cg1) { 183a1270fe9Sbrakmo printf("ERROR: create_and_get_cgroup\n"); 184a1270fe9Sbrakmo goto err; 185a1270fe9Sbrakmo } 186a1270fe9Sbrakmo if (join_cgroup(cg_dir)) { 187a1270fe9Sbrakmo printf("ERROR: join_cgroup\n"); 188a1270fe9Sbrakmo goto err; 189a1270fe9Sbrakmo } 190a1270fe9Sbrakmo 191a1270fe9Sbrakmo qstats.rate = rate; 192a1270fe9Sbrakmo qstats.stats = stats_flag ? 1 : 0; 193a1270fe9Sbrakmo qstats.loopback = loopback_flag ? 1 : 0; 194ffd81558Sbrakmo qstats.no_cn = no_cn_flag ? 1 : 0; 195c5815ac7SDaniel T. Lee if (bpf_map_update_elem(queue_stats_fd, &key, &qstats, BPF_ANY)) { 196a1270fe9Sbrakmo printf("ERROR: Could not update map element\n"); 197a1270fe9Sbrakmo goto err; 198a1270fe9Sbrakmo } 199a1270fe9Sbrakmo 200a1270fe9Sbrakmo if (!outFlag) 201c5815ac7SDaniel T. Lee bpf_program__set_expected_attach_type(bpf_prog, BPF_CGROUP_INET_INGRESS); 202c5815ac7SDaniel T. Lee 203c5815ac7SDaniel T. Lee link = bpf_program__attach_cgroup(bpf_prog, cg1); 204c5815ac7SDaniel T. Lee if (libbpf_get_error(link)) { 205c5815ac7SDaniel T. Lee fprintf(stderr, "ERROR: bpf_program__attach_cgroup failed\n"); 206c5815ac7SDaniel T. Lee goto err; 207c5815ac7SDaniel T. Lee } 208c5815ac7SDaniel T. Lee 209c5815ac7SDaniel T. Lee sprintf(cg_pin_path, "/sys/fs/bpf/hbm%d", cg_id); 210c5815ac7SDaniel T. Lee rc = bpf_link__pin(link, cg_pin_path); 211c5815ac7SDaniel T. Lee if (rc < 0) { 212c5815ac7SDaniel T. Lee printf("ERROR: bpf_link__pin failed: %d\n", rc); 213a1270fe9Sbrakmo goto err; 214a1270fe9Sbrakmo } 215a1270fe9Sbrakmo 216a1270fe9Sbrakmo if (work_conserving_flag) { 217a1270fe9Sbrakmo struct timeval t0, t_last, t_new; 218a1270fe9Sbrakmo FILE *fin; 219a1270fe9Sbrakmo unsigned long long last_eth_tx_bytes, new_eth_tx_bytes; 220a1270fe9Sbrakmo signed long long last_cg_tx_bytes, new_cg_tx_bytes; 221a1270fe9Sbrakmo signed long long delta_time, delta_bytes, delta_rate; 222a1270fe9Sbrakmo int delta_ms; 223a1270fe9Sbrakmo #define DELTA_RATE_CHECK 10000 /* in us */ 224a1270fe9Sbrakmo #define RATE_THRESHOLD 9500000000 /* 9.5 Gbps */ 225a1270fe9Sbrakmo 226c5815ac7SDaniel T. Lee bpf_map_lookup_elem(queue_stats_fd, &key, &qstats); 227a1270fe9Sbrakmo if (gettimeofday(&t0, NULL) < 0) 228a1270fe9Sbrakmo do_error("gettimeofday failed", true); 229a1270fe9Sbrakmo t_last = t0; 230a1270fe9Sbrakmo fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", "r"); 231a1270fe9Sbrakmo if (fscanf(fin, "%llu", &last_eth_tx_bytes) != 1) 232a1270fe9Sbrakmo do_error("fscanf fails", false); 233a1270fe9Sbrakmo fclose(fin); 234a1270fe9Sbrakmo last_cg_tx_bytes = qstats.bytes_total; 235a1270fe9Sbrakmo while (true) { 236a1270fe9Sbrakmo usleep(DELTA_RATE_CHECK); 237a1270fe9Sbrakmo if (gettimeofday(&t_new, NULL) < 0) 238a1270fe9Sbrakmo do_error("gettimeofday failed", true); 239a1270fe9Sbrakmo delta_ms = (t_new.tv_sec - t0.tv_sec) * 1000 + 240a1270fe9Sbrakmo (t_new.tv_usec - t0.tv_usec)/1000; 241a1270fe9Sbrakmo if (delta_ms > dur * 1000) 242a1270fe9Sbrakmo break; 243a1270fe9Sbrakmo delta_time = (t_new.tv_sec - t_last.tv_sec) * 1000000 + 244a1270fe9Sbrakmo (t_new.tv_usec - t_last.tv_usec); 245a1270fe9Sbrakmo if (delta_time == 0) 246a1270fe9Sbrakmo continue; 247a1270fe9Sbrakmo t_last = t_new; 248a1270fe9Sbrakmo fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", 249a1270fe9Sbrakmo "r"); 250a1270fe9Sbrakmo if (fscanf(fin, "%llu", &new_eth_tx_bytes) != 1) 251a1270fe9Sbrakmo do_error("fscanf fails", false); 252a1270fe9Sbrakmo fclose(fin); 253a1270fe9Sbrakmo printf(" new_eth_tx_bytes:%llu\n", 254a1270fe9Sbrakmo new_eth_tx_bytes); 255c5815ac7SDaniel T. Lee bpf_map_lookup_elem(queue_stats_fd, &key, &qstats); 256a1270fe9Sbrakmo new_cg_tx_bytes = qstats.bytes_total; 257a1270fe9Sbrakmo delta_bytes = new_eth_tx_bytes - last_eth_tx_bytes; 258a1270fe9Sbrakmo last_eth_tx_bytes = new_eth_tx_bytes; 259a1270fe9Sbrakmo delta_rate = (delta_bytes * 8000000) / delta_time; 260a1270fe9Sbrakmo printf("%5d - eth_rate:%.1fGbps cg_rate:%.3fGbps", 261a1270fe9Sbrakmo delta_ms, delta_rate/1000000000.0, 262a1270fe9Sbrakmo rate/1000.0); 263a1270fe9Sbrakmo if (delta_rate < RATE_THRESHOLD) { 264a1270fe9Sbrakmo /* can increase cgroup rate limit, but first 265a1270fe9Sbrakmo * check if we are using the current limit. 266a1270fe9Sbrakmo * Currently increasing by 6.25%, unknown 267a1270fe9Sbrakmo * if that is the optimal rate. 268a1270fe9Sbrakmo */ 269a1270fe9Sbrakmo int rate_diff100; 270a1270fe9Sbrakmo 271a1270fe9Sbrakmo delta_bytes = new_cg_tx_bytes - 272a1270fe9Sbrakmo last_cg_tx_bytes; 273a1270fe9Sbrakmo last_cg_tx_bytes = new_cg_tx_bytes; 274a1270fe9Sbrakmo delta_rate = (delta_bytes * 8000000) / 275a1270fe9Sbrakmo delta_time; 276a1270fe9Sbrakmo printf(" rate:%.3fGbps", 277a1270fe9Sbrakmo delta_rate/1000000000.0); 278a1270fe9Sbrakmo rate_diff100 = (((long long)rate)*1000000 - 279a1270fe9Sbrakmo delta_rate) * 100 / 280a1270fe9Sbrakmo (((long long) rate) * 1000000); 281a1270fe9Sbrakmo printf(" rdiff:%d", rate_diff100); 282a1270fe9Sbrakmo if (rate_diff100 <= 3) { 283a1270fe9Sbrakmo rate += (rate >> 4); 284a1270fe9Sbrakmo if (rate > RATE_THRESHOLD / 1000000) 285a1270fe9Sbrakmo rate = RATE_THRESHOLD / 1000000; 286a1270fe9Sbrakmo qstats.rate = rate; 287a1270fe9Sbrakmo printf(" INC\n"); 288a1270fe9Sbrakmo } else { 289a1270fe9Sbrakmo printf("\n"); 290a1270fe9Sbrakmo } 291a1270fe9Sbrakmo } else { 292a1270fe9Sbrakmo /* Need to decrease cgroup rate limit. 293a1270fe9Sbrakmo * Currently decreasing by 12.5%, unknown 294a1270fe9Sbrakmo * if that is optimal 295a1270fe9Sbrakmo */ 296a1270fe9Sbrakmo printf(" DEC\n"); 297a1270fe9Sbrakmo rate -= (rate >> 3); 298a1270fe9Sbrakmo if (rate < minRate) 299a1270fe9Sbrakmo rate = minRate; 300a1270fe9Sbrakmo qstats.rate = rate; 301a1270fe9Sbrakmo } 302c5815ac7SDaniel T. Lee if (bpf_map_update_elem(queue_stats_fd, &key, &qstats, BPF_ANY)) 303a1270fe9Sbrakmo do_error("update map element fails", false); 304a1270fe9Sbrakmo } 305a1270fe9Sbrakmo } else { 306a1270fe9Sbrakmo sleep(dur); 307a1270fe9Sbrakmo } 308a1270fe9Sbrakmo // Get stats! 309c5815ac7SDaniel T. Lee if (stats_flag && bpf_map_lookup_elem(queue_stats_fd, &key, &qstats)) { 310a1270fe9Sbrakmo char fname[100]; 311a1270fe9Sbrakmo FILE *fout; 312a1270fe9Sbrakmo 313a1270fe9Sbrakmo if (!outFlag) 314a1270fe9Sbrakmo sprintf(fname, "hbm.%d.in", cg_id); 315a1270fe9Sbrakmo else 316a1270fe9Sbrakmo sprintf(fname, "hbm.%d.out", cg_id); 317a1270fe9Sbrakmo fout = fopen(fname, "w"); 318a1270fe9Sbrakmo fprintf(fout, "id:%d\n", cg_id); 319a1270fe9Sbrakmo fprintf(fout, "ERROR: Could not lookup queue_stats\n"); 320a1270fe9Sbrakmo } else if (stats_flag && qstats.lastPacketTime > 321a1270fe9Sbrakmo qstats.firstPacketTime) { 322a1270fe9Sbrakmo long long delta_us = (qstats.lastPacketTime - 323a1270fe9Sbrakmo qstats.firstPacketTime)/1000; 324a1270fe9Sbrakmo unsigned int rate_mbps = ((qstats.bytes_total - 325a1270fe9Sbrakmo qstats.bytes_dropped) * 8 / 326a1270fe9Sbrakmo delta_us); 327a1270fe9Sbrakmo double percent_pkts, percent_bytes; 328a1270fe9Sbrakmo char fname[100]; 329a1270fe9Sbrakmo FILE *fout; 330d58c6f72Sbrakmo int k; 331d58c6f72Sbrakmo static const char *returnValNames[] = { 332d58c6f72Sbrakmo "DROP_PKT", 333d58c6f72Sbrakmo "ALLOW_PKT", 334d58c6f72Sbrakmo "DROP_PKT_CWR", 335d58c6f72Sbrakmo "ALLOW_PKT_CWR" 336d58c6f72Sbrakmo }; 337d58c6f72Sbrakmo #define RET_VAL_COUNT 4 338a1270fe9Sbrakmo 339a1270fe9Sbrakmo // Future support of ingress 340a1270fe9Sbrakmo // if (!outFlag) 341a1270fe9Sbrakmo // sprintf(fname, "hbm.%d.in", cg_id); 342a1270fe9Sbrakmo // else 343a1270fe9Sbrakmo sprintf(fname, "hbm.%d.out", cg_id); 344a1270fe9Sbrakmo fout = fopen(fname, "w"); 345a1270fe9Sbrakmo fprintf(fout, "id:%d\n", cg_id); 346a1270fe9Sbrakmo fprintf(fout, "rate_mbps:%d\n", rate_mbps); 347a1270fe9Sbrakmo fprintf(fout, "duration:%.1f secs\n", 348a1270fe9Sbrakmo (qstats.lastPacketTime - qstats.firstPacketTime) / 349a1270fe9Sbrakmo 1000000000.0); 350a1270fe9Sbrakmo fprintf(fout, "packets:%d\n", (int)qstats.pkts_total); 351a1270fe9Sbrakmo fprintf(fout, "bytes_MB:%d\n", (int)(qstats.bytes_total / 352a1270fe9Sbrakmo 1000000)); 353a1270fe9Sbrakmo fprintf(fout, "pkts_dropped:%d\n", (int)qstats.pkts_dropped); 354a1270fe9Sbrakmo fprintf(fout, "bytes_dropped_MB:%d\n", 355a1270fe9Sbrakmo (int)(qstats.bytes_dropped / 356a1270fe9Sbrakmo 1000000)); 357a1270fe9Sbrakmo // Marked Pkts and Bytes 358a1270fe9Sbrakmo percent_pkts = (qstats.pkts_marked * 100.0) / 359a1270fe9Sbrakmo (qstats.pkts_total + 1); 360a1270fe9Sbrakmo percent_bytes = (qstats.bytes_marked * 100.0) / 361a1270fe9Sbrakmo (qstats.bytes_total + 1); 362a1270fe9Sbrakmo fprintf(fout, "pkts_marked_percent:%6.2f\n", percent_pkts); 363a1270fe9Sbrakmo fprintf(fout, "bytes_marked_percent:%6.2f\n", percent_bytes); 364a1270fe9Sbrakmo 365a1270fe9Sbrakmo // Dropped Pkts and Bytes 366a1270fe9Sbrakmo percent_pkts = (qstats.pkts_dropped * 100.0) / 367a1270fe9Sbrakmo (qstats.pkts_total + 1); 368a1270fe9Sbrakmo percent_bytes = (qstats.bytes_dropped * 100.0) / 369a1270fe9Sbrakmo (qstats.bytes_total + 1); 370a1270fe9Sbrakmo fprintf(fout, "pkts_dropped_percent:%6.2f\n", percent_pkts); 371a1270fe9Sbrakmo fprintf(fout, "bytes_dropped_percent:%6.2f\n", percent_bytes); 372d58c6f72Sbrakmo 373d58c6f72Sbrakmo // ECN CE markings 374d58c6f72Sbrakmo percent_pkts = (qstats.pkts_ecn_ce * 100.0) / 375d58c6f72Sbrakmo (qstats.pkts_total + 1); 376d58c6f72Sbrakmo fprintf(fout, "pkts_ecn_ce:%6.2f (%d)\n", percent_pkts, 377d58c6f72Sbrakmo (int)qstats.pkts_ecn_ce); 378d58c6f72Sbrakmo 379d58c6f72Sbrakmo // Average cwnd 380d58c6f72Sbrakmo fprintf(fout, "avg cwnd:%d\n", 381d58c6f72Sbrakmo (int)(qstats.sum_cwnd / (qstats.sum_cwnd_cnt + 1))); 382d58c6f72Sbrakmo // Average rtt 383d58c6f72Sbrakmo fprintf(fout, "avg rtt:%d\n", 384d58c6f72Sbrakmo (int)(qstats.sum_rtt / (qstats.pkts_total + 1))); 385d58c6f72Sbrakmo // Average credit 38671634d7fSbrakmo if (edt_flag) 38771634d7fSbrakmo fprintf(fout, "avg credit_ms:%.03f\n", 38871634d7fSbrakmo (qstats.sum_credit / 38971634d7fSbrakmo (qstats.pkts_total + 1.0)) / 1000000.0); 39071634d7fSbrakmo else 391d58c6f72Sbrakmo fprintf(fout, "avg credit:%d\n", 392d58c6f72Sbrakmo (int)(qstats.sum_credit / 393d58c6f72Sbrakmo (1500 * ((int)qstats.pkts_total ) + 1))); 394d58c6f72Sbrakmo 395d58c6f72Sbrakmo // Return values stats 396d58c6f72Sbrakmo for (k = 0; k < RET_VAL_COUNT; k++) { 397d58c6f72Sbrakmo percent_pkts = (qstats.returnValCount[k] * 100.0) / 398d58c6f72Sbrakmo (qstats.pkts_total + 1); 399d58c6f72Sbrakmo fprintf(fout, "%s:%6.2f (%d)\n", returnValNames[k], 400d58c6f72Sbrakmo percent_pkts, (int)qstats.returnValCount[k]); 401d58c6f72Sbrakmo } 402a1270fe9Sbrakmo fclose(fout); 403a1270fe9Sbrakmo } 404a1270fe9Sbrakmo 405a1270fe9Sbrakmo if (debugFlag) 406a1270fe9Sbrakmo read_trace_pipe2(); 407c5815ac7SDaniel T. Lee goto cleanup; 408c5815ac7SDaniel T. Lee 409a1270fe9Sbrakmo err: 410a1270fe9Sbrakmo rc = 1; 411a1270fe9Sbrakmo 412c5815ac7SDaniel T. Lee cleanup: 413c5815ac7SDaniel T. Lee bpf_link__destroy(link); 414c5815ac7SDaniel T. Lee bpf_object__close(obj); 415a1270fe9Sbrakmo 416c5815ac7SDaniel T. Lee if (cg1 != -1) 417c5815ac7SDaniel T. Lee close(cg1); 418c5815ac7SDaniel T. Lee 419c5815ac7SDaniel T. Lee if (rc != 0) 420c5815ac7SDaniel T. Lee cleanup_cgroup_environment(); 421a1270fe9Sbrakmo return rc; 422a1270fe9Sbrakmo } 423a1270fe9Sbrakmo 424a1270fe9Sbrakmo static void Usage(void) 425a1270fe9Sbrakmo { 426a1270fe9Sbrakmo printf("This program loads a cgroup skb BPF program to enforce\n" 427a1270fe9Sbrakmo "cgroup output (egress) bandwidth limits.\n\n" 428ffd81558Sbrakmo "USAGE: hbm [-o] [-d] [-l] [-n <id>] [--no_cn] [-r <rate>]\n" 429ffd81558Sbrakmo " [-s] [-t <secs>] [-w] [-h] [prog]\n" 430a1270fe9Sbrakmo " Where:\n" 431a1270fe9Sbrakmo " -o indicates egress direction (default)\n" 432a1270fe9Sbrakmo " -d print BPF trace debug buffer\n" 43371634d7fSbrakmo " --edt use fq's Earliest Departure Time\n" 434a1270fe9Sbrakmo " -l also limit flows using loopback\n" 435a1270fe9Sbrakmo " -n <#> to create cgroup \"/hbm#\" and attach prog\n" 436a1270fe9Sbrakmo " Default is /hbm1\n" 4372ed99339SColin Ian King " --no_cn disable CN notifications\n" 438a1270fe9Sbrakmo " -r <rate> Rate in Mbps\n" 439a1270fe9Sbrakmo " -s Update HBM stats\n" 4405b4f21b2SColin Ian King " -t <time> Exit after specified seconds (default is 0)\n" 441a1270fe9Sbrakmo " -w Work conserving flag. cgroup can increase\n" 442a1270fe9Sbrakmo " bandwidth beyond the rate limit specified\n" 443a1270fe9Sbrakmo " while there is available bandwidth. Current\n" 444a1270fe9Sbrakmo " implementation assumes there is only eth0\n" 445a1270fe9Sbrakmo " but can be extended to support multiple NICs\n" 446a1270fe9Sbrakmo " -h print this info\n" 447a1270fe9Sbrakmo " prog BPF program file name. Name defaults to\n" 448a1270fe9Sbrakmo " hbm_out_kern.o\n"); 449a1270fe9Sbrakmo } 450a1270fe9Sbrakmo 451a1270fe9Sbrakmo int main(int argc, char **argv) 452a1270fe9Sbrakmo { 453a1270fe9Sbrakmo char *prog = "hbm_out_kern.o"; 454a1270fe9Sbrakmo int k; 455a1270fe9Sbrakmo int cg_id = 1; 456a1270fe9Sbrakmo char *optstring = "iodln:r:st:wh"; 457ffd81558Sbrakmo struct option loptions[] = { 458ffd81558Sbrakmo {"no_cn", 0, NULL, 1}, 45971634d7fSbrakmo {"edt", 0, NULL, 2}, 460ffd81558Sbrakmo {NULL, 0, NULL, 0} 461ffd81558Sbrakmo }; 462a1270fe9Sbrakmo 463ffd81558Sbrakmo while ((k = getopt_long(argc, argv, optstring, loptions, NULL)) != -1) { 464a1270fe9Sbrakmo switch (k) { 465ffd81558Sbrakmo case 1: 466ffd81558Sbrakmo no_cn_flag = true; 467ffd81558Sbrakmo break; 46871634d7fSbrakmo case 2: 46971634d7fSbrakmo prog = "hbm_edt_kern.o"; 47071634d7fSbrakmo edt_flag = true; 47171634d7fSbrakmo break; 472a1270fe9Sbrakmo case'o': 473a1270fe9Sbrakmo break; 474a1270fe9Sbrakmo case 'd': 475a1270fe9Sbrakmo debugFlag = true; 476a1270fe9Sbrakmo break; 477a1270fe9Sbrakmo case 'l': 478a1270fe9Sbrakmo loopback_flag = true; 479a1270fe9Sbrakmo break; 480a1270fe9Sbrakmo case 'n': 481a1270fe9Sbrakmo cg_id = atoi(optarg); 482a1270fe9Sbrakmo break; 483a1270fe9Sbrakmo case 'r': 484a1270fe9Sbrakmo minRate = atoi(optarg) * 1.024; 485a1270fe9Sbrakmo rate = minRate; 486a1270fe9Sbrakmo break; 487a1270fe9Sbrakmo case 's': 488a1270fe9Sbrakmo stats_flag = true; 489a1270fe9Sbrakmo break; 490a1270fe9Sbrakmo case 't': 491a1270fe9Sbrakmo dur = atoi(optarg); 492a1270fe9Sbrakmo break; 493a1270fe9Sbrakmo case 'w': 494a1270fe9Sbrakmo work_conserving_flag = true; 495a1270fe9Sbrakmo break; 496a1270fe9Sbrakmo case '?': 497a1270fe9Sbrakmo if (optopt == 'n' || optopt == 'r' || optopt == 't') 498a1270fe9Sbrakmo fprintf(stderr, 499a1270fe9Sbrakmo "Option -%c requires an argument.\n\n", 500a1270fe9Sbrakmo optopt); 501a1270fe9Sbrakmo case 'h': 502544d6adfSYonghong Song __fallthrough; 503a1270fe9Sbrakmo default: 504a1270fe9Sbrakmo Usage(); 505a1270fe9Sbrakmo return 0; 506a1270fe9Sbrakmo } 507a1270fe9Sbrakmo } 508a1270fe9Sbrakmo 509a1270fe9Sbrakmo if (optind < argc) 510a1270fe9Sbrakmo prog = argv[optind]; 511a1270fe9Sbrakmo printf("HBM prog: %s\n", prog != NULL ? prog : "NULL"); 512a1270fe9Sbrakmo 513a1270fe9Sbrakmo return run_bpf_prog(prog, cg_id); 514a1270fe9Sbrakmo } 515