diff mbox series

[bpf-next,v2,3/8] samples: bpf: Add BPF support for XDP samples helper

Message ID 20210721212833.701342-4-memxor@gmail.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Improve XDP samples usability and output | expand

Checks

Context Check Description
netdev/apply fail Patch does not apply to bpf-next
netdev/tree_selection success Clearly marked for bpf-next

Commit Message

Kumar Kartikeya Dwivedi July 21, 2021, 9:28 p.m. UTC
These eBPF tracepoint programs export data that is consumed by the
helpers added in the previous commit.

Also add support int the Makefile to generate a vmlinux.h header that
would be used by other bpf.c files in later commits.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/Makefile         |  25 ++++
 samples/bpf/xdp_sample.bpf.c | 215 +++++++++++++++++++++++++++++++++++
 samples/bpf/xdp_sample.bpf.h |  57 ++++++++++
 3 files changed, 297 insertions(+)
 create mode 100644 samples/bpf/xdp_sample.bpf.c
 create mode 100644 samples/bpf/xdp_sample.bpf.h

Comments

Andrii Nakryiko July 23, 2021, 3:42 a.m. UTC | #1
On Wed, Jul 21, 2021 at 2:28 PM Kumar Kartikeya Dwivedi
<memxor@gmail.com> wrote:
>
> These eBPF tracepoint programs export data that is consumed by the
> helpers added in the previous commit.
>
> Also add support int the Makefile to generate a vmlinux.h header that
> would be used by other bpf.c files in later commits.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
>  samples/bpf/Makefile         |  25 ++++
>  samples/bpf/xdp_sample.bpf.c | 215 +++++++++++++++++++++++++++++++++++
>  samples/bpf/xdp_sample.bpf.h |  57 ++++++++++
>  3 files changed, 297 insertions(+)
>  create mode 100644 samples/bpf/xdp_sample.bpf.c
>  create mode 100644 samples/bpf/xdp_sample.bpf.h
>

[...]

> +
> +SEC("tp_btf/xdp_cpumap_kthread")
> +int BPF_PROG(tp_xdp_cpumap_kthread, int map_id, unsigned int processed,
> +            unsigned int drops, int sched, struct xdp_cpumap_stats *xdp_stats)
> +{
> +       // Using xdp_stats directly fails verification

nit: C++ style comment

> +       u32 cpu = bpf_get_smp_processor_id();
> +       struct datarec *rec;
> +
> +       if (cpu < MAX_CPUS) {

tip: if you invert condition and exit early, the rest of your logic
will be less nested (same in few other places)

> +               rec = &sample_data.cpumap_kthread_cnt[cpu];
> +
> +               ATOMIC_ADD_NORMW(rec->processed, processed);
> +               ATOMIC_ADD_NORMW(rec->dropped, drops);
> +               ATOMIC_ADD_NORMW(rec->xdp_pass, xdp_stats->pass);
> +               ATOMIC_ADD_NORMW(rec->xdp_drop, xdp_stats->drop);
> +               ATOMIC_ADD_NORMW(rec->xdp_redirect, xdp_stats->redirect);
> +               /* Count times kthread yielded CPU via schedule call */
> +               if (sched)
> +                       ATOMIC_INC_NORMW(rec->issue);
> +       }
> +       return 0;
> +}
> +

[...]
diff mbox series

Patch

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 57ccff5ccac4..52ac1c6b56e2 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -280,6 +280,11 @@  $(LIBBPF): FORCE
 	$(MAKE) -C $(dir $@) RM='rm -rf' EXTRA_CFLAGS="$(TPROGS_CFLAGS)" \
 		LDFLAGS=$(TPROGS_LDFLAGS) srctree=$(BPF_SAMPLES_PATH)/../../ O=
 
+BPFTOOLDIR := $(TOOLS_PATH)/bpf/bpftool
+BPFTOOL := $(BPFTOOLDIR)/bpftool
+$(BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile)
+	    $(MAKE) -C $(BPFTOOLDIR) srctree=$(BPF_SAMPLES_PATH)/../../
+
 $(obj)/syscall_nrs.h:	$(obj)/syscall_nrs.s FORCE
 	$(call filechk,offsets,__SYSCALL_NRS_H__)
 
@@ -317,6 +322,26 @@  $(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
 
 -include $(BPF_SAMPLES_PATH)/Makefile.target
 
+VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux)				\
+		     $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux)	\
+		     ../../../../vmlinux				\
+		     /sys/kernel/btf/vmlinux				\
+		     /boot/vmlinux-$(shell uname -r)
+VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
+
+ifeq ($(VMLINUX_BTF),)
+$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
+endif
+
+$(obj)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL)
+ifeq ($(VMLINUX_H),)
+	$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
+else
+	$(Q)cp "$(VMLINUX_H)" $@
+endif
+
+clean-files += vmlinux.h
+
 # asm/sysreg.h - inline assembly used by it is incompatible with llvm.
 # But, there is no easy way to fix it, so just exclude it since it is
 # useless for BPF samples.
