18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019 Facebook 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of version 2 of the GNU General Public 68c2ecf20Sopenharmony_ci * License as published by the Free Software Foundation. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Example program for Host Bandwidth Managment 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program loads a cgroup skb BPF program to enforce cgroup output 118c2ecf20Sopenharmony_ci * (egress) or input (ingress) bandwidth limits. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * USAGE: hbm [-d] [-l] [-n <id>] [-r <rate>] [-s] [-t <secs>] [-w] [-h] [prog] 148c2ecf20Sopenharmony_ci * Where: 158c2ecf20Sopenharmony_ci * -d Print BPF trace debug buffer 168c2ecf20Sopenharmony_ci * -l Also limit flows doing loopback 178c2ecf20Sopenharmony_ci * -n <#> To create cgroup \"/hbm#\" and attach prog 188c2ecf20Sopenharmony_ci * Default is /hbm1 198c2ecf20Sopenharmony_ci * --no_cn Do not return cn notifications 208c2ecf20Sopenharmony_ci * -r <rate> Rate limit in Mbps 218c2ecf20Sopenharmony_ci * -s Get HBM stats (marked, dropped, etc.) 228c2ecf20Sopenharmony_ci * -t <time> Exit after specified seconds (default is 0) 238c2ecf20Sopenharmony_ci * -w Work conserving flag. cgroup can increase its bandwidth 248c2ecf20Sopenharmony_ci * beyond the rate limit specified while there is available 258c2ecf20Sopenharmony_ci * bandwidth. Current implementation assumes there is only 268c2ecf20Sopenharmony_ci * NIC (eth0), but can be extended to support multiple NICs. 278c2ecf20Sopenharmony_ci * Currrently only supported for egress. 288c2ecf20Sopenharmony_ci * -h Print this info 298c2ecf20Sopenharmony_ci * prog BPF program file name. Name defaults to hbm_out_kern.o 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define _GNU_SOURCE 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <stdio.h> 358c2ecf20Sopenharmony_ci#include <stdlib.h> 368c2ecf20Sopenharmony_ci#include <assert.h> 378c2ecf20Sopenharmony_ci#include <sys/resource.h> 388c2ecf20Sopenharmony_ci#include <sys/time.h> 398c2ecf20Sopenharmony_ci#include <unistd.h> 408c2ecf20Sopenharmony_ci#include <errno.h> 418c2ecf20Sopenharmony_ci#include <fcntl.h> 428c2ecf20Sopenharmony_ci#include <linux/unistd.h> 438c2ecf20Sopenharmony_ci#include <linux/compiler.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include <linux/bpf.h> 468c2ecf20Sopenharmony_ci#include <bpf/bpf.h> 478c2ecf20Sopenharmony_ci#include <getopt.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include "bpf_load.h" 508c2ecf20Sopenharmony_ci#include "bpf_rlimit.h" 518c2ecf20Sopenharmony_ci#include "cgroup_helpers.h" 528c2ecf20Sopenharmony_ci#include "hbm.h" 538c2ecf20Sopenharmony_ci#include "bpf_util.h" 548c2ecf20Sopenharmony_ci#include <bpf/bpf.h> 558c2ecf20Sopenharmony_ci#include <bpf/libbpf.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cibool outFlag = true; 588c2ecf20Sopenharmony_ciint minRate = 1000; /* cgroup rate limit in Mbps */ 598c2ecf20Sopenharmony_ciint rate = 1000; /* can grow if rate conserving is enabled */ 608c2ecf20Sopenharmony_ciint dur = 1; 618c2ecf20Sopenharmony_cibool stats_flag; 628c2ecf20Sopenharmony_cibool loopback_flag; 638c2ecf20Sopenharmony_cibool debugFlag; 648c2ecf20Sopenharmony_cibool work_conserving_flag; 658c2ecf20Sopenharmony_cibool no_cn_flag; 668c2ecf20Sopenharmony_cibool edt_flag; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void Usage(void); 698c2ecf20Sopenharmony_cistatic void read_trace_pipe2(void); 708c2ecf20Sopenharmony_cistatic void do_error(char *msg, bool errno_flag); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define DEBUGFS "/sys/kernel/debug/tracing/" 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct bpf_object *obj; 758c2ecf20Sopenharmony_ciint bpfprog_fd; 768c2ecf20Sopenharmony_ciint cgroup_storage_fd; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void read_trace_pipe2(void) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int trace_fd; 818c2ecf20Sopenharmony_ci FILE *outf; 828c2ecf20Sopenharmony_ci char *outFname = "hbm_out.log"; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); 858c2ecf20Sopenharmony_ci if (trace_fd < 0) { 868c2ecf20Sopenharmony_ci printf("Error opening trace_pipe\n"); 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci// Future support of ingress 918c2ecf20Sopenharmony_ci// if (!outFlag) 928c2ecf20Sopenharmony_ci// outFname = "hbm_in.log"; 938c2ecf20Sopenharmony_ci outf = fopen(outFname, "w"); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (outf == NULL) 968c2ecf20Sopenharmony_ci printf("Error creating %s\n", outFname); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci while (1) { 998c2ecf20Sopenharmony_ci static char buf[4097]; 1008c2ecf20Sopenharmony_ci ssize_t sz; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci sz = read(trace_fd, buf, sizeof(buf) - 1); 1038c2ecf20Sopenharmony_ci if (sz > 0) { 1048c2ecf20Sopenharmony_ci buf[sz] = 0; 1058c2ecf20Sopenharmony_ci puts(buf); 1068c2ecf20Sopenharmony_ci if (outf != NULL) { 1078c2ecf20Sopenharmony_ci fprintf(outf, "%s\n", buf); 1088c2ecf20Sopenharmony_ci fflush(outf); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void do_error(char *msg, bool errno_flag) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci if (errno_flag) 1178c2ecf20Sopenharmony_ci printf("ERROR: %s, errno: %d\n", msg, errno); 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci printf("ERROR: %s\n", msg); 1208c2ecf20Sopenharmony_ci exit(1); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int prog_load(char *prog) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct bpf_prog_load_attr prog_load_attr = { 1268c2ecf20Sopenharmony_ci .prog_type = BPF_PROG_TYPE_CGROUP_SKB, 1278c2ecf20Sopenharmony_ci .file = prog, 1288c2ecf20Sopenharmony_ci .expected_attach_type = BPF_CGROUP_INET_EGRESS, 1298c2ecf20Sopenharmony_ci }; 1308c2ecf20Sopenharmony_ci int map_fd; 1318c2ecf20Sopenharmony_ci struct bpf_map *map; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci int ret = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (access(prog, O_RDONLY) < 0) { 1368c2ecf20Sopenharmony_ci printf("Error accessing file %s: %s\n", prog, strerror(errno)); 1378c2ecf20Sopenharmony_ci return 1; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci if (bpf_prog_load_xattr(&prog_load_attr, &obj, &bpfprog_fd)) 1408c2ecf20Sopenharmony_ci ret = 1; 1418c2ecf20Sopenharmony_ci if (!ret) { 1428c2ecf20Sopenharmony_ci map = bpf_object__find_map_by_name(obj, "queue_stats"); 1438c2ecf20Sopenharmony_ci map_fd = bpf_map__fd(map); 1448c2ecf20Sopenharmony_ci if (map_fd < 0) { 1458c2ecf20Sopenharmony_ci printf("Map not found: %s\n", strerror(map_fd)); 1468c2ecf20Sopenharmony_ci ret = 1; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (ret) { 1518c2ecf20Sopenharmony_ci printf("ERROR: bpf_prog_load_xattr failed for: %s\n", prog); 1528c2ecf20Sopenharmony_ci printf(" Output from verifier:\n%s\n------\n", bpf_log_buf); 1538c2ecf20Sopenharmony_ci ret = -1; 1548c2ecf20Sopenharmony_ci } else { 1558c2ecf20Sopenharmony_ci ret = map_fd; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return ret; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int run_bpf_prog(char *prog, int cg_id) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int map_fd; 1648c2ecf20Sopenharmony_ci int rc = 0; 1658c2ecf20Sopenharmony_ci int key = 0; 1668c2ecf20Sopenharmony_ci int cg1 = 0; 1678c2ecf20Sopenharmony_ci int type = BPF_CGROUP_INET_EGRESS; 1688c2ecf20Sopenharmony_ci char cg_dir[100]; 1698c2ecf20Sopenharmony_ci struct hbm_queue_stats qstats = {0}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci sprintf(cg_dir, "/hbm%d", cg_id); 1728c2ecf20Sopenharmony_ci map_fd = prog_load(prog); 1738c2ecf20Sopenharmony_ci if (map_fd == -1) 1748c2ecf20Sopenharmony_ci return 1; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (setup_cgroup_environment()) { 1778c2ecf20Sopenharmony_ci printf("ERROR: setting cgroup environment\n"); 1788c2ecf20Sopenharmony_ci goto err; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci cg1 = create_and_get_cgroup(cg_dir); 1818c2ecf20Sopenharmony_ci if (!cg1) { 1828c2ecf20Sopenharmony_ci printf("ERROR: create_and_get_cgroup\n"); 1838c2ecf20Sopenharmony_ci goto err; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci if (join_cgroup(cg_dir)) { 1868c2ecf20Sopenharmony_ci printf("ERROR: join_cgroup\n"); 1878c2ecf20Sopenharmony_ci goto err; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci qstats.rate = rate; 1918c2ecf20Sopenharmony_ci qstats.stats = stats_flag ? 1 : 0; 1928c2ecf20Sopenharmony_ci qstats.loopback = loopback_flag ? 1 : 0; 1938c2ecf20Sopenharmony_ci qstats.no_cn = no_cn_flag ? 1 : 0; 1948c2ecf20Sopenharmony_ci if (bpf_map_update_elem(map_fd, &key, &qstats, BPF_ANY)) { 1958c2ecf20Sopenharmony_ci printf("ERROR: Could not update map element\n"); 1968c2ecf20Sopenharmony_ci goto err; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (!outFlag) 2008c2ecf20Sopenharmony_ci type = BPF_CGROUP_INET_INGRESS; 2018c2ecf20Sopenharmony_ci if (bpf_prog_attach(bpfprog_fd, cg1, type, 0)) { 2028c2ecf20Sopenharmony_ci printf("ERROR: bpf_prog_attach fails!\n"); 2038c2ecf20Sopenharmony_ci log_err("Attaching prog"); 2048c2ecf20Sopenharmony_ci goto err; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (work_conserving_flag) { 2088c2ecf20Sopenharmony_ci struct timeval t0, t_last, t_new; 2098c2ecf20Sopenharmony_ci FILE *fin; 2108c2ecf20Sopenharmony_ci unsigned long long last_eth_tx_bytes, new_eth_tx_bytes; 2118c2ecf20Sopenharmony_ci signed long long last_cg_tx_bytes, new_cg_tx_bytes; 2128c2ecf20Sopenharmony_ci signed long long delta_time, delta_bytes, delta_rate; 2138c2ecf20Sopenharmony_ci int delta_ms; 2148c2ecf20Sopenharmony_ci#define DELTA_RATE_CHECK 10000 /* in us */ 2158c2ecf20Sopenharmony_ci#define RATE_THRESHOLD 9500000000 /* 9.5 Gbps */ 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci bpf_map_lookup_elem(map_fd, &key, &qstats); 2188c2ecf20Sopenharmony_ci if (gettimeofday(&t0, NULL) < 0) 2198c2ecf20Sopenharmony_ci do_error("gettimeofday failed", true); 2208c2ecf20Sopenharmony_ci t_last = t0; 2218c2ecf20Sopenharmony_ci fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", "r"); 2228c2ecf20Sopenharmony_ci if (fscanf(fin, "%llu", &last_eth_tx_bytes) != 1) 2238c2ecf20Sopenharmony_ci do_error("fscanf fails", false); 2248c2ecf20Sopenharmony_ci fclose(fin); 2258c2ecf20Sopenharmony_ci last_cg_tx_bytes = qstats.bytes_total; 2268c2ecf20Sopenharmony_ci while (true) { 2278c2ecf20Sopenharmony_ci usleep(DELTA_RATE_CHECK); 2288c2ecf20Sopenharmony_ci if (gettimeofday(&t_new, NULL) < 0) 2298c2ecf20Sopenharmony_ci do_error("gettimeofday failed", true); 2308c2ecf20Sopenharmony_ci delta_ms = (t_new.tv_sec - t0.tv_sec) * 1000 + 2318c2ecf20Sopenharmony_ci (t_new.tv_usec - t0.tv_usec)/1000; 2328c2ecf20Sopenharmony_ci if (delta_ms > dur * 1000) 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci delta_time = (t_new.tv_sec - t_last.tv_sec) * 1000000 + 2358c2ecf20Sopenharmony_ci (t_new.tv_usec - t_last.tv_usec); 2368c2ecf20Sopenharmony_ci if (delta_time == 0) 2378c2ecf20Sopenharmony_ci continue; 2388c2ecf20Sopenharmony_ci t_last = t_new; 2398c2ecf20Sopenharmony_ci fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", 2408c2ecf20Sopenharmony_ci "r"); 2418c2ecf20Sopenharmony_ci if (fscanf(fin, "%llu", &new_eth_tx_bytes) != 1) 2428c2ecf20Sopenharmony_ci do_error("fscanf fails", false); 2438c2ecf20Sopenharmony_ci fclose(fin); 2448c2ecf20Sopenharmony_ci printf(" new_eth_tx_bytes:%llu\n", 2458c2ecf20Sopenharmony_ci new_eth_tx_bytes); 2468c2ecf20Sopenharmony_ci bpf_map_lookup_elem(map_fd, &key, &qstats); 2478c2ecf20Sopenharmony_ci new_cg_tx_bytes = qstats.bytes_total; 2488c2ecf20Sopenharmony_ci delta_bytes = new_eth_tx_bytes - last_eth_tx_bytes; 2498c2ecf20Sopenharmony_ci last_eth_tx_bytes = new_eth_tx_bytes; 2508c2ecf20Sopenharmony_ci delta_rate = (delta_bytes * 8000000) / delta_time; 2518c2ecf20Sopenharmony_ci printf("%5d - eth_rate:%.1fGbps cg_rate:%.3fGbps", 2528c2ecf20Sopenharmony_ci delta_ms, delta_rate/1000000000.0, 2538c2ecf20Sopenharmony_ci rate/1000.0); 2548c2ecf20Sopenharmony_ci if (delta_rate < RATE_THRESHOLD) { 2558c2ecf20Sopenharmony_ci /* can increase cgroup rate limit, but first 2568c2ecf20Sopenharmony_ci * check if we are using the current limit. 2578c2ecf20Sopenharmony_ci * Currently increasing by 6.25%, unknown 2588c2ecf20Sopenharmony_ci * if that is the optimal rate. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci int rate_diff100; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci delta_bytes = new_cg_tx_bytes - 2638c2ecf20Sopenharmony_ci last_cg_tx_bytes; 2648c2ecf20Sopenharmony_ci last_cg_tx_bytes = new_cg_tx_bytes; 2658c2ecf20Sopenharmony_ci delta_rate = (delta_bytes * 8000000) / 2668c2ecf20Sopenharmony_ci delta_time; 2678c2ecf20Sopenharmony_ci printf(" rate:%.3fGbps", 2688c2ecf20Sopenharmony_ci delta_rate/1000000000.0); 2698c2ecf20Sopenharmony_ci rate_diff100 = (((long long)rate)*1000000 - 2708c2ecf20Sopenharmony_ci delta_rate) * 100 / 2718c2ecf20Sopenharmony_ci (((long long) rate) * 1000000); 2728c2ecf20Sopenharmony_ci printf(" rdiff:%d", rate_diff100); 2738c2ecf20Sopenharmony_ci if (rate_diff100 <= 3) { 2748c2ecf20Sopenharmony_ci rate += (rate >> 4); 2758c2ecf20Sopenharmony_ci if (rate > RATE_THRESHOLD / 1000000) 2768c2ecf20Sopenharmony_ci rate = RATE_THRESHOLD / 1000000; 2778c2ecf20Sopenharmony_ci qstats.rate = rate; 2788c2ecf20Sopenharmony_ci printf(" INC\n"); 2798c2ecf20Sopenharmony_ci } else { 2808c2ecf20Sopenharmony_ci printf("\n"); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci } else { 2838c2ecf20Sopenharmony_ci /* Need to decrease cgroup rate limit. 2848c2ecf20Sopenharmony_ci * Currently decreasing by 12.5%, unknown 2858c2ecf20Sopenharmony_ci * if that is optimal 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci printf(" DEC\n"); 2888c2ecf20Sopenharmony_ci rate -= (rate >> 3); 2898c2ecf20Sopenharmony_ci if (rate < minRate) 2908c2ecf20Sopenharmony_ci rate = minRate; 2918c2ecf20Sopenharmony_ci qstats.rate = rate; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci if (bpf_map_update_elem(map_fd, &key, &qstats, BPF_ANY)) 2948c2ecf20Sopenharmony_ci do_error("update map element fails", false); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } else { 2978c2ecf20Sopenharmony_ci sleep(dur); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci // Get stats! 3008c2ecf20Sopenharmony_ci if (stats_flag && bpf_map_lookup_elem(map_fd, &key, &qstats)) { 3018c2ecf20Sopenharmony_ci char fname[100]; 3028c2ecf20Sopenharmony_ci FILE *fout; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (!outFlag) 3058c2ecf20Sopenharmony_ci sprintf(fname, "hbm.%d.in", cg_id); 3068c2ecf20Sopenharmony_ci else 3078c2ecf20Sopenharmony_ci sprintf(fname, "hbm.%d.out", cg_id); 3088c2ecf20Sopenharmony_ci fout = fopen(fname, "w"); 3098c2ecf20Sopenharmony_ci fprintf(fout, "id:%d\n", cg_id); 3108c2ecf20Sopenharmony_ci fprintf(fout, "ERROR: Could not lookup queue_stats\n"); 3118c2ecf20Sopenharmony_ci fclose(fout); 3128c2ecf20Sopenharmony_ci } else if (stats_flag && qstats.lastPacketTime > 3138c2ecf20Sopenharmony_ci qstats.firstPacketTime) { 3148c2ecf20Sopenharmony_ci long long delta_us = (qstats.lastPacketTime - 3158c2ecf20Sopenharmony_ci qstats.firstPacketTime)/1000; 3168c2ecf20Sopenharmony_ci unsigned int rate_mbps = ((qstats.bytes_total - 3178c2ecf20Sopenharmony_ci qstats.bytes_dropped) * 8 / 3188c2ecf20Sopenharmony_ci delta_us); 3198c2ecf20Sopenharmony_ci double percent_pkts, percent_bytes; 3208c2ecf20Sopenharmony_ci char fname[100]; 3218c2ecf20Sopenharmony_ci FILE *fout; 3228c2ecf20Sopenharmony_ci int k; 3238c2ecf20Sopenharmony_ci static const char *returnValNames[] = { 3248c2ecf20Sopenharmony_ci "DROP_PKT", 3258c2ecf20Sopenharmony_ci "ALLOW_PKT", 3268c2ecf20Sopenharmony_ci "DROP_PKT_CWR", 3278c2ecf20Sopenharmony_ci "ALLOW_PKT_CWR" 3288c2ecf20Sopenharmony_ci }; 3298c2ecf20Sopenharmony_ci#define RET_VAL_COUNT 4 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci// Future support of ingress 3328c2ecf20Sopenharmony_ci// if (!outFlag) 3338c2ecf20Sopenharmony_ci// sprintf(fname, "hbm.%d.in", cg_id); 3348c2ecf20Sopenharmony_ci// else 3358c2ecf20Sopenharmony_ci sprintf(fname, "hbm.%d.out", cg_id); 3368c2ecf20Sopenharmony_ci fout = fopen(fname, "w"); 3378c2ecf20Sopenharmony_ci fprintf(fout, "id:%d\n", cg_id); 3388c2ecf20Sopenharmony_ci fprintf(fout, "rate_mbps:%d\n", rate_mbps); 3398c2ecf20Sopenharmony_ci fprintf(fout, "duration:%.1f secs\n", 3408c2ecf20Sopenharmony_ci (qstats.lastPacketTime - qstats.firstPacketTime) / 3418c2ecf20Sopenharmony_ci 1000000000.0); 3428c2ecf20Sopenharmony_ci fprintf(fout, "packets:%d\n", (int)qstats.pkts_total); 3438c2ecf20Sopenharmony_ci fprintf(fout, "bytes_MB:%d\n", (int)(qstats.bytes_total / 3448c2ecf20Sopenharmony_ci 1000000)); 3458c2ecf20Sopenharmony_ci fprintf(fout, "pkts_dropped:%d\n", (int)qstats.pkts_dropped); 3468c2ecf20Sopenharmony_ci fprintf(fout, "bytes_dropped_MB:%d\n", 3478c2ecf20Sopenharmony_ci (int)(qstats.bytes_dropped / 3488c2ecf20Sopenharmony_ci 1000000)); 3498c2ecf20Sopenharmony_ci // Marked Pkts and Bytes 3508c2ecf20Sopenharmony_ci percent_pkts = (qstats.pkts_marked * 100.0) / 3518c2ecf20Sopenharmony_ci (qstats.pkts_total + 1); 3528c2ecf20Sopenharmony_ci percent_bytes = (qstats.bytes_marked * 100.0) / 3538c2ecf20Sopenharmony_ci (qstats.bytes_total + 1); 3548c2ecf20Sopenharmony_ci fprintf(fout, "pkts_marked_percent:%6.2f\n", percent_pkts); 3558c2ecf20Sopenharmony_ci fprintf(fout, "bytes_marked_percent:%6.2f\n", percent_bytes); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci // Dropped Pkts and Bytes 3588c2ecf20Sopenharmony_ci percent_pkts = (qstats.pkts_dropped * 100.0) / 3598c2ecf20Sopenharmony_ci (qstats.pkts_total + 1); 3608c2ecf20Sopenharmony_ci percent_bytes = (qstats.bytes_dropped * 100.0) / 3618c2ecf20Sopenharmony_ci (qstats.bytes_total + 1); 3628c2ecf20Sopenharmony_ci fprintf(fout, "pkts_dropped_percent:%6.2f\n", percent_pkts); 3638c2ecf20Sopenharmony_ci fprintf(fout, "bytes_dropped_percent:%6.2f\n", percent_bytes); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci // ECN CE markings 3668c2ecf20Sopenharmony_ci percent_pkts = (qstats.pkts_ecn_ce * 100.0) / 3678c2ecf20Sopenharmony_ci (qstats.pkts_total + 1); 3688c2ecf20Sopenharmony_ci fprintf(fout, "pkts_ecn_ce:%6.2f (%d)\n", percent_pkts, 3698c2ecf20Sopenharmony_ci (int)qstats.pkts_ecn_ce); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci // Average cwnd 3728c2ecf20Sopenharmony_ci fprintf(fout, "avg cwnd:%d\n", 3738c2ecf20Sopenharmony_ci (int)(qstats.sum_cwnd / (qstats.sum_cwnd_cnt + 1))); 3748c2ecf20Sopenharmony_ci // Average rtt 3758c2ecf20Sopenharmony_ci fprintf(fout, "avg rtt:%d\n", 3768c2ecf20Sopenharmony_ci (int)(qstats.sum_rtt / (qstats.pkts_total + 1))); 3778c2ecf20Sopenharmony_ci // Average credit 3788c2ecf20Sopenharmony_ci if (edt_flag) 3798c2ecf20Sopenharmony_ci fprintf(fout, "avg credit_ms:%.03f\n", 3808c2ecf20Sopenharmony_ci (qstats.sum_credit / 3818c2ecf20Sopenharmony_ci (qstats.pkts_total + 1.0)) / 1000000.0); 3828c2ecf20Sopenharmony_ci else 3838c2ecf20Sopenharmony_ci fprintf(fout, "avg credit:%d\n", 3848c2ecf20Sopenharmony_ci (int)(qstats.sum_credit / 3858c2ecf20Sopenharmony_ci (1500 * ((int)qstats.pkts_total ) + 1))); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci // Return values stats 3888c2ecf20Sopenharmony_ci for (k = 0; k < RET_VAL_COUNT; k++) { 3898c2ecf20Sopenharmony_ci percent_pkts = (qstats.returnValCount[k] * 100.0) / 3908c2ecf20Sopenharmony_ci (qstats.pkts_total + 1); 3918c2ecf20Sopenharmony_ci fprintf(fout, "%s:%6.2f (%d)\n", returnValNames[k], 3928c2ecf20Sopenharmony_ci percent_pkts, (int)qstats.returnValCount[k]); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci fclose(fout); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (debugFlag) 3988c2ecf20Sopenharmony_ci read_trace_pipe2(); 3998c2ecf20Sopenharmony_ci return rc; 4008c2ecf20Sopenharmony_cierr: 4018c2ecf20Sopenharmony_ci rc = 1; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (cg1) 4048c2ecf20Sopenharmony_ci close(cg1); 4058c2ecf20Sopenharmony_ci cleanup_cgroup_environment(); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return rc; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic void Usage(void) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci printf("This program loads a cgroup skb BPF program to enforce\n" 4138c2ecf20Sopenharmony_ci "cgroup output (egress) bandwidth limits.\n\n" 4148c2ecf20Sopenharmony_ci "USAGE: hbm [-o] [-d] [-l] [-n <id>] [--no_cn] [-r <rate>]\n" 4158c2ecf20Sopenharmony_ci " [-s] [-t <secs>] [-w] [-h] [prog]\n" 4168c2ecf20Sopenharmony_ci " Where:\n" 4178c2ecf20Sopenharmony_ci " -o indicates egress direction (default)\n" 4188c2ecf20Sopenharmony_ci " -d print BPF trace debug buffer\n" 4198c2ecf20Sopenharmony_ci " --edt use fq's Earliest Departure Time\n" 4208c2ecf20Sopenharmony_ci " -l also limit flows using loopback\n" 4218c2ecf20Sopenharmony_ci " -n <#> to create cgroup \"/hbm#\" and attach prog\n" 4228c2ecf20Sopenharmony_ci " Default is /hbm1\n" 4238c2ecf20Sopenharmony_ci " --no_cn disable CN notifications\n" 4248c2ecf20Sopenharmony_ci " -r <rate> Rate in Mbps\n" 4258c2ecf20Sopenharmony_ci " -s Update HBM stats\n" 4268c2ecf20Sopenharmony_ci " -t <time> Exit after specified seconds (default is 0)\n" 4278c2ecf20Sopenharmony_ci " -w Work conserving flag. cgroup can increase\n" 4288c2ecf20Sopenharmony_ci " bandwidth beyond the rate limit specified\n" 4298c2ecf20Sopenharmony_ci " while there is available bandwidth. Current\n" 4308c2ecf20Sopenharmony_ci " implementation assumes there is only eth0\n" 4318c2ecf20Sopenharmony_ci " but can be extended to support multiple NICs\n" 4328c2ecf20Sopenharmony_ci " -h print this info\n" 4338c2ecf20Sopenharmony_ci " prog BPF program file name. Name defaults to\n" 4348c2ecf20Sopenharmony_ci " hbm_out_kern.o\n"); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ciint main(int argc, char **argv) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci char *prog = "hbm_out_kern.o"; 4408c2ecf20Sopenharmony_ci int k; 4418c2ecf20Sopenharmony_ci int cg_id = 1; 4428c2ecf20Sopenharmony_ci char *optstring = "iodln:r:st:wh"; 4438c2ecf20Sopenharmony_ci struct option loptions[] = { 4448c2ecf20Sopenharmony_ci {"no_cn", 0, NULL, 1}, 4458c2ecf20Sopenharmony_ci {"edt", 0, NULL, 2}, 4468c2ecf20Sopenharmony_ci {NULL, 0, NULL, 0} 4478c2ecf20Sopenharmony_ci }; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci while ((k = getopt_long(argc, argv, optstring, loptions, NULL)) != -1) { 4508c2ecf20Sopenharmony_ci switch (k) { 4518c2ecf20Sopenharmony_ci case 1: 4528c2ecf20Sopenharmony_ci no_cn_flag = true; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci case 2: 4558c2ecf20Sopenharmony_ci prog = "hbm_edt_kern.o"; 4568c2ecf20Sopenharmony_ci edt_flag = true; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci case'o': 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci case 'd': 4618c2ecf20Sopenharmony_ci debugFlag = true; 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci case 'l': 4648c2ecf20Sopenharmony_ci loopback_flag = true; 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci case 'n': 4678c2ecf20Sopenharmony_ci cg_id = atoi(optarg); 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci case 'r': 4708c2ecf20Sopenharmony_ci minRate = atoi(optarg) * 1.024; 4718c2ecf20Sopenharmony_ci rate = minRate; 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci case 's': 4748c2ecf20Sopenharmony_ci stats_flag = true; 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci case 't': 4778c2ecf20Sopenharmony_ci dur = atoi(optarg); 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci case 'w': 4808c2ecf20Sopenharmony_ci work_conserving_flag = true; 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci case '?': 4838c2ecf20Sopenharmony_ci if (optopt == 'n' || optopt == 'r' || optopt == 't') 4848c2ecf20Sopenharmony_ci fprintf(stderr, 4858c2ecf20Sopenharmony_ci "Option -%c requires an argument.\n\n", 4868c2ecf20Sopenharmony_ci optopt); 4878c2ecf20Sopenharmony_ci case 'h': 4888c2ecf20Sopenharmony_ci __fallthrough; 4898c2ecf20Sopenharmony_ci default: 4908c2ecf20Sopenharmony_ci Usage(); 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (optind < argc) 4968c2ecf20Sopenharmony_ci prog = argv[optind]; 4978c2ecf20Sopenharmony_ci printf("HBM prog: %s\n", prog != NULL ? prog : "NULL"); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return run_bpf_prog(prog, cg_id); 5008c2ecf20Sopenharmony_ci} 501