162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019 Facebook 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or 562306a36Sopenharmony_ci * modify it under the terms of version 2 of the GNU General Public 662306a36Sopenharmony_ci * License as published by the Free Software Foundation. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Example program for Host Bandwidth Managment 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This program loads a cgroup skb BPF program to enforce cgroup output 1162306a36Sopenharmony_ci * (egress) or input (ingress) bandwidth limits. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * USAGE: hbm [-d] [-l] [-n <id>] [-r <rate>] [-s] [-t <secs>] [-w] [-h] [prog] 1462306a36Sopenharmony_ci * Where: 1562306a36Sopenharmony_ci * -d Print BPF trace debug buffer 1662306a36Sopenharmony_ci * -l Also limit flows doing loopback 1762306a36Sopenharmony_ci * -n <#> To create cgroup \"/hbm#\" and attach prog 1862306a36Sopenharmony_ci * Default is /hbm1 1962306a36Sopenharmony_ci * --no_cn Do not return cn notifications 2062306a36Sopenharmony_ci * -r <rate> Rate limit in Mbps 2162306a36Sopenharmony_ci * -s Get HBM stats (marked, dropped, etc.) 2262306a36Sopenharmony_ci * -t <time> Exit after specified seconds (default is 0) 2362306a36Sopenharmony_ci * -w Work conserving flag. cgroup can increase its bandwidth 2462306a36Sopenharmony_ci * beyond the rate limit specified while there is available 2562306a36Sopenharmony_ci * bandwidth. Current implementation assumes there is only 2662306a36Sopenharmony_ci * NIC (eth0), but can be extended to support multiple NICs. 2762306a36Sopenharmony_ci * Currrently only supported for egress. 2862306a36Sopenharmony_ci * -h Print this info 2962306a36Sopenharmony_ci * prog BPF program file name. Name defaults to hbm_out_kern.o 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define _GNU_SOURCE 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <stdio.h> 3562306a36Sopenharmony_ci#include <stdlib.h> 3662306a36Sopenharmony_ci#include <assert.h> 3762306a36Sopenharmony_ci#include <sys/time.h> 3862306a36Sopenharmony_ci#include <unistd.h> 3962306a36Sopenharmony_ci#include <errno.h> 4062306a36Sopenharmony_ci#include <fcntl.h> 4162306a36Sopenharmony_ci#include <linux/unistd.h> 4262306a36Sopenharmony_ci#include <linux/compiler.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <linux/bpf.h> 4562306a36Sopenharmony_ci#include <bpf/bpf.h> 4662306a36Sopenharmony_ci#include <getopt.h> 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include "cgroup_helpers.h" 4962306a36Sopenharmony_ci#include "hbm.h" 5062306a36Sopenharmony_ci#include "bpf_util.h" 5162306a36Sopenharmony_ci#include <bpf/libbpf.h> 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cibool outFlag = true; 5462306a36Sopenharmony_ciint minRate = 1000; /* cgroup rate limit in Mbps */ 5562306a36Sopenharmony_ciint rate = 1000; /* can grow if rate conserving is enabled */ 5662306a36Sopenharmony_ciint dur = 1; 5762306a36Sopenharmony_cibool stats_flag; 5862306a36Sopenharmony_cibool loopback_flag; 5962306a36Sopenharmony_cibool debugFlag; 6062306a36Sopenharmony_cibool work_conserving_flag; 6162306a36Sopenharmony_cibool no_cn_flag; 6262306a36Sopenharmony_cibool edt_flag; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void Usage(void); 6562306a36Sopenharmony_cistatic void read_trace_pipe2(void); 6662306a36Sopenharmony_cistatic void do_error(char *msg, bool errno_flag); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define TRACEFS "/sys/kernel/tracing/" 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct bpf_program *bpf_prog; 7162306a36Sopenharmony_cistatic struct bpf_object *obj; 7262306a36Sopenharmony_cistatic int queue_stats_fd; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void read_trace_pipe2(void) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int trace_fd; 7762306a36Sopenharmony_ci FILE *outf; 7862306a36Sopenharmony_ci char *outFname = "hbm_out.log"; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci trace_fd = open(TRACEFS "trace_pipe", O_RDONLY, 0); 8162306a36Sopenharmony_ci if (trace_fd < 0) { 8262306a36Sopenharmony_ci printf("Error opening trace_pipe\n"); 8362306a36Sopenharmony_ci return; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci// Future support of ingress 8762306a36Sopenharmony_ci// if (!outFlag) 8862306a36Sopenharmony_ci// outFname = "hbm_in.log"; 8962306a36Sopenharmony_ci outf = fopen(outFname, "w"); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (outf == NULL) 9262306a36Sopenharmony_ci printf("Error creating %s\n", outFname); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci while (1) { 9562306a36Sopenharmony_ci static char buf[4097]; 9662306a36Sopenharmony_ci ssize_t sz; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci sz = read(trace_fd, buf, sizeof(buf) - 1); 9962306a36Sopenharmony_ci if (sz > 0) { 10062306a36Sopenharmony_ci buf[sz] = 0; 10162306a36Sopenharmony_ci puts(buf); 10262306a36Sopenharmony_ci if (outf != NULL) { 10362306a36Sopenharmony_ci fprintf(outf, "%s\n", buf); 10462306a36Sopenharmony_ci fflush(outf); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void do_error(char *msg, bool errno_flag) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci if (errno_flag) 11362306a36Sopenharmony_ci printf("ERROR: %s, errno: %d\n", msg, errno); 11462306a36Sopenharmony_ci else 11562306a36Sopenharmony_ci printf("ERROR: %s\n", msg); 11662306a36Sopenharmony_ci exit(1); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int prog_load(char *prog) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct bpf_program *pos; 12262306a36Sopenharmony_ci const char *sec_name; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci obj = bpf_object__open_file(prog, NULL); 12562306a36Sopenharmony_ci if (libbpf_get_error(obj)) { 12662306a36Sopenharmony_ci printf("ERROR: opening BPF object file failed\n"); 12762306a36Sopenharmony_ci return 1; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* load BPF program */ 13162306a36Sopenharmony_ci if (bpf_object__load(obj)) { 13262306a36Sopenharmony_ci printf("ERROR: loading BPF object file failed\n"); 13362306a36Sopenharmony_ci goto err; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci bpf_object__for_each_program(pos, obj) { 13762306a36Sopenharmony_ci sec_name = bpf_program__section_name(pos); 13862306a36Sopenharmony_ci if (sec_name && !strcmp(sec_name, "cgroup_skb/egress")) { 13962306a36Sopenharmony_ci bpf_prog = pos; 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci if (!bpf_prog) { 14462306a36Sopenharmony_ci printf("ERROR: finding a prog in obj file failed\n"); 14562306a36Sopenharmony_ci goto err; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci queue_stats_fd = bpf_object__find_map_fd_by_name(obj, "queue_stats"); 14962306a36Sopenharmony_ci if (queue_stats_fd < 0) { 15062306a36Sopenharmony_ci printf("ERROR: finding a map in obj file failed\n"); 15162306a36Sopenharmony_ci goto err; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cierr: 15762306a36Sopenharmony_ci bpf_object__close(obj); 15862306a36Sopenharmony_ci return 1; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int run_bpf_prog(char *prog, int cg_id) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct hbm_queue_stats qstats = {0}; 16462306a36Sopenharmony_ci char cg_dir[100], cg_pin_path[100]; 16562306a36Sopenharmony_ci struct bpf_link *link = NULL; 16662306a36Sopenharmony_ci int key = 0; 16762306a36Sopenharmony_ci int cg1 = 0; 16862306a36Sopenharmony_ci int rc = 0; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci sprintf(cg_dir, "/hbm%d", cg_id); 17162306a36Sopenharmony_ci rc = prog_load(prog); 17262306a36Sopenharmony_ci if (rc != 0) 17362306a36Sopenharmony_ci return rc; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (setup_cgroup_environment()) { 17662306a36Sopenharmony_ci printf("ERROR: setting cgroup environment\n"); 17762306a36Sopenharmony_ci goto err; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci cg1 = create_and_get_cgroup(cg_dir); 18062306a36Sopenharmony_ci if (!cg1) { 18162306a36Sopenharmony_ci printf("ERROR: create_and_get_cgroup\n"); 18262306a36Sopenharmony_ci goto err; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci if (join_cgroup(cg_dir)) { 18562306a36Sopenharmony_ci printf("ERROR: join_cgroup\n"); 18662306a36Sopenharmony_ci goto err; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci qstats.rate = rate; 19062306a36Sopenharmony_ci qstats.stats = stats_flag ? 1 : 0; 19162306a36Sopenharmony_ci qstats.loopback = loopback_flag ? 1 : 0; 19262306a36Sopenharmony_ci qstats.no_cn = no_cn_flag ? 1 : 0; 19362306a36Sopenharmony_ci if (bpf_map_update_elem(queue_stats_fd, &key, &qstats, BPF_ANY)) { 19462306a36Sopenharmony_ci printf("ERROR: Could not update map element\n"); 19562306a36Sopenharmony_ci goto err; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!outFlag) 19962306a36Sopenharmony_ci bpf_program__set_expected_attach_type(bpf_prog, BPF_CGROUP_INET_INGRESS); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci link = bpf_program__attach_cgroup(bpf_prog, cg1); 20262306a36Sopenharmony_ci if (libbpf_get_error(link)) { 20362306a36Sopenharmony_ci fprintf(stderr, "ERROR: bpf_program__attach_cgroup failed\n"); 20462306a36Sopenharmony_ci goto err; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci sprintf(cg_pin_path, "/sys/fs/bpf/hbm%d", cg_id); 20862306a36Sopenharmony_ci rc = bpf_link__pin(link, cg_pin_path); 20962306a36Sopenharmony_ci if (rc < 0) { 21062306a36Sopenharmony_ci printf("ERROR: bpf_link__pin failed: %d\n", rc); 21162306a36Sopenharmony_ci goto err; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (work_conserving_flag) { 21562306a36Sopenharmony_ci struct timeval t0, t_last, t_new; 21662306a36Sopenharmony_ci FILE *fin; 21762306a36Sopenharmony_ci unsigned long long last_eth_tx_bytes, new_eth_tx_bytes; 21862306a36Sopenharmony_ci signed long long last_cg_tx_bytes, new_cg_tx_bytes; 21962306a36Sopenharmony_ci signed long long delta_time, delta_bytes, delta_rate; 22062306a36Sopenharmony_ci int delta_ms; 22162306a36Sopenharmony_ci#define DELTA_RATE_CHECK 10000 /* in us */ 22262306a36Sopenharmony_ci#define RATE_THRESHOLD 9500000000 /* 9.5 Gbps */ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci bpf_map_lookup_elem(queue_stats_fd, &key, &qstats); 22562306a36Sopenharmony_ci if (gettimeofday(&t0, NULL) < 0) 22662306a36Sopenharmony_ci do_error("gettimeofday failed", true); 22762306a36Sopenharmony_ci t_last = t0; 22862306a36Sopenharmony_ci fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", "r"); 22962306a36Sopenharmony_ci if (fscanf(fin, "%llu", &last_eth_tx_bytes) != 1) 23062306a36Sopenharmony_ci do_error("fscanf fails", false); 23162306a36Sopenharmony_ci fclose(fin); 23262306a36Sopenharmony_ci last_cg_tx_bytes = qstats.bytes_total; 23362306a36Sopenharmony_ci while (true) { 23462306a36Sopenharmony_ci usleep(DELTA_RATE_CHECK); 23562306a36Sopenharmony_ci if (gettimeofday(&t_new, NULL) < 0) 23662306a36Sopenharmony_ci do_error("gettimeofday failed", true); 23762306a36Sopenharmony_ci delta_ms = (t_new.tv_sec - t0.tv_sec) * 1000 + 23862306a36Sopenharmony_ci (t_new.tv_usec - t0.tv_usec)/1000; 23962306a36Sopenharmony_ci if (delta_ms > dur * 1000) 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci delta_time = (t_new.tv_sec - t_last.tv_sec) * 1000000 + 24262306a36Sopenharmony_ci (t_new.tv_usec - t_last.tv_usec); 24362306a36Sopenharmony_ci if (delta_time == 0) 24462306a36Sopenharmony_ci continue; 24562306a36Sopenharmony_ci t_last = t_new; 24662306a36Sopenharmony_ci fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", 24762306a36Sopenharmony_ci "r"); 24862306a36Sopenharmony_ci if (fscanf(fin, "%llu", &new_eth_tx_bytes) != 1) 24962306a36Sopenharmony_ci do_error("fscanf fails", false); 25062306a36Sopenharmony_ci fclose(fin); 25162306a36Sopenharmony_ci printf(" new_eth_tx_bytes:%llu\n", 25262306a36Sopenharmony_ci new_eth_tx_bytes); 25362306a36Sopenharmony_ci bpf_map_lookup_elem(queue_stats_fd, &key, &qstats); 25462306a36Sopenharmony_ci new_cg_tx_bytes = qstats.bytes_total; 25562306a36Sopenharmony_ci delta_bytes = new_eth_tx_bytes - last_eth_tx_bytes; 25662306a36Sopenharmony_ci last_eth_tx_bytes = new_eth_tx_bytes; 25762306a36Sopenharmony_ci delta_rate = (delta_bytes * 8000000) / delta_time; 25862306a36Sopenharmony_ci printf("%5d - eth_rate:%.1fGbps cg_rate:%.3fGbps", 25962306a36Sopenharmony_ci delta_ms, delta_rate/1000000000.0, 26062306a36Sopenharmony_ci rate/1000.0); 26162306a36Sopenharmony_ci if (delta_rate < RATE_THRESHOLD) { 26262306a36Sopenharmony_ci /* can increase cgroup rate limit, but first 26362306a36Sopenharmony_ci * check if we are using the current limit. 26462306a36Sopenharmony_ci * Currently increasing by 6.25%, unknown 26562306a36Sopenharmony_ci * if that is the optimal rate. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci int rate_diff100; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci delta_bytes = new_cg_tx_bytes - 27062306a36Sopenharmony_ci last_cg_tx_bytes; 27162306a36Sopenharmony_ci last_cg_tx_bytes = new_cg_tx_bytes; 27262306a36Sopenharmony_ci delta_rate = (delta_bytes * 8000000) / 27362306a36Sopenharmony_ci delta_time; 27462306a36Sopenharmony_ci printf(" rate:%.3fGbps", 27562306a36Sopenharmony_ci delta_rate/1000000000.0); 27662306a36Sopenharmony_ci rate_diff100 = (((long long)rate)*1000000 - 27762306a36Sopenharmony_ci delta_rate) * 100 / 27862306a36Sopenharmony_ci (((long long) rate) * 1000000); 27962306a36Sopenharmony_ci printf(" rdiff:%d", rate_diff100); 28062306a36Sopenharmony_ci if (rate_diff100 <= 3) { 28162306a36Sopenharmony_ci rate += (rate >> 4); 28262306a36Sopenharmony_ci if (rate > RATE_THRESHOLD / 1000000) 28362306a36Sopenharmony_ci rate = RATE_THRESHOLD / 1000000; 28462306a36Sopenharmony_ci qstats.rate = rate; 28562306a36Sopenharmony_ci printf(" INC\n"); 28662306a36Sopenharmony_ci } else { 28762306a36Sopenharmony_ci printf("\n"); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci /* Need to decrease cgroup rate limit. 29162306a36Sopenharmony_ci * Currently decreasing by 12.5%, unknown 29262306a36Sopenharmony_ci * if that is optimal 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci printf(" DEC\n"); 29562306a36Sopenharmony_ci rate -= (rate >> 3); 29662306a36Sopenharmony_ci if (rate < minRate) 29762306a36Sopenharmony_ci rate = minRate; 29862306a36Sopenharmony_ci qstats.rate = rate; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci if (bpf_map_update_elem(queue_stats_fd, &key, &qstats, BPF_ANY)) 30162306a36Sopenharmony_ci do_error("update map element fails", false); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } else { 30462306a36Sopenharmony_ci sleep(dur); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci // Get stats! 30762306a36Sopenharmony_ci if (stats_flag && bpf_map_lookup_elem(queue_stats_fd, &key, &qstats)) { 30862306a36Sopenharmony_ci char fname[100]; 30962306a36Sopenharmony_ci FILE *fout; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!outFlag) 31262306a36Sopenharmony_ci sprintf(fname, "hbm.%d.in", cg_id); 31362306a36Sopenharmony_ci else 31462306a36Sopenharmony_ci sprintf(fname, "hbm.%d.out", cg_id); 31562306a36Sopenharmony_ci fout = fopen(fname, "w"); 31662306a36Sopenharmony_ci fprintf(fout, "id:%d\n", cg_id); 31762306a36Sopenharmony_ci fprintf(fout, "ERROR: Could not lookup queue_stats\n"); 31862306a36Sopenharmony_ci fclose(fout); 31962306a36Sopenharmony_ci } else if (stats_flag && qstats.lastPacketTime > 32062306a36Sopenharmony_ci qstats.firstPacketTime) { 32162306a36Sopenharmony_ci long long delta_us = (qstats.lastPacketTime - 32262306a36Sopenharmony_ci qstats.firstPacketTime)/1000; 32362306a36Sopenharmony_ci unsigned int rate_mbps = ((qstats.bytes_total - 32462306a36Sopenharmony_ci qstats.bytes_dropped) * 8 / 32562306a36Sopenharmony_ci delta_us); 32662306a36Sopenharmony_ci double percent_pkts, percent_bytes; 32762306a36Sopenharmony_ci char fname[100]; 32862306a36Sopenharmony_ci FILE *fout; 32962306a36Sopenharmony_ci int k; 33062306a36Sopenharmony_ci static const char *returnValNames[] = { 33162306a36Sopenharmony_ci "DROP_PKT", 33262306a36Sopenharmony_ci "ALLOW_PKT", 33362306a36Sopenharmony_ci "DROP_PKT_CWR", 33462306a36Sopenharmony_ci "ALLOW_PKT_CWR" 33562306a36Sopenharmony_ci }; 33662306a36Sopenharmony_ci#define RET_VAL_COUNT 4 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci// Future support of ingress 33962306a36Sopenharmony_ci// if (!outFlag) 34062306a36Sopenharmony_ci// sprintf(fname, "hbm.%d.in", cg_id); 34162306a36Sopenharmony_ci// else 34262306a36Sopenharmony_ci sprintf(fname, "hbm.%d.out", cg_id); 34362306a36Sopenharmony_ci fout = fopen(fname, "w"); 34462306a36Sopenharmony_ci fprintf(fout, "id:%d\n", cg_id); 34562306a36Sopenharmony_ci fprintf(fout, "rate_mbps:%d\n", rate_mbps); 34662306a36Sopenharmony_ci fprintf(fout, "duration:%.1f secs\n", 34762306a36Sopenharmony_ci (qstats.lastPacketTime - qstats.firstPacketTime) / 34862306a36Sopenharmony_ci 1000000000.0); 34962306a36Sopenharmony_ci fprintf(fout, "packets:%d\n", (int)qstats.pkts_total); 35062306a36Sopenharmony_ci fprintf(fout, "bytes_MB:%d\n", (int)(qstats.bytes_total / 35162306a36Sopenharmony_ci 1000000)); 35262306a36Sopenharmony_ci fprintf(fout, "pkts_dropped:%d\n", (int)qstats.pkts_dropped); 35362306a36Sopenharmony_ci fprintf(fout, "bytes_dropped_MB:%d\n", 35462306a36Sopenharmony_ci (int)(qstats.bytes_dropped / 35562306a36Sopenharmony_ci 1000000)); 35662306a36Sopenharmony_ci // Marked Pkts and Bytes 35762306a36Sopenharmony_ci percent_pkts = (qstats.pkts_marked * 100.0) / 35862306a36Sopenharmony_ci (qstats.pkts_total + 1); 35962306a36Sopenharmony_ci percent_bytes = (qstats.bytes_marked * 100.0) / 36062306a36Sopenharmony_ci (qstats.bytes_total + 1); 36162306a36Sopenharmony_ci fprintf(fout, "pkts_marked_percent:%6.2f\n", percent_pkts); 36262306a36Sopenharmony_ci fprintf(fout, "bytes_marked_percent:%6.2f\n", percent_bytes); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci // Dropped Pkts and Bytes 36562306a36Sopenharmony_ci percent_pkts = (qstats.pkts_dropped * 100.0) / 36662306a36Sopenharmony_ci (qstats.pkts_total + 1); 36762306a36Sopenharmony_ci percent_bytes = (qstats.bytes_dropped * 100.0) / 36862306a36Sopenharmony_ci (qstats.bytes_total + 1); 36962306a36Sopenharmony_ci fprintf(fout, "pkts_dropped_percent:%6.2f\n", percent_pkts); 37062306a36Sopenharmony_ci fprintf(fout, "bytes_dropped_percent:%6.2f\n", percent_bytes); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci // ECN CE markings 37362306a36Sopenharmony_ci percent_pkts = (qstats.pkts_ecn_ce * 100.0) / 37462306a36Sopenharmony_ci (qstats.pkts_total + 1); 37562306a36Sopenharmony_ci fprintf(fout, "pkts_ecn_ce:%6.2f (%d)\n", percent_pkts, 37662306a36Sopenharmony_ci (int)qstats.pkts_ecn_ce); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci // Average cwnd 37962306a36Sopenharmony_ci fprintf(fout, "avg cwnd:%d\n", 38062306a36Sopenharmony_ci (int)(qstats.sum_cwnd / (qstats.sum_cwnd_cnt + 1))); 38162306a36Sopenharmony_ci // Average rtt 38262306a36Sopenharmony_ci fprintf(fout, "avg rtt:%d\n", 38362306a36Sopenharmony_ci (int)(qstats.sum_rtt / (qstats.pkts_total + 1))); 38462306a36Sopenharmony_ci // Average credit 38562306a36Sopenharmony_ci if (edt_flag) 38662306a36Sopenharmony_ci fprintf(fout, "avg credit_ms:%.03f\n", 38762306a36Sopenharmony_ci (qstats.sum_credit / 38862306a36Sopenharmony_ci (qstats.pkts_total + 1.0)) / 1000000.0); 38962306a36Sopenharmony_ci else 39062306a36Sopenharmony_ci fprintf(fout, "avg credit:%d\n", 39162306a36Sopenharmony_ci (int)(qstats.sum_credit / 39262306a36Sopenharmony_ci (1500 * ((int)qstats.pkts_total ) + 1))); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci // Return values stats 39562306a36Sopenharmony_ci for (k = 0; k < RET_VAL_COUNT; k++) { 39662306a36Sopenharmony_ci percent_pkts = (qstats.returnValCount[k] * 100.0) / 39762306a36Sopenharmony_ci (qstats.pkts_total + 1); 39862306a36Sopenharmony_ci fprintf(fout, "%s:%6.2f (%d)\n", returnValNames[k], 39962306a36Sopenharmony_ci percent_pkts, (int)qstats.returnValCount[k]); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci fclose(fout); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (debugFlag) 40562306a36Sopenharmony_ci read_trace_pipe2(); 40662306a36Sopenharmony_ci goto cleanup; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cierr: 40962306a36Sopenharmony_ci rc = 1; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cicleanup: 41262306a36Sopenharmony_ci bpf_link__destroy(link); 41362306a36Sopenharmony_ci bpf_object__close(obj); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (cg1 != -1) 41662306a36Sopenharmony_ci close(cg1); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (rc != 0) 41962306a36Sopenharmony_ci cleanup_cgroup_environment(); 42062306a36Sopenharmony_ci return rc; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void Usage(void) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci printf("This program loads a cgroup skb BPF program to enforce\n" 42662306a36Sopenharmony_ci "cgroup output (egress) bandwidth limits.\n\n" 42762306a36Sopenharmony_ci "USAGE: hbm [-o] [-d] [-l] [-n <id>] [--no_cn] [-r <rate>]\n" 42862306a36Sopenharmony_ci " [-s] [-t <secs>] [-w] [-h] [prog]\n" 42962306a36Sopenharmony_ci " Where:\n" 43062306a36Sopenharmony_ci " -o indicates egress direction (default)\n" 43162306a36Sopenharmony_ci " -d print BPF trace debug buffer\n" 43262306a36Sopenharmony_ci " --edt use fq's Earliest Departure Time\n" 43362306a36Sopenharmony_ci " -l also limit flows using loopback\n" 43462306a36Sopenharmony_ci " -n <#> to create cgroup \"/hbm#\" and attach prog\n" 43562306a36Sopenharmony_ci " Default is /hbm1\n" 43662306a36Sopenharmony_ci " --no_cn disable CN notifications\n" 43762306a36Sopenharmony_ci " -r <rate> Rate in Mbps\n" 43862306a36Sopenharmony_ci " -s Update HBM stats\n" 43962306a36Sopenharmony_ci " -t <time> Exit after specified seconds (default is 0)\n" 44062306a36Sopenharmony_ci " -w Work conserving flag. cgroup can increase\n" 44162306a36Sopenharmony_ci " bandwidth beyond the rate limit specified\n" 44262306a36Sopenharmony_ci " while there is available bandwidth. Current\n" 44362306a36Sopenharmony_ci " implementation assumes there is only eth0\n" 44462306a36Sopenharmony_ci " but can be extended to support multiple NICs\n" 44562306a36Sopenharmony_ci " -h print this info\n" 44662306a36Sopenharmony_ci " prog BPF program file name. Name defaults to\n" 44762306a36Sopenharmony_ci " hbm_out_kern.o\n"); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciint main(int argc, char **argv) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci char *prog = "hbm_out_kern.o"; 45362306a36Sopenharmony_ci int k; 45462306a36Sopenharmony_ci int cg_id = 1; 45562306a36Sopenharmony_ci char *optstring = "iodln:r:st:wh"; 45662306a36Sopenharmony_ci struct option loptions[] = { 45762306a36Sopenharmony_ci {"no_cn", 0, NULL, 1}, 45862306a36Sopenharmony_ci {"edt", 0, NULL, 2}, 45962306a36Sopenharmony_ci {NULL, 0, NULL, 0} 46062306a36Sopenharmony_ci }; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci while ((k = getopt_long(argc, argv, optstring, loptions, NULL)) != -1) { 46362306a36Sopenharmony_ci switch (k) { 46462306a36Sopenharmony_ci case 1: 46562306a36Sopenharmony_ci no_cn_flag = true; 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci case 2: 46862306a36Sopenharmony_ci prog = "hbm_edt_kern.o"; 46962306a36Sopenharmony_ci edt_flag = true; 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci case'o': 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case 'd': 47462306a36Sopenharmony_ci debugFlag = true; 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci case 'l': 47762306a36Sopenharmony_ci loopback_flag = true; 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci case 'n': 48062306a36Sopenharmony_ci cg_id = atoi(optarg); 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci case 'r': 48362306a36Sopenharmony_ci minRate = atoi(optarg) * 1.024; 48462306a36Sopenharmony_ci rate = minRate; 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci case 's': 48762306a36Sopenharmony_ci stats_flag = true; 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci case 't': 49062306a36Sopenharmony_ci dur = atoi(optarg); 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci case 'w': 49362306a36Sopenharmony_ci work_conserving_flag = true; 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci case '?': 49662306a36Sopenharmony_ci if (optopt == 'n' || optopt == 'r' || optopt == 't') 49762306a36Sopenharmony_ci fprintf(stderr, 49862306a36Sopenharmony_ci "Option -%c requires an argument.\n\n", 49962306a36Sopenharmony_ci optopt); 50062306a36Sopenharmony_ci case 'h': 50162306a36Sopenharmony_ci default: 50262306a36Sopenharmony_ci Usage(); 50362306a36Sopenharmony_ci return 0; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (optind < argc) 50862306a36Sopenharmony_ci prog = argv[optind]; 50962306a36Sopenharmony_ci printf("HBM prog: %s\n", prog != NULL ? prog : "NULL"); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* Use libbpf 1.0 API mode */ 51262306a36Sopenharmony_ci libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return run_bpf_prog(prog, cg_id); 51562306a36Sopenharmony_ci} 516