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