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