1 // SPDX-License-Identifier: GPL-2.0
2 /* GPLv2, Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc. */
3 #include "xdp_sample.bpf.h"
5 #include <bpf/bpf_tracing.h>
6 #include <bpf/bpf_core_read.h>
7 #include <bpf/bpf_helpers.h>
9 array_map rx_cnt
SEC(".maps");
10 array_map redir_err_cnt
SEC(".maps");
11 array_map cpumap_enqueue_cnt
SEC(".maps");
12 array_map cpumap_kthread_cnt
SEC(".maps");
13 array_map exception_cnt
SEC(".maps");
14 array_map devmap_xmit_cnt
SEC(".maps");
17 __uint(type
, BPF_MAP_TYPE_PERCPU_HASH
);
18 __uint(max_entries
, 32 * 32);
20 __type(value
, struct datarec
);
21 } devmap_xmit_cnt_multi
SEC(".maps");
23 const volatile int nr_cpus
= 0;
25 /* These can be set before loading so that redundant comparisons can be DCE'd by
26 * the verifier, and only actual matches are tried after loading tp_btf program.
27 * This allows sample to filter tracepoint stats based on net_device.
29 const volatile int from_match
[32] = {};
30 const volatile int to_match
[32] = {};
32 int cpumap_map_id
= 0;
34 /* Find if b is part of set a, but if a is empty set then evaluate to true */
35 #define IN_SET(a, b) \
37 bool __res = !(a)[0]; \
38 for (int i = 0; i < ARRAY_SIZE(a) && (a)[i]; i++) { \
39 __res = (a)[i] == (b); \
46 static __always_inline __u32
xdp_get_err_key(int err
)
66 static __always_inline
int xdp_redirect_collect_stat(int from
, int err
)
68 u32 cpu
= bpf_get_smp_processor_id();
69 u32 key
= XDP_REDIRECT_ERROR
;
73 if (!IN_SET(from_match
, from
))
76 key
= xdp_get_err_key(err
);
78 idx
= key
* nr_cpus
+ cpu
;
79 rec
= bpf_map_lookup_elem(&redir_err_cnt
, &idx
);
83 NO_TEAR_INC(rec
->dropped
);
85 NO_TEAR_INC(rec
->processed
);
86 return 0; /* Indicate event was filtered (no further processing)*/
88 * Returning 1 here would allow e.g. a perf-record tracepoint
89 * to see and record these events, but it doesn't work well
90 * in-practice as stopping perf-record also unload this
91 * bpf_prog. Plus, there is additional overhead of doing so.
95 SEC("tp_btf/xdp_redirect_err")
96 int BPF_PROG(tp_xdp_redirect_err
, const struct net_device
*dev
,
97 const struct bpf_prog
*xdp
, const void *tgt
, int err
,
98 const struct bpf_map
*map
, u32 index
)
100 return xdp_redirect_collect_stat(dev
->ifindex
, err
);
103 SEC("tp_btf/xdp_redirect_map_err")
104 int BPF_PROG(tp_xdp_redirect_map_err
, const struct net_device
*dev
,
105 const struct bpf_prog
*xdp
, const void *tgt
, int err
,
106 const struct bpf_map
*map
, u32 index
)
108 return xdp_redirect_collect_stat(dev
->ifindex
, err
);
111 SEC("tp_btf/xdp_redirect")
112 int BPF_PROG(tp_xdp_redirect
, const struct net_device
*dev
,
113 const struct bpf_prog
*xdp
, const void *tgt
, int err
,
114 const struct bpf_map
*map
, u32 index
)
116 return xdp_redirect_collect_stat(dev
->ifindex
, err
);
119 SEC("tp_btf/xdp_redirect_map")
120 int BPF_PROG(tp_xdp_redirect_map
, const struct net_device
*dev
,
121 const struct bpf_prog
*xdp
, const void *tgt
, int err
,
122 const struct bpf_map
*map
, u32 index
)
124 return xdp_redirect_collect_stat(dev
->ifindex
, err
);
127 SEC("tp_btf/xdp_cpumap_enqueue")
128 int BPF_PROG(tp_xdp_cpumap_enqueue
, int map_id
, unsigned int processed
,
129 unsigned int drops
, int to_cpu
)
131 u32 cpu
= bpf_get_smp_processor_id();
135 if (cpumap_map_id
&& cpumap_map_id
!= map_id
)
138 idx
= to_cpu
* nr_cpus
+ cpu
;
139 rec
= bpf_map_lookup_elem(&cpumap_enqueue_cnt
, &idx
);
142 NO_TEAR_ADD(rec
->processed
, processed
);
143 NO_TEAR_ADD(rec
->dropped
, drops
);
144 /* Record bulk events, then userspace can calc average bulk size */
146 NO_TEAR_INC(rec
->issue
);
147 /* Inception: It's possible to detect overload situations, via
148 * this tracepoint. This can be used for creating a feedback
149 * loop to XDP, which can take appropriate actions to mitigate
150 * this overload situation.
155 SEC("tp_btf/xdp_cpumap_kthread")
156 int BPF_PROG(tp_xdp_cpumap_kthread
, int map_id
, unsigned int processed
,
157 unsigned int drops
, int sched
, struct xdp_cpumap_stats
*xdp_stats
)
162 if (cpumap_map_id
&& cpumap_map_id
!= map_id
)
165 cpu
= bpf_get_smp_processor_id();
166 rec
= bpf_map_lookup_elem(&cpumap_kthread_cnt
, &cpu
);
169 NO_TEAR_ADD(rec
->processed
, processed
);
170 NO_TEAR_ADD(rec
->dropped
, drops
);
171 NO_TEAR_ADD(rec
->xdp_pass
, xdp_stats
->pass
);
172 NO_TEAR_ADD(rec
->xdp_drop
, xdp_stats
->drop
);
173 NO_TEAR_ADD(rec
->xdp_redirect
, xdp_stats
->redirect
);
174 /* Count times kthread yielded CPU via schedule call */
176 NO_TEAR_INC(rec
->issue
);
180 SEC("tp_btf/xdp_exception")
181 int BPF_PROG(tp_xdp_exception
, const struct net_device
*dev
,
182 const struct bpf_prog
*xdp
, u32 act
)
184 u32 cpu
= bpf_get_smp_processor_id();
188 if (!IN_SET(from_match
, dev
->ifindex
))
190 if (!IN_SET(to_match
, dev
->ifindex
))
193 if (key
> XDP_REDIRECT
)
194 key
= XDP_REDIRECT
+ 1;
196 idx
= key
* nr_cpus
+ cpu
;
197 rec
= bpf_map_lookup_elem(&exception_cnt
, &idx
);
200 NO_TEAR_INC(rec
->dropped
);
205 SEC("tp_btf/xdp_devmap_xmit")
206 int BPF_PROG(tp_xdp_devmap_xmit
, const struct net_device
*from_dev
,
207 const struct net_device
*to_dev
, int sent
, int drops
, int err
)
213 idx_in
= from_dev
->ifindex
;
214 idx_out
= to_dev
->ifindex
;
216 if (!IN_SET(from_match
, idx_in
))
218 if (!IN_SET(to_match
, idx_out
))
221 cpu
= bpf_get_smp_processor_id();
222 rec
= bpf_map_lookup_elem(&devmap_xmit_cnt
, &cpu
);
225 NO_TEAR_ADD(rec
->processed
, sent
);
226 NO_TEAR_ADD(rec
->dropped
, drops
);
227 /* Record bulk events, then userspace can calc average bulk size */
228 NO_TEAR_INC(rec
->info
);
229 /* Record error cases, where no frame were sent */
230 /* Catch API error of drv ndo_xdp_xmit sent more than count */
231 if (err
|| drops
< 0)
232 NO_TEAR_INC(rec
->issue
);
236 SEC("tp_btf/xdp_devmap_xmit")
237 int BPF_PROG(tp_xdp_devmap_xmit_multi
, const struct net_device
*from_dev
,
238 const struct net_device
*to_dev
, int sent
, int drops
, int err
)
240 struct datarec empty
= {};
245 idx_in
= from_dev
->ifindex
;
246 idx_out
= to_dev
->ifindex
;
248 idx
= idx
<< 32 | idx_out
;
250 if (!IN_SET(from_match
, idx_in
))
252 if (!IN_SET(to_match
, idx_out
))
255 bpf_map_update_elem(&devmap_xmit_cnt_multi
, &idx
, &empty
, BPF_NOEXIST
);
256 rec
= bpf_map_lookup_elem(&devmap_xmit_cnt_multi
, &idx
);
260 NO_TEAR_ADD(rec
->processed
, sent
);
261 NO_TEAR_ADD(rec
->dropped
, drops
);
262 NO_TEAR_INC(rec
->info
);
263 if (err
|| drops
< 0)
264 NO_TEAR_INC(rec
->issue
);