diff --git a/samples/bpf/xdp_sample.bpf.c b/samples/bpf/xdp_sample.bpf.c
new file mode 100644
index 000000000000..3359b4791ca4
--- /dev/null
+++ b/samples/bpf/xdp_sample.bpf.c
@@ -0,0 +1,215 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*  GPLv2, Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc. */
+#include "xdp_sample.bpf.h"
+
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_helpers.h>
+
+struct sample_data sample_data;
+devmap_xmit_cnt_multi_t devmap_xmit_cnt_multi SEC(".maps");
+
+static __always_inline
+__u32 xdp_get_err_key(int err)
+{
+	switch (err) {
+	case 0:
+		return 0;
+	case -EINVAL:
+		return 2;
+	case -ENETDOWN:
+		return 3;
+	case -EMSGSIZE:
+		return 4;
+	case -EOPNOTSUPP:
+		return 5;
+	case -ENOSPC:
+		return 6;
+	default:
+		return 1;
+	}
+}
+
+static __always_inline
+int xdp_redirect_collect_stat(int err)
+{
+	u32 cpu = bpf_get_smp_processor_id();
+	u32 key = XDP_REDIRECT_ERROR;
+	struct datarec *rec;
+	u32 idx;
+
+	key = xdp_get_err_key(err);
+	idx = key * MAX_CPUS + cpu;
+
+	if (idx < 7 * MAX_CPUS) {
+		rec = &sample_data.redirect_err_cnt[idx];
+		if (key)
+			ATOMIC_INC_NORMW(rec->dropped);
+		else
+			ATOMIC_INC_NORMW(rec->processed);
+	}
+	return 0; /* Indicate event was filtered (no further processing)*/
+	/*
+	 * Returning 1 here would allow e.g. a perf-record tracepoint
+	 * to see and record these events, but it doesn't work well
+	 * in-practice as stopping perf-record also unload this
+	 * bpf_prog.  Plus, there is additional overhead of doing so.
+	 */
+}
+
+SEC("tp_btf/xdp_redirect_err")
+int BPF_PROG(tp_xdp_redirect_err, const struct net_device *dev,
+		 const struct bpf_prog *xdp,
+		 const void *tgt, int err,
+		 const struct bpf_map *map, u32 index)
+{
+	return xdp_redirect_collect_stat(err);
+}
+
+SEC("tp_btf/xdp_redirect_map_err")
+int BPF_PROG(tp_xdp_redirect_map_err, const struct net_device *dev,
+		 const struct bpf_prog *xdp,
+		 const void *tgt, int err,
+		 const struct bpf_map *map, u32 index)
+{
+	return xdp_redirect_collect_stat(err);
+}
+
+SEC("tp_btf/xdp_redirect")
+int BPF_PROG(tp_xdp_redirect, const struct net_device *dev,
+		 const struct bpf_prog *xdp,
+		 const void *tgt, int err,
+		 const struct bpf_map *map, u32 index)
+{
+	return xdp_redirect_collect_stat(err);
+}
+
+SEC("tp_btf/xdp_redirect_map")
+int BPF_PROG(tp_xdp_redirect_map, const struct net_device *dev,
+		 const struct bpf_prog *xdp,
+		 const void *tgt, int err,
+		 const struct bpf_map *map, u32 index)
+{
+	return xdp_redirect_collect_stat(err);
+}
+
+SEC("tp_btf/xdp_exception")
+int BPF_PROG(tp_xdp_exception, const struct net_device *dev,
+	     const struct bpf_prog *xdp, u32 act)
+{
+	u32 cpu = bpf_get_smp_processor_id();
+	struct datarec *rec;
+	u32 key = act, idx;
+
+	if (key > XDP_REDIRECT)
+		key = XDP_REDIRECT + 1;
+	idx = key * MAX_CPUS + cpu;
+
+	if (idx < 6 * MAX_CPUS) {
+		rec = &sample_data.exception_cnt[idx];
+		ATOMIC_INC_NORMW(rec->dropped);
+	}
+
+	return 0;
+}
+
+SEC("tp_btf/xdp_cpumap_enqueue")
+int BPF_PROG(tp_xdp_cpumap_enqueue, int map_id, unsigned int processed,
+	     unsigned int drops, int to_cpu)
+{
+	u32 cpu = bpf_get_smp_processor_id();
+	struct datarec *rec;
+	u32 idx;
+
+	idx = to_cpu * MAX_CPUS + cpu;
+
+	if (idx < MAX_CPUS * MAX_CPUS) {
+		rec = &sample_data.cpumap_enqueue_cnt[idx];
+
+		ATOMIC_ADD_NORMW(rec->processed, processed);
+		ATOMIC_ADD_NORMW(rec->dropped, drops);
+		/* Record bulk events, then userspace can calc average bulk size */
+		if (processed > 0)
+			ATOMIC_INC_NORMW(rec->issue);
+	}
+	/* Inception: It's possible to detect overload situations, via
+	 * this tracepoint.  This can be used for creating a feedback
+	 * loop to XDP, which can take appropriate actions to mitigate
+	 * this overload situation.
+	 */
+	return 0;
+}
+
+SEC("tp_btf/xdp_cpumap_kthread")
+int BPF_PROG(tp_xdp_cpumap_kthread, int map_id, unsigned int processed,
+	     unsigned int drops, int sched, struct xdp_cpumap_stats *xdp_stats)
+{
+	// Using xdp_stats directly fails verification
+	u32 cpu = bpf_get_smp_processor_id();
+	struct datarec *rec;
+
+	if (cpu < MAX_CPUS) {
+		rec = &sample_data.cpumap_kthread_cnt[cpu];
+
+		ATOMIC_ADD_NORMW(rec->processed, processed);
+		ATOMIC_ADD_NORMW(rec->dropped, drops);
+		ATOMIC_ADD_NORMW(rec->xdp_pass, xdp_stats->pass);
+		ATOMIC_ADD_NORMW(rec->xdp_drop, xdp_stats->drop);
+		ATOMIC_ADD_NORMW(rec->xdp_redirect, xdp_stats->redirect);
+		/* Count times kthread yielded CPU via schedule call */
+		if (sched)
+			ATOMIC_INC_NORMW(rec->issue);
+	}
+	return 0;
+}
+
+SEC("tp_btf/xdp_devmap_xmit")
+int BPF_PROG(tp_xdp_devmap_xmit, const struct net_device *from_dev,
+	     const struct net_device *to_dev, int sent, int drops, int err)
+{
+	u32 cpu = bpf_get_smp_processor_id();
+	struct datarec *rec;
+
+	if (cpu < MAX_CPUS) {
+		rec = &sample_data.devmap_xmit_cnt[cpu];
+
+		ATOMIC_ADD_NORMW(rec->processed, sent);
+		ATOMIC_ADD_NORMW(rec->dropped, drops);
+		/* Record bulk events, then userspace can calc average bulk size */
+		ATOMIC_INC_NORMW(rec->info);
+		/* Record error cases, where no frame were sent */
+		/* Catch API error of drv ndo_xdp_xmit sent more than count */
+		if (err || drops < 0)
+			ATOMIC_INC_NORMW(rec->issue);
+	}
+	return 0;
+}
+
+SEC("tp_btf/xdp_devmap_xmit")
+int BPF_PROG(tp_xdp_devmap_xmit_multi, const struct net_device *from_dev,
+	     const struct net_device *to_dev, int sent, int drops, int err)
+{
+	struct datarec empty = {};
+	struct datarec *rec;
+	int idx_in, idx_out;
+	u64 idx;
+
+	idx_in = from_dev->ifindex;
+	idx_out = to_dev->ifindex;
+	idx = idx_in;
+	idx = idx << 32 | idx_out;
+
+	bpf_map_update_elem(&devmap_xmit_cnt_multi, &idx, &empty, BPF_NOEXIST);
+	rec = bpf_map_lookup_elem(&devmap_xmit_cnt_multi, &idx);
+	if (!rec)
+		return 0;
+
+	ATOMIC_ADD_NORMW(rec->processed, sent);
+	ATOMIC_ADD_NORMW(rec->dropped, drops);
+	ATOMIC_INC_NORMW(rec->info);
+
+	if (err || drops < 0)
+		ATOMIC_INC_NORMW(rec->issue);
+
+	return 0;
+}
diff --git a/samples/bpf/xdp_sample.bpf.h b/samples/bpf/xdp_sample.bpf.h
new file mode 100644
index 000000000000..20fbafe28be5
--- /dev/null
+++ b/samples/bpf/xdp_sample.bpf.h
@@ -0,0 +1,57 @@ 
+// SPDX-License-Identifier: GPL-2.0
+#ifndef _XDP_SAMPLE_BPF_H
+#define _XDP_SAMPLE_BPF_H
+
+#include "vmlinux.h"
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_helpers.h>
+
+#include "xdp_sample_shared.h"
+
+#define ETH_ALEN 6
+#define ETH_P_802_3_MIN 0x0600
+#define ETH_P_8021Q 0x8100
+#define ETH_P_8021AD 0x88A8
+#define ETH_P_IP 0x0800
+#define ETH_P_IPV6 0x86DD
+#define ETH_P_ARP 0x0806
+#define IPPROTO_ICMPV6 58
+
+#define EINVAL 22
+#define ENETDOWN 100
+#define EMSGSIZE 90
+#define EOPNOTSUPP 95
+#define ENOSPC 28
+
+extern struct sample_data sample_data;
+
+typedef struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
+	__uint(max_entries, 32);
+	__type(key, u64);
+	__type(value, struct datarec);
+} devmap_xmit_cnt_multi_t;
+
+enum {
+	XDP_REDIRECT_SUCCESS = 0,
+	XDP_REDIRECT_ERROR = 1
+};
+
+static __always_inline void swap_src_dst_mac(void *data)
+{
+	unsigned short *p = data;
+	unsigned short dst[3];
+
+	dst[0] = p[0];
+	dst[1] = p[1];
+	dst[2] = p[2];
+	p[0] = p[3];
+	p[1] = p[4];
+	p[2] = p[5];
+	p[3] = dst[0];
+	p[4] = dst[1];
+	p[5] = dst[2];
+}
+
+#endif