xref: /OK3568_Linux_fs/kernel/samples/bpf/xdp_rxq_info_kern.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  *  Example howto extract XDP RX-queue info
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun #include <uapi/linux/bpf.h>
7*4882a593Smuzhiyun #include <uapi/linux/if_ether.h>
8*4882a593Smuzhiyun #include <uapi/linux/in.h>
9*4882a593Smuzhiyun #include <bpf/bpf_helpers.h>
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun /* Config setup from with userspace
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * User-side setup ifindex in config_map, to verify that
14*4882a593Smuzhiyun  * ctx->ingress_ifindex is correct (against configured ifindex)
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun struct config {
17*4882a593Smuzhiyun 	__u32 action;
18*4882a593Smuzhiyun 	int ifindex;
19*4882a593Smuzhiyun 	__u32 options;
20*4882a593Smuzhiyun };
21*4882a593Smuzhiyun enum cfg_options_flags {
22*4882a593Smuzhiyun 	NO_TOUCH = 0x0U,
23*4882a593Smuzhiyun 	READ_MEM = 0x1U,
24*4882a593Smuzhiyun 	SWAP_MAC = 0x2U,
25*4882a593Smuzhiyun };
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun struct {
28*4882a593Smuzhiyun 	__uint(type, BPF_MAP_TYPE_ARRAY);
29*4882a593Smuzhiyun 	__type(key, int);
30*4882a593Smuzhiyun 	__type(value, struct config);
31*4882a593Smuzhiyun 	__uint(max_entries, 1);
32*4882a593Smuzhiyun } config_map SEC(".maps");
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun /* Common stats data record (shared with userspace) */
35*4882a593Smuzhiyun struct datarec {
36*4882a593Smuzhiyun 	__u64 processed;
37*4882a593Smuzhiyun 	__u64 issue;
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun struct {
41*4882a593Smuzhiyun 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
42*4882a593Smuzhiyun 	__type(key, u32);
43*4882a593Smuzhiyun 	__type(value, struct datarec);
44*4882a593Smuzhiyun 	__uint(max_entries, 1);
45*4882a593Smuzhiyun } stats_global_map SEC(".maps");
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #define MAX_RXQs 64
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun /* Stats per rx_queue_index (per CPU) */
50*4882a593Smuzhiyun struct {
51*4882a593Smuzhiyun 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
52*4882a593Smuzhiyun 	__type(key, u32);
53*4882a593Smuzhiyun 	__type(value, struct datarec);
54*4882a593Smuzhiyun 	__uint(max_entries, MAX_RXQs + 1);
55*4882a593Smuzhiyun } rx_queue_index_map SEC(".maps");
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun static __always_inline
swap_src_dst_mac(void * data)58*4882a593Smuzhiyun void swap_src_dst_mac(void *data)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun 	unsigned short *p = data;
61*4882a593Smuzhiyun 	unsigned short dst[3];
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	dst[0] = p[0];
64*4882a593Smuzhiyun 	dst[1] = p[1];
65*4882a593Smuzhiyun 	dst[2] = p[2];
66*4882a593Smuzhiyun 	p[0] = p[3];
67*4882a593Smuzhiyun 	p[1] = p[4];
68*4882a593Smuzhiyun 	p[2] = p[5];
69*4882a593Smuzhiyun 	p[3] = dst[0];
70*4882a593Smuzhiyun 	p[4] = dst[1];
71*4882a593Smuzhiyun 	p[5] = dst[2];
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun SEC("xdp_prog0")
xdp_prognum0(struct xdp_md * ctx)75*4882a593Smuzhiyun int  xdp_prognum0(struct xdp_md *ctx)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	void *data_end = (void *)(long)ctx->data_end;
78*4882a593Smuzhiyun 	void *data     = (void *)(long)ctx->data;
79*4882a593Smuzhiyun 	struct datarec *rec, *rxq_rec;
80*4882a593Smuzhiyun 	int ingress_ifindex;
81*4882a593Smuzhiyun 	struct config *config;
82*4882a593Smuzhiyun 	u32 key = 0;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	/* Global stats record */
85*4882a593Smuzhiyun 	rec = bpf_map_lookup_elem(&stats_global_map, &key);
86*4882a593Smuzhiyun 	if (!rec)
87*4882a593Smuzhiyun 		return XDP_ABORTED;
88*4882a593Smuzhiyun 	rec->processed++;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	/* Accessing ctx->ingress_ifindex, cause BPF to rewrite BPF
91*4882a593Smuzhiyun 	 * instructions inside kernel to access xdp_rxq->dev->ifindex
92*4882a593Smuzhiyun 	 */
93*4882a593Smuzhiyun 	ingress_ifindex = ctx->ingress_ifindex;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	config = bpf_map_lookup_elem(&config_map, &key);
96*4882a593Smuzhiyun 	if (!config)
97*4882a593Smuzhiyun 		return XDP_ABORTED;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	/* Simple test: check ctx provided ifindex is as expected */
100*4882a593Smuzhiyun 	if (ingress_ifindex != config->ifindex) {
101*4882a593Smuzhiyun 		/* count this error case */
102*4882a593Smuzhiyun 		rec->issue++;
103*4882a593Smuzhiyun 		return XDP_ABORTED;
104*4882a593Smuzhiyun 	}
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	/* Update stats per rx_queue_index. Handle if rx_queue_index
107*4882a593Smuzhiyun 	 * is larger than stats map can contain info for.
108*4882a593Smuzhiyun 	 */
109*4882a593Smuzhiyun 	key = ctx->rx_queue_index;
110*4882a593Smuzhiyun 	if (key >= MAX_RXQs)
111*4882a593Smuzhiyun 		key = MAX_RXQs;
112*4882a593Smuzhiyun 	rxq_rec = bpf_map_lookup_elem(&rx_queue_index_map, &key);
113*4882a593Smuzhiyun 	if (!rxq_rec)
114*4882a593Smuzhiyun 		return XDP_ABORTED;
115*4882a593Smuzhiyun 	rxq_rec->processed++;
116*4882a593Smuzhiyun 	if (key == MAX_RXQs)
117*4882a593Smuzhiyun 		rxq_rec->issue++;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	/* Default: Don't touch packet data, only count packets */
120*4882a593Smuzhiyun 	if (unlikely(config->options & (READ_MEM|SWAP_MAC))) {
121*4882a593Smuzhiyun 		struct ethhdr *eth = data;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 		if (eth + 1 > data_end)
124*4882a593Smuzhiyun 			return XDP_ABORTED;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 		/* Avoid compiler removing this: Drop non 802.3 Ethertypes */
127*4882a593Smuzhiyun 		if (ntohs(eth->h_proto) < ETH_P_802_3_MIN)
128*4882a593Smuzhiyun 			return XDP_ABORTED;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 		/* XDP_TX requires changing MAC-addrs, else HW may drop.
131*4882a593Smuzhiyun 		 * Can also be enabled with --swapmac (for test purposes)
132*4882a593Smuzhiyun 		 */
133*4882a593Smuzhiyun 		if (unlikely(config->options & SWAP_MAC))
134*4882a593Smuzhiyun 			swap_src_dst_mac(data);
135*4882a593Smuzhiyun 	}
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	return config->action;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun char _license[] SEC("license") = "GPL";
141