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