18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci *  Copyright(c) 2017-2018 Jesper Dangaard Brouer, Red Hat Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * XDP monitor tool, based on tracepoints
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <uapi/linux/bpf.h>
78c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_cistruct {
108c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
118c2ecf20Sopenharmony_ci	__type(key, u32);
128c2ecf20Sopenharmony_ci	__type(value, u64);
138c2ecf20Sopenharmony_ci	__uint(max_entries, 2);
148c2ecf20Sopenharmony_ci	/* TODO: have entries for all possible errno's */
158c2ecf20Sopenharmony_ci} redirect_err_cnt SEC(".maps");
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define XDP_UNKNOWN	XDP_REDIRECT + 1
188c2ecf20Sopenharmony_cistruct {
198c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
208c2ecf20Sopenharmony_ci	__type(key, u32);
218c2ecf20Sopenharmony_ci	__type(value, u64);
228c2ecf20Sopenharmony_ci	__uint(max_entries, XDP_UNKNOWN + 1);
238c2ecf20Sopenharmony_ci} exception_cnt SEC(".maps");
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_redirect/format
268c2ecf20Sopenharmony_ci * Code in:                kernel/include/trace/events/xdp.h
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_cistruct xdp_redirect_ctx {
298c2ecf20Sopenharmony_ci	u64 __pad;		// First 8 bytes are not accessible by bpf code
308c2ecf20Sopenharmony_ci	int prog_id;		//	offset:8;  size:4; signed:1;
318c2ecf20Sopenharmony_ci	u32 act;		//	offset:12  size:4; signed:0;
328c2ecf20Sopenharmony_ci	int ifindex;		//	offset:16  size:4; signed:1;
338c2ecf20Sopenharmony_ci	int err;		//	offset:20  size:4; signed:1;
348c2ecf20Sopenharmony_ci	int to_ifindex;		//	offset:24  size:4; signed:1;
358c2ecf20Sopenharmony_ci	u32 map_id;		//	offset:28  size:4; signed:0;
368c2ecf20Sopenharmony_ci	int map_index;		//	offset:32  size:4; signed:1;
378c2ecf20Sopenharmony_ci};				//	offset:36
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cienum {
408c2ecf20Sopenharmony_ci	XDP_REDIRECT_SUCCESS = 0,
418c2ecf20Sopenharmony_ci	XDP_REDIRECT_ERROR = 1
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic __always_inline
458c2ecf20Sopenharmony_ciint xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	u32 key = XDP_REDIRECT_ERROR;
488c2ecf20Sopenharmony_ci	int err = ctx->err;
498c2ecf20Sopenharmony_ci	u64 *cnt;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (!err)
528c2ecf20Sopenharmony_ci		key = XDP_REDIRECT_SUCCESS;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	cnt  = bpf_map_lookup_elem(&redirect_err_cnt, &key);
558c2ecf20Sopenharmony_ci	if (!cnt)
568c2ecf20Sopenharmony_ci		return 1;
578c2ecf20Sopenharmony_ci	*cnt += 1;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return 0; /* Indicate event was filtered (no further processing)*/
608c2ecf20Sopenharmony_ci	/*
618c2ecf20Sopenharmony_ci	 * Returning 1 here would allow e.g. a perf-record tracepoint
628c2ecf20Sopenharmony_ci	 * to see and record these events, but it doesn't work well
638c2ecf20Sopenharmony_ci	 * in-practice as stopping perf-record also unload this
648c2ecf20Sopenharmony_ci	 * bpf_prog.  Plus, there is additional overhead of doing so.
658c2ecf20Sopenharmony_ci	 */
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ciSEC("tracepoint/xdp/xdp_redirect_err")
698c2ecf20Sopenharmony_ciint trace_xdp_redirect_err(struct xdp_redirect_ctx *ctx)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	return xdp_redirect_collect_stat(ctx);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ciSEC("tracepoint/xdp/xdp_redirect_map_err")
768c2ecf20Sopenharmony_ciint trace_xdp_redirect_map_err(struct xdp_redirect_ctx *ctx)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	return xdp_redirect_collect_stat(ctx);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* Likely unloaded when prog starts */
828c2ecf20Sopenharmony_ciSEC("tracepoint/xdp/xdp_redirect")
838c2ecf20Sopenharmony_ciint trace_xdp_redirect(struct xdp_redirect_ctx *ctx)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	return xdp_redirect_collect_stat(ctx);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* Likely unloaded when prog starts */
898c2ecf20Sopenharmony_ciSEC("tracepoint/xdp/xdp_redirect_map")
908c2ecf20Sopenharmony_ciint trace_xdp_redirect_map(struct xdp_redirect_ctx *ctx)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	return xdp_redirect_collect_stat(ctx);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_exception/format
968c2ecf20Sopenharmony_ci * Code in:                kernel/include/trace/events/xdp.h
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_cistruct xdp_exception_ctx {
998c2ecf20Sopenharmony_ci	u64 __pad;	// First 8 bytes are not accessible by bpf code
1008c2ecf20Sopenharmony_ci	int prog_id;	//	offset:8;  size:4; signed:1;
1018c2ecf20Sopenharmony_ci	u32 act;	//	offset:12; size:4; signed:0;
1028c2ecf20Sopenharmony_ci	int ifindex;	//	offset:16; size:4; signed:1;
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ciSEC("tracepoint/xdp/xdp_exception")
1068c2ecf20Sopenharmony_ciint trace_xdp_exception(struct xdp_exception_ctx *ctx)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	u64 *cnt;
1098c2ecf20Sopenharmony_ci	u32 key;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	key = ctx->act;
1128c2ecf20Sopenharmony_ci	if (key > XDP_REDIRECT)
1138c2ecf20Sopenharmony_ci		key = XDP_UNKNOWN;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	cnt = bpf_map_lookup_elem(&exception_cnt, &key);
1168c2ecf20Sopenharmony_ci	if (!cnt)
1178c2ecf20Sopenharmony_ci		return 1;
1188c2ecf20Sopenharmony_ci	*cnt += 1;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* Common stats data record shared with _user.c */
1248c2ecf20Sopenharmony_cistruct datarec {
1258c2ecf20Sopenharmony_ci	u64 processed;
1268c2ecf20Sopenharmony_ci	u64 dropped;
1278c2ecf20Sopenharmony_ci	u64 info;
1288c2ecf20Sopenharmony_ci	u64 err;
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci#define MAX_CPUS 64
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistruct {
1338c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
1348c2ecf20Sopenharmony_ci	__type(key, u32);
1358c2ecf20Sopenharmony_ci	__type(value, struct datarec);
1368c2ecf20Sopenharmony_ci	__uint(max_entries, MAX_CPUS);
1378c2ecf20Sopenharmony_ci} cpumap_enqueue_cnt SEC(".maps");
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistruct {
1408c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
1418c2ecf20Sopenharmony_ci	__type(key, u32);
1428c2ecf20Sopenharmony_ci	__type(value, struct datarec);
1438c2ecf20Sopenharmony_ci	__uint(max_entries, 1);
1448c2ecf20Sopenharmony_ci} cpumap_kthread_cnt SEC(".maps");
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_enqueue/format
1478c2ecf20Sopenharmony_ci * Code in:         kernel/include/trace/events/xdp.h
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistruct cpumap_enqueue_ctx {
1508c2ecf20Sopenharmony_ci	u64 __pad;		// First 8 bytes are not accessible by bpf code
1518c2ecf20Sopenharmony_ci	int map_id;		//	offset:8;  size:4; signed:1;
1528c2ecf20Sopenharmony_ci	u32 act;		//	offset:12; size:4; signed:0;
1538c2ecf20Sopenharmony_ci	int cpu;		//	offset:16; size:4; signed:1;
1548c2ecf20Sopenharmony_ci	unsigned int drops;	//	offset:20; size:4; signed:0;
1558c2ecf20Sopenharmony_ci	unsigned int processed;	//	offset:24; size:4; signed:0;
1568c2ecf20Sopenharmony_ci	int to_cpu;		//	offset:28; size:4; signed:1;
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ciSEC("tracepoint/xdp/xdp_cpumap_enqueue")
1608c2ecf20Sopenharmony_ciint trace_xdp_cpumap_enqueue(struct cpumap_enqueue_ctx *ctx)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	u32 to_cpu = ctx->to_cpu;
1638c2ecf20Sopenharmony_ci	struct datarec *rec;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (to_cpu >= MAX_CPUS)
1668c2ecf20Sopenharmony_ci		return 1;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	rec = bpf_map_lookup_elem(&cpumap_enqueue_cnt, &to_cpu);
1698c2ecf20Sopenharmony_ci	if (!rec)
1708c2ecf20Sopenharmony_ci		return 0;
1718c2ecf20Sopenharmony_ci	rec->processed += ctx->processed;
1728c2ecf20Sopenharmony_ci	rec->dropped   += ctx->drops;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* Record bulk events, then userspace can calc average bulk size */
1758c2ecf20Sopenharmony_ci	if (ctx->processed > 0)
1768c2ecf20Sopenharmony_ci		rec->info += 1;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_kthread/format
1828c2ecf20Sopenharmony_ci * Code in:         kernel/include/trace/events/xdp.h
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_cistruct cpumap_kthread_ctx {
1858c2ecf20Sopenharmony_ci	u64 __pad;		// First 8 bytes are not accessible by bpf code
1868c2ecf20Sopenharmony_ci	int map_id;		//	offset:8;  size:4; signed:1;
1878c2ecf20Sopenharmony_ci	u32 act;		//	offset:12; size:4; signed:0;
1888c2ecf20Sopenharmony_ci	int cpu;		//	offset:16; size:4; signed:1;
1898c2ecf20Sopenharmony_ci	unsigned int drops;	//	offset:20; size:4; signed:0;
1908c2ecf20Sopenharmony_ci	unsigned int processed;	//	offset:24; size:4; signed:0;
1918c2ecf20Sopenharmony_ci	int sched;		//	offset:28; size:4; signed:1;
1928c2ecf20Sopenharmony_ci};
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ciSEC("tracepoint/xdp/xdp_cpumap_kthread")
1958c2ecf20Sopenharmony_ciint trace_xdp_cpumap_kthread(struct cpumap_kthread_ctx *ctx)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct datarec *rec;
1988c2ecf20Sopenharmony_ci	u32 key = 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	rec = bpf_map_lookup_elem(&cpumap_kthread_cnt, &key);
2018c2ecf20Sopenharmony_ci	if (!rec)
2028c2ecf20Sopenharmony_ci		return 0;
2038c2ecf20Sopenharmony_ci	rec->processed += ctx->processed;
2048c2ecf20Sopenharmony_ci	rec->dropped   += ctx->drops;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Count times kthread yielded CPU via schedule call */
2078c2ecf20Sopenharmony_ci	if (ctx->sched)
2088c2ecf20Sopenharmony_ci		rec->info++;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return 0;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistruct {
2148c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
2158c2ecf20Sopenharmony_ci	__type(key, u32);
2168c2ecf20Sopenharmony_ci	__type(value, struct datarec);
2178c2ecf20Sopenharmony_ci	__uint(max_entries, 1);
2188c2ecf20Sopenharmony_ci} devmap_xmit_cnt SEC(".maps");
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_devmap_xmit/format
2218c2ecf20Sopenharmony_ci * Code in:         kernel/include/trace/events/xdp.h
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cistruct devmap_xmit_ctx {
2248c2ecf20Sopenharmony_ci	u64 __pad;		// First 8 bytes are not accessible by bpf code
2258c2ecf20Sopenharmony_ci	int from_ifindex;	//	offset:8;  size:4; signed:1;
2268c2ecf20Sopenharmony_ci	u32 act;		//	offset:12; size:4; signed:0;
2278c2ecf20Sopenharmony_ci	int to_ifindex; 	//	offset:16; size:4; signed:1;
2288c2ecf20Sopenharmony_ci	int drops;		//	offset:20; size:4; signed:1;
2298c2ecf20Sopenharmony_ci	int sent;		//	offset:24; size:4; signed:1;
2308c2ecf20Sopenharmony_ci	int err;		//	offset:28; size:4; signed:1;
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ciSEC("tracepoint/xdp/xdp_devmap_xmit")
2348c2ecf20Sopenharmony_ciint trace_xdp_devmap_xmit(struct devmap_xmit_ctx *ctx)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct datarec *rec;
2378c2ecf20Sopenharmony_ci	u32 key = 0;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	rec = bpf_map_lookup_elem(&devmap_xmit_cnt, &key);
2408c2ecf20Sopenharmony_ci	if (!rec)
2418c2ecf20Sopenharmony_ci		return 0;
2428c2ecf20Sopenharmony_ci	rec->processed += ctx->sent;
2438c2ecf20Sopenharmony_ci	rec->dropped   += ctx->drops;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* Record bulk events, then userspace can calc average bulk size */
2468c2ecf20Sopenharmony_ci	rec->info += 1;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* Record error cases, where no frame were sent */
2498c2ecf20Sopenharmony_ci	if (ctx->err)
2508c2ecf20Sopenharmony_ci		rec->err++;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* Catch API error of drv ndo_xdp_xmit sent more than count */
2538c2ecf20Sopenharmony_ci	if (ctx->drops < 0)
2548c2ecf20Sopenharmony_ci		rec->err++;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return 1;
2578c2ecf20Sopenharmony_ci}
258