xref: /OK3568_Linux_fs/kernel/samples/bpf/xdp_monitor_user.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun  * Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
3*4882a593Smuzhiyun  */
4*4882a593Smuzhiyun static const char *__doc__=
5*4882a593Smuzhiyun  "XDP monitor tool, based on tracepoints\n"
6*4882a593Smuzhiyun ;
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun static const char *__doc_err_only__=
9*4882a593Smuzhiyun  " NOTICE: Only tracking XDP redirect errors\n"
10*4882a593Smuzhiyun  "         Enable TX success stats via '--stats'\n"
11*4882a593Smuzhiyun  "         (which comes with a per packet processing overhead)\n"
12*4882a593Smuzhiyun ;
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <errno.h>
15*4882a593Smuzhiyun #include <stdio.h>
16*4882a593Smuzhiyun #include <stdlib.h>
17*4882a593Smuzhiyun #include <stdbool.h>
18*4882a593Smuzhiyun #include <stdint.h>
19*4882a593Smuzhiyun #include <string.h>
20*4882a593Smuzhiyun #include <ctype.h>
21*4882a593Smuzhiyun #include <unistd.h>
22*4882a593Smuzhiyun #include <locale.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include <sys/resource.h>
25*4882a593Smuzhiyun #include <getopt.h>
26*4882a593Smuzhiyun #include <net/if.h>
27*4882a593Smuzhiyun #include <time.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #include <signal.h>
30*4882a593Smuzhiyun #include <bpf/bpf.h>
31*4882a593Smuzhiyun #include <bpf/libbpf.h>
32*4882a593Smuzhiyun #include "bpf_util.h"
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun enum map_type {
35*4882a593Smuzhiyun 	REDIRECT_ERR_CNT,
36*4882a593Smuzhiyun 	EXCEPTION_CNT,
37*4882a593Smuzhiyun 	CPUMAP_ENQUEUE_CNT,
38*4882a593Smuzhiyun 	CPUMAP_KTHREAD_CNT,
39*4882a593Smuzhiyun 	DEVMAP_XMIT_CNT,
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun static const char *const map_type_strings[] = {
43*4882a593Smuzhiyun 	[REDIRECT_ERR_CNT] = "redirect_err_cnt",
44*4882a593Smuzhiyun 	[EXCEPTION_CNT] = "exception_cnt",
45*4882a593Smuzhiyun 	[CPUMAP_ENQUEUE_CNT] = "cpumap_enqueue_cnt",
46*4882a593Smuzhiyun 	[CPUMAP_KTHREAD_CNT] = "cpumap_kthread_cnt",
47*4882a593Smuzhiyun 	[DEVMAP_XMIT_CNT] = "devmap_xmit_cnt",
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun #define NUM_MAP 5
51*4882a593Smuzhiyun #define NUM_TP 8
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun static int tp_cnt;
54*4882a593Smuzhiyun static int map_cnt;
55*4882a593Smuzhiyun static int verbose = 1;
56*4882a593Smuzhiyun static bool debug = false;
57*4882a593Smuzhiyun struct bpf_map *map_data[NUM_MAP] = {};
58*4882a593Smuzhiyun struct bpf_link *tp_links[NUM_TP] = {};
59*4882a593Smuzhiyun struct bpf_object *obj;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun static const struct option long_options[] = {
62*4882a593Smuzhiyun 	{"help",	no_argument,		NULL, 'h' },
63*4882a593Smuzhiyun 	{"debug",	no_argument,		NULL, 'D' },
64*4882a593Smuzhiyun 	{"stats",	no_argument,		NULL, 'S' },
65*4882a593Smuzhiyun 	{"sec", 	required_argument,	NULL, 's' },
66*4882a593Smuzhiyun 	{0, 0, NULL,  0 }
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun 
int_exit(int sig)69*4882a593Smuzhiyun static void int_exit(int sig)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	/* Detach tracepoints */
72*4882a593Smuzhiyun 	while (tp_cnt)
73*4882a593Smuzhiyun 		bpf_link__destroy(tp_links[--tp_cnt]);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	bpf_object__close(obj);
76*4882a593Smuzhiyun 	exit(0);
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /* C standard specifies two constants, EXIT_SUCCESS(0) and EXIT_FAILURE(1) */
80*4882a593Smuzhiyun #define EXIT_FAIL_MEM	5
81*4882a593Smuzhiyun 
usage(char * argv[])82*4882a593Smuzhiyun static void usage(char *argv[])
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun 	int i;
85*4882a593Smuzhiyun 	printf("\nDOCUMENTATION:\n%s\n", __doc__);
86*4882a593Smuzhiyun 	printf("\n");
87*4882a593Smuzhiyun 	printf(" Usage: %s (options-see-below)\n",
88*4882a593Smuzhiyun 	       argv[0]);
89*4882a593Smuzhiyun 	printf(" Listing options:\n");
90*4882a593Smuzhiyun 	for (i = 0; long_options[i].name != 0; i++) {
91*4882a593Smuzhiyun 		printf(" --%-15s", long_options[i].name);
92*4882a593Smuzhiyun 		if (long_options[i].flag != NULL)
93*4882a593Smuzhiyun 			printf(" flag (internal value:%d)",
94*4882a593Smuzhiyun 			       *long_options[i].flag);
95*4882a593Smuzhiyun 		else
96*4882a593Smuzhiyun 			printf("short-option: -%c",
97*4882a593Smuzhiyun 			       long_options[i].val);
98*4882a593Smuzhiyun 		printf("\n");
99*4882a593Smuzhiyun 	}
100*4882a593Smuzhiyun 	printf("\n");
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun #define NANOSEC_PER_SEC 1000000000 /* 10^9 */
gettime(void)104*4882a593Smuzhiyun static __u64 gettime(void)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	struct timespec t;
107*4882a593Smuzhiyun 	int res;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	res = clock_gettime(CLOCK_MONOTONIC, &t);
110*4882a593Smuzhiyun 	if (res < 0) {
111*4882a593Smuzhiyun 		fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
112*4882a593Smuzhiyun 		exit(EXIT_FAILURE);
113*4882a593Smuzhiyun 	}
114*4882a593Smuzhiyun 	return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun enum {
118*4882a593Smuzhiyun 	REDIR_SUCCESS = 0,
119*4882a593Smuzhiyun 	REDIR_ERROR = 1,
120*4882a593Smuzhiyun };
121*4882a593Smuzhiyun #define REDIR_RES_MAX 2
122*4882a593Smuzhiyun static const char *redir_names[REDIR_RES_MAX] = {
123*4882a593Smuzhiyun 	[REDIR_SUCCESS]	= "Success",
124*4882a593Smuzhiyun 	[REDIR_ERROR]	= "Error",
125*4882a593Smuzhiyun };
err2str(int err)126*4882a593Smuzhiyun static const char *err2str(int err)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	if (err < REDIR_RES_MAX)
129*4882a593Smuzhiyun 		return redir_names[err];
130*4882a593Smuzhiyun 	return NULL;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun /* enum xdp_action */
133*4882a593Smuzhiyun #define XDP_UNKNOWN	XDP_REDIRECT + 1
134*4882a593Smuzhiyun #define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
135*4882a593Smuzhiyun static const char *xdp_action_names[XDP_ACTION_MAX] = {
136*4882a593Smuzhiyun 	[XDP_ABORTED]	= "XDP_ABORTED",
137*4882a593Smuzhiyun 	[XDP_DROP]	= "XDP_DROP",
138*4882a593Smuzhiyun 	[XDP_PASS]	= "XDP_PASS",
139*4882a593Smuzhiyun 	[XDP_TX]	= "XDP_TX",
140*4882a593Smuzhiyun 	[XDP_REDIRECT]	= "XDP_REDIRECT",
141*4882a593Smuzhiyun 	[XDP_UNKNOWN]	= "XDP_UNKNOWN",
142*4882a593Smuzhiyun };
action2str(int action)143*4882a593Smuzhiyun static const char *action2str(int action)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun 	if (action < XDP_ACTION_MAX)
146*4882a593Smuzhiyun 		return xdp_action_names[action];
147*4882a593Smuzhiyun 	return NULL;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun /* Common stats data record shared with _kern.c */
151*4882a593Smuzhiyun struct datarec {
152*4882a593Smuzhiyun 	__u64 processed;
153*4882a593Smuzhiyun 	__u64 dropped;
154*4882a593Smuzhiyun 	__u64 info;
155*4882a593Smuzhiyun 	__u64 err;
156*4882a593Smuzhiyun };
157*4882a593Smuzhiyun #define MAX_CPUS 64
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun /* Userspace structs for collection of stats from maps */
160*4882a593Smuzhiyun struct record {
161*4882a593Smuzhiyun 	__u64 timestamp;
162*4882a593Smuzhiyun 	struct datarec total;
163*4882a593Smuzhiyun 	struct datarec *cpu;
164*4882a593Smuzhiyun };
165*4882a593Smuzhiyun struct u64rec {
166*4882a593Smuzhiyun 	__u64 processed;
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun struct record_u64 {
169*4882a593Smuzhiyun 	/* record for _kern side __u64 values */
170*4882a593Smuzhiyun 	__u64 timestamp;
171*4882a593Smuzhiyun 	struct u64rec total;
172*4882a593Smuzhiyun 	struct u64rec *cpu;
173*4882a593Smuzhiyun };
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun struct stats_record {
176*4882a593Smuzhiyun 	struct record_u64 xdp_redirect[REDIR_RES_MAX];
177*4882a593Smuzhiyun 	struct record_u64 xdp_exception[XDP_ACTION_MAX];
178*4882a593Smuzhiyun 	struct record xdp_cpumap_kthread;
179*4882a593Smuzhiyun 	struct record xdp_cpumap_enqueue[MAX_CPUS];
180*4882a593Smuzhiyun 	struct record xdp_devmap_xmit;
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun 
map_collect_record(int fd,__u32 key,struct record * rec)183*4882a593Smuzhiyun static bool map_collect_record(int fd, __u32 key, struct record *rec)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun 	/* For percpu maps, userspace gets a value per possible CPU */
186*4882a593Smuzhiyun 	unsigned int nr_cpus = bpf_num_possible_cpus();
187*4882a593Smuzhiyun 	struct datarec values[nr_cpus];
188*4882a593Smuzhiyun 	__u64 sum_processed = 0;
189*4882a593Smuzhiyun 	__u64 sum_dropped = 0;
190*4882a593Smuzhiyun 	__u64 sum_info = 0;
191*4882a593Smuzhiyun 	__u64 sum_err = 0;
192*4882a593Smuzhiyun 	int i;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
195*4882a593Smuzhiyun 		fprintf(stderr,
196*4882a593Smuzhiyun 			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
197*4882a593Smuzhiyun 		return false;
198*4882a593Smuzhiyun 	}
199*4882a593Smuzhiyun 	/* Get time as close as possible to reading map contents */
200*4882a593Smuzhiyun 	rec->timestamp = gettime();
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	/* Record and sum values from each CPU */
203*4882a593Smuzhiyun 	for (i = 0; i < nr_cpus; i++) {
204*4882a593Smuzhiyun 		rec->cpu[i].processed = values[i].processed;
205*4882a593Smuzhiyun 		sum_processed        += values[i].processed;
206*4882a593Smuzhiyun 		rec->cpu[i].dropped = values[i].dropped;
207*4882a593Smuzhiyun 		sum_dropped        += values[i].dropped;
208*4882a593Smuzhiyun 		rec->cpu[i].info = values[i].info;
209*4882a593Smuzhiyun 		sum_info        += values[i].info;
210*4882a593Smuzhiyun 		rec->cpu[i].err = values[i].err;
211*4882a593Smuzhiyun 		sum_err        += values[i].err;
212*4882a593Smuzhiyun 	}
213*4882a593Smuzhiyun 	rec->total.processed = sum_processed;
214*4882a593Smuzhiyun 	rec->total.dropped   = sum_dropped;
215*4882a593Smuzhiyun 	rec->total.info      = sum_info;
216*4882a593Smuzhiyun 	rec->total.err       = sum_err;
217*4882a593Smuzhiyun 	return true;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
map_collect_record_u64(int fd,__u32 key,struct record_u64 * rec)220*4882a593Smuzhiyun static bool map_collect_record_u64(int fd, __u32 key, struct record_u64 *rec)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	/* For percpu maps, userspace gets a value per possible CPU */
223*4882a593Smuzhiyun 	unsigned int nr_cpus = bpf_num_possible_cpus();
224*4882a593Smuzhiyun 	struct u64rec values[nr_cpus];
225*4882a593Smuzhiyun 	__u64 sum_total = 0;
226*4882a593Smuzhiyun 	int i;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
229*4882a593Smuzhiyun 		fprintf(stderr,
230*4882a593Smuzhiyun 			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
231*4882a593Smuzhiyun 		return false;
232*4882a593Smuzhiyun 	}
233*4882a593Smuzhiyun 	/* Get time as close as possible to reading map contents */
234*4882a593Smuzhiyun 	rec->timestamp = gettime();
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	/* Record and sum values from each CPU */
237*4882a593Smuzhiyun 	for (i = 0; i < nr_cpus; i++) {
238*4882a593Smuzhiyun 		rec->cpu[i].processed = values[i].processed;
239*4882a593Smuzhiyun 		sum_total            += values[i].processed;
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun 	rec->total.processed = sum_total;
242*4882a593Smuzhiyun 	return true;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
calc_period(struct record * r,struct record * p)245*4882a593Smuzhiyun static double calc_period(struct record *r, struct record *p)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	double period_ = 0;
248*4882a593Smuzhiyun 	__u64 period = 0;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	period = r->timestamp - p->timestamp;
251*4882a593Smuzhiyun 	if (period > 0)
252*4882a593Smuzhiyun 		period_ = ((double) period / NANOSEC_PER_SEC);
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	return period_;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun 
calc_period_u64(struct record_u64 * r,struct record_u64 * p)257*4882a593Smuzhiyun static double calc_period_u64(struct record_u64 *r, struct record_u64 *p)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun 	double period_ = 0;
260*4882a593Smuzhiyun 	__u64 period = 0;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	period = r->timestamp - p->timestamp;
263*4882a593Smuzhiyun 	if (period > 0)
264*4882a593Smuzhiyun 		period_ = ((double) period / NANOSEC_PER_SEC);
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	return period_;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
calc_pps(struct datarec * r,struct datarec * p,double period)269*4882a593Smuzhiyun static double calc_pps(struct datarec *r, struct datarec *p, double period)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	__u64 packets = 0;
272*4882a593Smuzhiyun 	double pps = 0;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	if (period > 0) {
275*4882a593Smuzhiyun 		packets = r->processed - p->processed;
276*4882a593Smuzhiyun 		pps = packets / period;
277*4882a593Smuzhiyun 	}
278*4882a593Smuzhiyun 	return pps;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
calc_pps_u64(struct u64rec * r,struct u64rec * p,double period)281*4882a593Smuzhiyun static double calc_pps_u64(struct u64rec *r, struct u64rec *p, double period)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	__u64 packets = 0;
284*4882a593Smuzhiyun 	double pps = 0;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	if (period > 0) {
287*4882a593Smuzhiyun 		packets = r->processed - p->processed;
288*4882a593Smuzhiyun 		pps = packets / period;
289*4882a593Smuzhiyun 	}
290*4882a593Smuzhiyun 	return pps;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun 
calc_drop(struct datarec * r,struct datarec * p,double period)293*4882a593Smuzhiyun static double calc_drop(struct datarec *r, struct datarec *p, double period)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun 	__u64 packets = 0;
296*4882a593Smuzhiyun 	double pps = 0;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	if (period > 0) {
299*4882a593Smuzhiyun 		packets = r->dropped - p->dropped;
300*4882a593Smuzhiyun 		pps = packets / period;
301*4882a593Smuzhiyun 	}
302*4882a593Smuzhiyun 	return pps;
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun 
calc_info(struct datarec * r,struct datarec * p,double period)305*4882a593Smuzhiyun static double calc_info(struct datarec *r, struct datarec *p, double period)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun 	__u64 packets = 0;
308*4882a593Smuzhiyun 	double pps = 0;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (period > 0) {
311*4882a593Smuzhiyun 		packets = r->info - p->info;
312*4882a593Smuzhiyun 		pps = packets / period;
313*4882a593Smuzhiyun 	}
314*4882a593Smuzhiyun 	return pps;
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun 
calc_err(struct datarec * r,struct datarec * p,double period)317*4882a593Smuzhiyun static double calc_err(struct datarec *r, struct datarec *p, double period)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun 	__u64 packets = 0;
320*4882a593Smuzhiyun 	double pps = 0;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	if (period > 0) {
323*4882a593Smuzhiyun 		packets = r->err - p->err;
324*4882a593Smuzhiyun 		pps = packets / period;
325*4882a593Smuzhiyun 	}
326*4882a593Smuzhiyun 	return pps;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
stats_print(struct stats_record * stats_rec,struct stats_record * stats_prev,bool err_only)329*4882a593Smuzhiyun static void stats_print(struct stats_record *stats_rec,
330*4882a593Smuzhiyun 			struct stats_record *stats_prev,
331*4882a593Smuzhiyun 			bool err_only)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun 	unsigned int nr_cpus = bpf_num_possible_cpus();
334*4882a593Smuzhiyun 	int rec_i = 0, i, to_cpu;
335*4882a593Smuzhiyun 	double t = 0, pps = 0;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	/* Header */
338*4882a593Smuzhiyun 	printf("%-15s %-7s %-12s %-12s %-9s\n",
339*4882a593Smuzhiyun 	       "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info");
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	/* tracepoint: xdp:xdp_redirect_* */
342*4882a593Smuzhiyun 	if (err_only)
343*4882a593Smuzhiyun 		rec_i = REDIR_ERROR;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	for (; rec_i < REDIR_RES_MAX; rec_i++) {
346*4882a593Smuzhiyun 		struct record_u64 *rec, *prev;
347*4882a593Smuzhiyun 		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
348*4882a593Smuzhiyun 		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 		rec  =  &stats_rec->xdp_redirect[rec_i];
351*4882a593Smuzhiyun 		prev = &stats_prev->xdp_redirect[rec_i];
352*4882a593Smuzhiyun 		t = calc_period_u64(rec, prev);
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 		for (i = 0; i < nr_cpus; i++) {
355*4882a593Smuzhiyun 			struct u64rec *r = &rec->cpu[i];
356*4882a593Smuzhiyun 			struct u64rec *p = &prev->cpu[i];
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 			pps = calc_pps_u64(r, p, t);
359*4882a593Smuzhiyun 			if (pps > 0)
360*4882a593Smuzhiyun 				printf(fmt1, "XDP_REDIRECT", i,
361*4882a593Smuzhiyun 				       rec_i ? 0.0: pps, rec_i ? pps : 0.0,
362*4882a593Smuzhiyun 				       err2str(rec_i));
363*4882a593Smuzhiyun 		}
364*4882a593Smuzhiyun 		pps = calc_pps_u64(&rec->total, &prev->total, t);
365*4882a593Smuzhiyun 		printf(fmt2, "XDP_REDIRECT", "total",
366*4882a593Smuzhiyun 		       rec_i ? 0.0: pps, rec_i ? pps : 0.0, err2str(rec_i));
367*4882a593Smuzhiyun 	}
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	/* tracepoint: xdp:xdp_exception */
370*4882a593Smuzhiyun 	for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
371*4882a593Smuzhiyun 		struct record_u64 *rec, *prev;
372*4882a593Smuzhiyun 		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
373*4882a593Smuzhiyun 		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 		rec  =  &stats_rec->xdp_exception[rec_i];
376*4882a593Smuzhiyun 		prev = &stats_prev->xdp_exception[rec_i];
377*4882a593Smuzhiyun 		t = calc_period_u64(rec, prev);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 		for (i = 0; i < nr_cpus; i++) {
380*4882a593Smuzhiyun 			struct u64rec *r = &rec->cpu[i];
381*4882a593Smuzhiyun 			struct u64rec *p = &prev->cpu[i];
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 			pps = calc_pps_u64(r, p, t);
384*4882a593Smuzhiyun 			if (pps > 0)
385*4882a593Smuzhiyun 				printf(fmt1, "Exception", i,
386*4882a593Smuzhiyun 				       0.0, pps, action2str(rec_i));
387*4882a593Smuzhiyun 		}
388*4882a593Smuzhiyun 		pps = calc_pps_u64(&rec->total, &prev->total, t);
389*4882a593Smuzhiyun 		if (pps > 0)
390*4882a593Smuzhiyun 			printf(fmt2, "Exception", "total",
391*4882a593Smuzhiyun 			       0.0, pps, action2str(rec_i));
392*4882a593Smuzhiyun 	}
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	/* cpumap enqueue stats */
395*4882a593Smuzhiyun 	for (to_cpu = 0; to_cpu < MAX_CPUS; to_cpu++) {
396*4882a593Smuzhiyun 		char *fmt1 = "%-15s %3d:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
397*4882a593Smuzhiyun 		char *fmt2 = "%-15s %3s:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
398*4882a593Smuzhiyun 		struct record *rec, *prev;
399*4882a593Smuzhiyun 		char *info_str = "";
400*4882a593Smuzhiyun 		double drop, info;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 		rec  =  &stats_rec->xdp_cpumap_enqueue[to_cpu];
403*4882a593Smuzhiyun 		prev = &stats_prev->xdp_cpumap_enqueue[to_cpu];
404*4882a593Smuzhiyun 		t = calc_period(rec, prev);
405*4882a593Smuzhiyun 		for (i = 0; i < nr_cpus; i++) {
406*4882a593Smuzhiyun 			struct datarec *r = &rec->cpu[i];
407*4882a593Smuzhiyun 			struct datarec *p = &prev->cpu[i];
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 			pps  = calc_pps(r, p, t);
410*4882a593Smuzhiyun 			drop = calc_drop(r, p, t);
411*4882a593Smuzhiyun 			info = calc_info(r, p, t);
412*4882a593Smuzhiyun 			if (info > 0) {
413*4882a593Smuzhiyun 				info_str = "bulk-average";
414*4882a593Smuzhiyun 				info = pps / info; /* calc average bulk size */
415*4882a593Smuzhiyun 			}
416*4882a593Smuzhiyun 			if (pps > 0)
417*4882a593Smuzhiyun 				printf(fmt1, "cpumap-enqueue",
418*4882a593Smuzhiyun 				       i, to_cpu, pps, drop, info, info_str);
419*4882a593Smuzhiyun 		}
420*4882a593Smuzhiyun 		pps = calc_pps(&rec->total, &prev->total, t);
421*4882a593Smuzhiyun 		if (pps > 0) {
422*4882a593Smuzhiyun 			drop = calc_drop(&rec->total, &prev->total, t);
423*4882a593Smuzhiyun 			info = calc_info(&rec->total, &prev->total, t);
424*4882a593Smuzhiyun 			if (info > 0) {
425*4882a593Smuzhiyun 				info_str = "bulk-average";
426*4882a593Smuzhiyun 				info = pps / info; /* calc average bulk size */
427*4882a593Smuzhiyun 			}
428*4882a593Smuzhiyun 			printf(fmt2, "cpumap-enqueue",
429*4882a593Smuzhiyun 			       "sum", to_cpu, pps, drop, info, info_str);
430*4882a593Smuzhiyun 		}
431*4882a593Smuzhiyun 	}
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	/* cpumap kthread stats */
434*4882a593Smuzhiyun 	{
435*4882a593Smuzhiyun 		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.0f %s\n";
436*4882a593Smuzhiyun 		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.0f %s\n";
437*4882a593Smuzhiyun 		struct record *rec, *prev;
438*4882a593Smuzhiyun 		double drop, info;
439*4882a593Smuzhiyun 		char *i_str = "";
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 		rec  =  &stats_rec->xdp_cpumap_kthread;
442*4882a593Smuzhiyun 		prev = &stats_prev->xdp_cpumap_kthread;
443*4882a593Smuzhiyun 		t = calc_period(rec, prev);
444*4882a593Smuzhiyun 		for (i = 0; i < nr_cpus; i++) {
445*4882a593Smuzhiyun 			struct datarec *r = &rec->cpu[i];
446*4882a593Smuzhiyun 			struct datarec *p = &prev->cpu[i];
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 			pps  = calc_pps(r, p, t);
449*4882a593Smuzhiyun 			drop = calc_drop(r, p, t);
450*4882a593Smuzhiyun 			info = calc_info(r, p, t);
451*4882a593Smuzhiyun 			if (info > 0)
452*4882a593Smuzhiyun 				i_str = "sched";
453*4882a593Smuzhiyun 			if (pps > 0 || drop > 0)
454*4882a593Smuzhiyun 				printf(fmt1, "cpumap-kthread",
455*4882a593Smuzhiyun 				       i, pps, drop, info, i_str);
456*4882a593Smuzhiyun 		}
457*4882a593Smuzhiyun 		pps = calc_pps(&rec->total, &prev->total, t);
458*4882a593Smuzhiyun 		drop = calc_drop(&rec->total, &prev->total, t);
459*4882a593Smuzhiyun 		info = calc_info(&rec->total, &prev->total, t);
460*4882a593Smuzhiyun 		if (info > 0)
461*4882a593Smuzhiyun 			i_str = "sched-sum";
462*4882a593Smuzhiyun 		printf(fmt2, "cpumap-kthread", "total", pps, drop, info, i_str);
463*4882a593Smuzhiyun 	}
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	/* devmap ndo_xdp_xmit stats */
466*4882a593Smuzhiyun 	{
467*4882a593Smuzhiyun 		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.2f %s %s\n";
468*4882a593Smuzhiyun 		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.2f %s %s\n";
469*4882a593Smuzhiyun 		struct record *rec, *prev;
470*4882a593Smuzhiyun 		double drop, info, err;
471*4882a593Smuzhiyun 		char *i_str = "";
472*4882a593Smuzhiyun 		char *err_str = "";
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 		rec  =  &stats_rec->xdp_devmap_xmit;
475*4882a593Smuzhiyun 		prev = &stats_prev->xdp_devmap_xmit;
476*4882a593Smuzhiyun 		t = calc_period(rec, prev);
477*4882a593Smuzhiyun 		for (i = 0; i < nr_cpus; i++) {
478*4882a593Smuzhiyun 			struct datarec *r = &rec->cpu[i];
479*4882a593Smuzhiyun 			struct datarec *p = &prev->cpu[i];
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 			pps  = calc_pps(r, p, t);
482*4882a593Smuzhiyun 			drop = calc_drop(r, p, t);
483*4882a593Smuzhiyun 			info = calc_info(r, p, t);
484*4882a593Smuzhiyun 			err  = calc_err(r, p, t);
485*4882a593Smuzhiyun 			if (info > 0) {
486*4882a593Smuzhiyun 				i_str = "bulk-average";
487*4882a593Smuzhiyun 				info = (pps+drop) / info; /* calc avg bulk */
488*4882a593Smuzhiyun 			}
489*4882a593Smuzhiyun 			if (err > 0)
490*4882a593Smuzhiyun 				err_str = "drv-err";
491*4882a593Smuzhiyun 			if (pps > 0 || drop > 0)
492*4882a593Smuzhiyun 				printf(fmt1, "devmap-xmit",
493*4882a593Smuzhiyun 				       i, pps, drop, info, i_str, err_str);
494*4882a593Smuzhiyun 		}
495*4882a593Smuzhiyun 		pps = calc_pps(&rec->total, &prev->total, t);
496*4882a593Smuzhiyun 		drop = calc_drop(&rec->total, &prev->total, t);
497*4882a593Smuzhiyun 		info = calc_info(&rec->total, &prev->total, t);
498*4882a593Smuzhiyun 		err  = calc_err(&rec->total, &prev->total, t);
499*4882a593Smuzhiyun 		if (info > 0) {
500*4882a593Smuzhiyun 			i_str = "bulk-average";
501*4882a593Smuzhiyun 			info = (pps+drop) / info; /* calc avg bulk */
502*4882a593Smuzhiyun 		}
503*4882a593Smuzhiyun 		if (err > 0)
504*4882a593Smuzhiyun 			err_str = "drv-err";
505*4882a593Smuzhiyun 		printf(fmt2, "devmap-xmit", "total", pps, drop,
506*4882a593Smuzhiyun 		       info, i_str, err_str);
507*4882a593Smuzhiyun 	}
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	printf("\n");
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun 
stats_collect(struct stats_record * rec)512*4882a593Smuzhiyun static bool stats_collect(struct stats_record *rec)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun 	int fd;
515*4882a593Smuzhiyun 	int i;
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	/* TODO: Detect if someone unloaded the perf event_fd's, as
518*4882a593Smuzhiyun 	 * this can happen by someone running perf-record -e
519*4882a593Smuzhiyun 	 */
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	fd = bpf_map__fd(map_data[REDIRECT_ERR_CNT]);
522*4882a593Smuzhiyun 	for (i = 0; i < REDIR_RES_MAX; i++)
523*4882a593Smuzhiyun 		map_collect_record_u64(fd, i, &rec->xdp_redirect[i]);
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	fd = bpf_map__fd(map_data[EXCEPTION_CNT]);
526*4882a593Smuzhiyun 	for (i = 0; i < XDP_ACTION_MAX; i++) {
527*4882a593Smuzhiyun 		map_collect_record_u64(fd, i, &rec->xdp_exception[i]);
528*4882a593Smuzhiyun 	}
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	fd = bpf_map__fd(map_data[CPUMAP_ENQUEUE_CNT]);
531*4882a593Smuzhiyun 	for (i = 0; i < MAX_CPUS; i++)
532*4882a593Smuzhiyun 		map_collect_record(fd, i, &rec->xdp_cpumap_enqueue[i]);
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	fd = bpf_map__fd(map_data[CPUMAP_KTHREAD_CNT]);
535*4882a593Smuzhiyun 	map_collect_record(fd, 0, &rec->xdp_cpumap_kthread);
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	fd = bpf_map__fd(map_data[DEVMAP_XMIT_CNT]);
538*4882a593Smuzhiyun 	map_collect_record(fd, 0, &rec->xdp_devmap_xmit);
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	return true;
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun 
alloc_rec_per_cpu(int record_size)543*4882a593Smuzhiyun static void *alloc_rec_per_cpu(int record_size)
544*4882a593Smuzhiyun {
545*4882a593Smuzhiyun 	unsigned int nr_cpus = bpf_num_possible_cpus();
546*4882a593Smuzhiyun 	void *array;
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	array = calloc(nr_cpus, record_size);
549*4882a593Smuzhiyun 	if (!array) {
550*4882a593Smuzhiyun 		fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
551*4882a593Smuzhiyun 		exit(EXIT_FAIL_MEM);
552*4882a593Smuzhiyun 	}
553*4882a593Smuzhiyun 	return array;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun 
alloc_stats_record(void)556*4882a593Smuzhiyun static struct stats_record *alloc_stats_record(void)
557*4882a593Smuzhiyun {
558*4882a593Smuzhiyun 	struct stats_record *rec;
559*4882a593Smuzhiyun 	int rec_sz;
560*4882a593Smuzhiyun 	int i;
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	/* Alloc main stats_record structure */
563*4882a593Smuzhiyun 	rec = calloc(1, sizeof(*rec));
564*4882a593Smuzhiyun 	if (!rec) {
565*4882a593Smuzhiyun 		fprintf(stderr, "Mem alloc error\n");
566*4882a593Smuzhiyun 		exit(EXIT_FAIL_MEM);
567*4882a593Smuzhiyun 	}
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	/* Alloc stats stored per CPU for each record */
570*4882a593Smuzhiyun 	rec_sz = sizeof(struct u64rec);
571*4882a593Smuzhiyun 	for (i = 0; i < REDIR_RES_MAX; i++)
572*4882a593Smuzhiyun 		rec->xdp_redirect[i].cpu = alloc_rec_per_cpu(rec_sz);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	for (i = 0; i < XDP_ACTION_MAX; i++)
575*4882a593Smuzhiyun 		rec->xdp_exception[i].cpu = alloc_rec_per_cpu(rec_sz);
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	rec_sz = sizeof(struct datarec);
578*4882a593Smuzhiyun 	rec->xdp_cpumap_kthread.cpu = alloc_rec_per_cpu(rec_sz);
579*4882a593Smuzhiyun 	rec->xdp_devmap_xmit.cpu    = alloc_rec_per_cpu(rec_sz);
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	for (i = 0; i < MAX_CPUS; i++)
582*4882a593Smuzhiyun 		rec->xdp_cpumap_enqueue[i].cpu = alloc_rec_per_cpu(rec_sz);
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	return rec;
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun 
free_stats_record(struct stats_record * r)587*4882a593Smuzhiyun static void free_stats_record(struct stats_record *r)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	int i;
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	for (i = 0; i < REDIR_RES_MAX; i++)
592*4882a593Smuzhiyun 		free(r->xdp_redirect[i].cpu);
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	for (i = 0; i < XDP_ACTION_MAX; i++)
595*4882a593Smuzhiyun 		free(r->xdp_exception[i].cpu);
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	free(r->xdp_cpumap_kthread.cpu);
598*4882a593Smuzhiyun 	free(r->xdp_devmap_xmit.cpu);
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	for (i = 0; i < MAX_CPUS; i++)
601*4882a593Smuzhiyun 		free(r->xdp_cpumap_enqueue[i].cpu);
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	free(r);
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun /* Pointer swap trick */
swap(struct stats_record ** a,struct stats_record ** b)607*4882a593Smuzhiyun static inline void swap(struct stats_record **a, struct stats_record **b)
608*4882a593Smuzhiyun {
609*4882a593Smuzhiyun 	struct stats_record *tmp;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	tmp = *a;
612*4882a593Smuzhiyun 	*a = *b;
613*4882a593Smuzhiyun 	*b = tmp;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun 
stats_poll(int interval,bool err_only)616*4882a593Smuzhiyun static void stats_poll(int interval, bool err_only)
617*4882a593Smuzhiyun {
618*4882a593Smuzhiyun 	struct stats_record *rec, *prev;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	rec  = alloc_stats_record();
621*4882a593Smuzhiyun 	prev = alloc_stats_record();
622*4882a593Smuzhiyun 	stats_collect(rec);
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	if (err_only)
625*4882a593Smuzhiyun 		printf("\n%s\n", __doc_err_only__);
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	/* Trick to pretty printf with thousands separators use %' */
628*4882a593Smuzhiyun 	setlocale(LC_NUMERIC, "en_US");
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	/* Header */
631*4882a593Smuzhiyun 	if (verbose)
632*4882a593Smuzhiyun 		printf("\n%s", __doc__);
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	/* TODO Need more advanced stats on error types */
635*4882a593Smuzhiyun 	if (verbose) {
636*4882a593Smuzhiyun 		printf(" - Stats map0: %s\n", bpf_map__name(map_data[0]));
637*4882a593Smuzhiyun 		printf(" - Stats map1: %s\n", bpf_map__name(map_data[1]));
638*4882a593Smuzhiyun 		printf("\n");
639*4882a593Smuzhiyun 	}
640*4882a593Smuzhiyun 	fflush(stdout);
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun 	while (1) {
643*4882a593Smuzhiyun 		swap(&prev, &rec);
644*4882a593Smuzhiyun 		stats_collect(rec);
645*4882a593Smuzhiyun 		stats_print(rec, prev, err_only);
646*4882a593Smuzhiyun 		fflush(stdout);
647*4882a593Smuzhiyun 		sleep(interval);
648*4882a593Smuzhiyun 	}
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	free_stats_record(rec);
651*4882a593Smuzhiyun 	free_stats_record(prev);
652*4882a593Smuzhiyun }
653*4882a593Smuzhiyun 
print_bpf_prog_info(void)654*4882a593Smuzhiyun static void print_bpf_prog_info(void)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun 	struct bpf_program *prog;
657*4882a593Smuzhiyun 	struct bpf_map *map;
658*4882a593Smuzhiyun 	int i = 0;
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	/* Prog info */
661*4882a593Smuzhiyun 	printf("Loaded BPF prog have %d bpf program(s)\n", tp_cnt);
662*4882a593Smuzhiyun 	bpf_object__for_each_program(prog, obj) {
663*4882a593Smuzhiyun 		printf(" - prog_fd[%d] = fd(%d)\n", i, bpf_program__fd(prog));
664*4882a593Smuzhiyun 		i++;
665*4882a593Smuzhiyun 	}
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	i = 0;
668*4882a593Smuzhiyun 	/* Maps info */
669*4882a593Smuzhiyun 	printf("Loaded BPF prog have %d map(s)\n", map_cnt);
670*4882a593Smuzhiyun 	bpf_object__for_each_map(map, obj) {
671*4882a593Smuzhiyun 		const char *name = bpf_map__name(map);
672*4882a593Smuzhiyun 		int fd		 = bpf_map__fd(map);
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 		printf(" - map_data[%d] = fd(%d) name:%s\n", i, fd, name);
675*4882a593Smuzhiyun 		i++;
676*4882a593Smuzhiyun 	}
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	/* Event info */
679*4882a593Smuzhiyun 	printf("Searching for (max:%d) event file descriptor(s)\n", tp_cnt);
680*4882a593Smuzhiyun 	for (i = 0; i < tp_cnt; i++) {
681*4882a593Smuzhiyun 		int fd = bpf_link__fd(tp_links[i]);
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 		if (fd != -1)
684*4882a593Smuzhiyun 			printf(" - event_fd[%d] = fd(%d)\n", i, fd);
685*4882a593Smuzhiyun 	}
686*4882a593Smuzhiyun }
687*4882a593Smuzhiyun 
main(int argc,char ** argv)688*4882a593Smuzhiyun int main(int argc, char **argv)
689*4882a593Smuzhiyun {
690*4882a593Smuzhiyun 	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
691*4882a593Smuzhiyun 	struct bpf_program *prog;
692*4882a593Smuzhiyun 	int longindex = 0, opt;
693*4882a593Smuzhiyun 	int ret = EXIT_FAILURE;
694*4882a593Smuzhiyun 	enum map_type type;
695*4882a593Smuzhiyun 	char filename[256];
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	/* Default settings: */
698*4882a593Smuzhiyun 	bool errors_only = true;
699*4882a593Smuzhiyun 	int interval = 2;
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 	/* Parse commands line args */
702*4882a593Smuzhiyun 	while ((opt = getopt_long(argc, argv, "hDSs:",
703*4882a593Smuzhiyun 				  long_options, &longindex)) != -1) {
704*4882a593Smuzhiyun 		switch (opt) {
705*4882a593Smuzhiyun 		case 'D':
706*4882a593Smuzhiyun 			debug = true;
707*4882a593Smuzhiyun 			break;
708*4882a593Smuzhiyun 		case 'S':
709*4882a593Smuzhiyun 			errors_only = false;
710*4882a593Smuzhiyun 			break;
711*4882a593Smuzhiyun 		case 's':
712*4882a593Smuzhiyun 			interval = atoi(optarg);
713*4882a593Smuzhiyun 			break;
714*4882a593Smuzhiyun 		case 'h':
715*4882a593Smuzhiyun 		default:
716*4882a593Smuzhiyun 			usage(argv);
717*4882a593Smuzhiyun 			return ret;
718*4882a593Smuzhiyun 		}
719*4882a593Smuzhiyun 	}
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun 	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
722*4882a593Smuzhiyun 	if (setrlimit(RLIMIT_MEMLOCK, &r)) {
723*4882a593Smuzhiyun 		perror("setrlimit(RLIMIT_MEMLOCK)");
724*4882a593Smuzhiyun 		return ret;
725*4882a593Smuzhiyun 	}
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun 	/* Remove tracepoint program when program is interrupted or killed */
728*4882a593Smuzhiyun 	signal(SIGINT, int_exit);
729*4882a593Smuzhiyun 	signal(SIGTERM, int_exit);
730*4882a593Smuzhiyun 
731*4882a593Smuzhiyun 	obj = bpf_object__open_file(filename, NULL);
732*4882a593Smuzhiyun 	if (libbpf_get_error(obj)) {
733*4882a593Smuzhiyun 		printf("ERROR: opening BPF object file failed\n");
734*4882a593Smuzhiyun 		obj = NULL;
735*4882a593Smuzhiyun 		goto cleanup;
736*4882a593Smuzhiyun 	}
737*4882a593Smuzhiyun 
738*4882a593Smuzhiyun 	/* load BPF program */
739*4882a593Smuzhiyun 	if (bpf_object__load(obj)) {
740*4882a593Smuzhiyun 		printf("ERROR: loading BPF object file failed\n");
741*4882a593Smuzhiyun 		goto cleanup;
742*4882a593Smuzhiyun 	}
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	for (type = 0; type < NUM_MAP; type++) {
745*4882a593Smuzhiyun 		map_data[type] =
746*4882a593Smuzhiyun 			bpf_object__find_map_by_name(obj, map_type_strings[type]);
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 		if (libbpf_get_error(map_data[type])) {
749*4882a593Smuzhiyun 			printf("ERROR: finding a map in obj file failed\n");
750*4882a593Smuzhiyun 			goto cleanup;
751*4882a593Smuzhiyun 		}
752*4882a593Smuzhiyun 		map_cnt++;
753*4882a593Smuzhiyun 	}
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	bpf_object__for_each_program(prog, obj) {
756*4882a593Smuzhiyun 		tp_links[tp_cnt] = bpf_program__attach(prog);
757*4882a593Smuzhiyun 		if (libbpf_get_error(tp_links[tp_cnt])) {
758*4882a593Smuzhiyun 			printf("ERROR: bpf_program__attach failed\n");
759*4882a593Smuzhiyun 			tp_links[tp_cnt] = NULL;
760*4882a593Smuzhiyun 			goto cleanup;
761*4882a593Smuzhiyun 		}
762*4882a593Smuzhiyun 		tp_cnt++;
763*4882a593Smuzhiyun 	}
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 	if (debug) {
766*4882a593Smuzhiyun 		print_bpf_prog_info();
767*4882a593Smuzhiyun 	}
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 	/* Unload/stop tracepoint event by closing bpf_link's */
770*4882a593Smuzhiyun 	if (errors_only) {
771*4882a593Smuzhiyun 		/* The bpf_link[i] depend on the order of
772*4882a593Smuzhiyun 		 * the functions was defined in _kern.c
773*4882a593Smuzhiyun 		 */
774*4882a593Smuzhiyun 		bpf_link__destroy(tp_links[2]);	/* tracepoint/xdp/xdp_redirect */
775*4882a593Smuzhiyun 		tp_links[2] = NULL;
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 		bpf_link__destroy(tp_links[3]);	/* tracepoint/xdp/xdp_redirect_map */
778*4882a593Smuzhiyun 		tp_links[3] = NULL;
779*4882a593Smuzhiyun 	}
780*4882a593Smuzhiyun 
781*4882a593Smuzhiyun 	stats_poll(interval, errors_only);
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	ret = EXIT_SUCCESS;
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun cleanup:
786*4882a593Smuzhiyun 	/* Detach tracepoints */
787*4882a593Smuzhiyun 	while (tp_cnt)
788*4882a593Smuzhiyun 		bpf_link__destroy(tp_links[--tp_cnt]);
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 	bpf_object__close(obj);
791*4882a593Smuzhiyun 	return ret;
792*4882a593Smuzhiyun }
793