18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci * Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Example howto extract XDP RX-queue info
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <uapi/linux/bpf.h>
78c2ecf20Sopenharmony_ci#include <uapi/linux/if_ether.h>
88c2ecf20Sopenharmony_ci#include <uapi/linux/in.h>
98c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/* Config setup from with userspace
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * User-side setup ifindex in config_map, to verify that
148c2ecf20Sopenharmony_ci * ctx->ingress_ifindex is correct (against configured ifindex)
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_cistruct config {
178c2ecf20Sopenharmony_ci	__u32 action;
188c2ecf20Sopenharmony_ci	int ifindex;
198c2ecf20Sopenharmony_ci	__u32 options;
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_cienum cfg_options_flags {
228c2ecf20Sopenharmony_ci	NO_TOUCH = 0x0U,
238c2ecf20Sopenharmony_ci	READ_MEM = 0x1U,
248c2ecf20Sopenharmony_ci	SWAP_MAC = 0x2U,
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct {
288c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_ARRAY);
298c2ecf20Sopenharmony_ci	__type(key, int);
308c2ecf20Sopenharmony_ci	__type(value, struct config);
318c2ecf20Sopenharmony_ci	__uint(max_entries, 1);
328c2ecf20Sopenharmony_ci} config_map SEC(".maps");
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Common stats data record (shared with userspace) */
358c2ecf20Sopenharmony_cistruct datarec {
368c2ecf20Sopenharmony_ci	__u64 processed;
378c2ecf20Sopenharmony_ci	__u64 issue;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct {
418c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
428c2ecf20Sopenharmony_ci	__type(key, u32);
438c2ecf20Sopenharmony_ci	__type(value, struct datarec);
448c2ecf20Sopenharmony_ci	__uint(max_entries, 1);
458c2ecf20Sopenharmony_ci} stats_global_map SEC(".maps");
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define MAX_RXQs 64
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Stats per rx_queue_index (per CPU) */
508c2ecf20Sopenharmony_cistruct {
518c2ecf20Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
528c2ecf20Sopenharmony_ci	__type(key, u32);
538c2ecf20Sopenharmony_ci	__type(value, struct datarec);
548c2ecf20Sopenharmony_ci	__uint(max_entries, MAX_RXQs + 1);
558c2ecf20Sopenharmony_ci} rx_queue_index_map SEC(".maps");
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic __always_inline
588c2ecf20Sopenharmony_civoid swap_src_dst_mac(void *data)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	unsigned short *p = data;
618c2ecf20Sopenharmony_ci	unsigned short dst[3];
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	dst[0] = p[0];
648c2ecf20Sopenharmony_ci	dst[1] = p[1];
658c2ecf20Sopenharmony_ci	dst[2] = p[2];
668c2ecf20Sopenharmony_ci	p[0] = p[3];
678c2ecf20Sopenharmony_ci	p[1] = p[4];
688c2ecf20Sopenharmony_ci	p[2] = p[5];
698c2ecf20Sopenharmony_ci	p[3] = dst[0];
708c2ecf20Sopenharmony_ci	p[4] = dst[1];
718c2ecf20Sopenharmony_ci	p[5] = dst[2];
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciSEC("xdp_prog0")
758c2ecf20Sopenharmony_ciint  xdp_prognum0(struct xdp_md *ctx)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	void *data_end = (void *)(long)ctx->data_end;
788c2ecf20Sopenharmony_ci	void *data     = (void *)(long)ctx->data;
798c2ecf20Sopenharmony_ci	struct datarec *rec, *rxq_rec;
808c2ecf20Sopenharmony_ci	int ingress_ifindex;
818c2ecf20Sopenharmony_ci	struct config *config;
828c2ecf20Sopenharmony_ci	u32 key = 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* Global stats record */
858c2ecf20Sopenharmony_ci	rec = bpf_map_lookup_elem(&stats_global_map, &key);
868c2ecf20Sopenharmony_ci	if (!rec)
878c2ecf20Sopenharmony_ci		return XDP_ABORTED;
888c2ecf20Sopenharmony_ci	rec->processed++;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Accessing ctx->ingress_ifindex, cause BPF to rewrite BPF
918c2ecf20Sopenharmony_ci	 * instructions inside kernel to access xdp_rxq->dev->ifindex
928c2ecf20Sopenharmony_ci	 */
938c2ecf20Sopenharmony_ci	ingress_ifindex = ctx->ingress_ifindex;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	config = bpf_map_lookup_elem(&config_map, &key);
968c2ecf20Sopenharmony_ci	if (!config)
978c2ecf20Sopenharmony_ci		return XDP_ABORTED;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* Simple test: check ctx provided ifindex is as expected */
1008c2ecf20Sopenharmony_ci	if (ingress_ifindex != config->ifindex) {
1018c2ecf20Sopenharmony_ci		/* count this error case */
1028c2ecf20Sopenharmony_ci		rec->issue++;
1038c2ecf20Sopenharmony_ci		return XDP_ABORTED;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Update stats per rx_queue_index. Handle if rx_queue_index
1078c2ecf20Sopenharmony_ci	 * is larger than stats map can contain info for.
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	key = ctx->rx_queue_index;
1108c2ecf20Sopenharmony_ci	if (key >= MAX_RXQs)
1118c2ecf20Sopenharmony_ci		key = MAX_RXQs;
1128c2ecf20Sopenharmony_ci	rxq_rec = bpf_map_lookup_elem(&rx_queue_index_map, &key);
1138c2ecf20Sopenharmony_ci	if (!rxq_rec)
1148c2ecf20Sopenharmony_ci		return XDP_ABORTED;
1158c2ecf20Sopenharmony_ci	rxq_rec->processed++;
1168c2ecf20Sopenharmony_ci	if (key == MAX_RXQs)
1178c2ecf20Sopenharmony_ci		rxq_rec->issue++;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* Default: Don't touch packet data, only count packets */
1208c2ecf20Sopenharmony_ci	if (unlikely(config->options & (READ_MEM|SWAP_MAC))) {
1218c2ecf20Sopenharmony_ci		struct ethhdr *eth = data;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		if (eth + 1 > data_end)
1248c2ecf20Sopenharmony_ci			return XDP_ABORTED;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		/* Avoid compiler removing this: Drop non 802.3 Ethertypes */
1278c2ecf20Sopenharmony_ci		if (ntohs(eth->h_proto) < ETH_P_802_3_MIN)
1288c2ecf20Sopenharmony_ci			return XDP_ABORTED;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		/* XDP_TX requires changing MAC-addrs, else HW may drop.
1318c2ecf20Sopenharmony_ci		 * Can also be enabled with --swapmac (for test purposes)
1328c2ecf20Sopenharmony_ci		 */
1338c2ecf20Sopenharmony_ci		if (unlikely(config->options & SWAP_MAC))
1348c2ecf20Sopenharmony_ci			swap_src_dst_mac(data);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	return config->action;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cichar _license[] SEC("license") = "GPL";
141