All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output
@ 2021-05-28 23:52 Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 01/15] samples: bpf: fix a couple of NULL dereferences Kumar Kartikeya Dwivedi
                   ` (14 more replies)
  0 siblings, 15 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This first version is primarily for collecting feedback on the changes being made.

There is currently inconsistency between output of some XDP samples, for
instance xdp_redirect_cpu and xdp_monitor are fairly featureful but the first
misses support for XDP tracepoints, and both miss support for showing the source
of xdp redirection errors.

Some others like xdp_redirect and xdp_redirect_map are even worse in that they only the packet count.

This series consolidates the common core of all these samples into a single
object that can be linked into all these samples, with support for the
following:

* Received packet statistics (counted from the xdp redirect program)
* Redirect success tracepoint statistics (has to be enabled explicitly)
* Redirect error tracepoint statistics (with support to report exact errno that was
  hit in the kernel)
* XDP cpumap enqueue/kthread tp stats (only relevant for xdp_redirect_cpu)
* XDP devmap_xmit tp stats (only relevant in native xdp redirect mode)
* XDP exception tp statistics (with support for per XDP action statistics)
* ... and a fair amount of cleanups everywhere

All tracepoints have also been converted to use raw_tp infrastructure, in an
effort to reduce overhead when they are enabled. A tracepoint is only enabled
when it is required.

For now, the series has only converted xdp_monitor, xdp_redirect_map, and
xdp_redirect_cpu. Once there is general agreement on the approach, it can be
extended to xdp_redirect, xdp_redirect_multi, and xdp_rxq_info in a subsequent
revision.

Explanation of the output:

There is now a terse output mode by default that shows primarily four fields:
  rx/s     Number of packets received per second
  redir/s  Number of packets successfully redirected per second
  error/s  Aggregated count of errors per second (including dropped packets)
  xmit/s   Number of packets transmitted on the output device per second

Some examples:
 ; sudo ./xdp_redirect_map veth0 veth1 -s
Redirecting from veth0 (ifindex 15; driver veth) to veth1 (ifindex 14; driver veth)
veth0->veth1                    0 rx/s                  0 redir/s               0 error/s               0 xmit/s
veth0->veth1            9,998,660 rx/s          9,998,658 redir/s               0 error/s       9,998,654 xmit/s
...

There is also a verbose mode, that can also be enabled by default using -v (--verbose).
The output mode can be switched dynamically at runtime using Ctrl + \ (SIGQUIT).

To make the terse output more useful, the errors that occur are expanded inline
(as if verbose mode was enabled) to let the user pin down the source of the
problem without having to clutter output (or possibly miss it).

For instance, let's consider a case where the output device link state is set to
down while redirection is happening:

[...]
veth0->veth1           24,503,376 rx/s                  0 error/s      24,503,372 xmit/s
veth0->veth1           25,044,775 rx/s                  0 error/s      25,044,783 xmit/s
veth0->veth1           25,263,046 rx/s                  4 error/s      25,263,028 xmit/s
  redirect_err                  4 error/s
    ENETDOWN                    4 error/s
[...]

This is how the error is expanded when it occurs. If there is more than one
errno (say when watching using xdp_monitor), all of them will be expanded
inline. The sample holds for xdp_exception.

Even when we expand errors in terse mode, we don't show the per-CPU stats. This
is only meant to be displayed in verbose mode.

Another usability improvement is letting the user jump from the tracepoint field
(the text that displays the name of the tracepoint event) directly to points in
the kernel where the tracepoint is triggered. This is done by means of making
the text a hyperlink on capable terminals. The tool is also smart enough to add
the running kernel's major version in the link.

An example of how a complete xdp_redirect_map session would look:

Redirecting from veth0 (ifindex 15; driver veth) to veth1 (ifindex 14; driver veth)
veth0->veth1                    0 rx/s                  0 error/s               0 xmit/s
veth0->veth1           11,626,046 rx/s                  0 error/s      11,626,059 xmit/s
^\
veth0->veth1           13,621,785 rx/s                  0 error/s      13,621,782 xmit/s
  receive total        13,621,785 pkt/s                 0 drop/s                0 error/s
          cpu:3        13,621,785 pkt/s                 0 drop/s                0 error/s
  redirect_err                  0 error/s
  xdp_exception                 0 hit/s
  devmap_xmit total    13,621,782 xmit/s                0 drop/s                0 drv_err/s          2.00 bulk_avg
              cpu:3    13,621,782 xmit/s                0 drop/s                0 drv_err/s          2.00 bulk_avg

veth0->veth1           13,168,898 rx/s                  0 error/s      13,168,901 xmit/s
  receive total        13,168,898 pkt/s                 0 drop/s                0 error/s
          cpu:3        13,168,898 pkt/s                 0 drop/s                0 error/s
  redirect_err                  0 error/s
  xdp_exception                 0 hit/s
  devmap_xmit total    13,168,901 xmit/s                0 drop/s                0 drv_err/s          2.00 bulk_avg
              cpu:3    13,168,901 xmit/s                0 drop/s                0 drv_err/s          2.00 bulk_avg

veth0->veth1           13,427,862 rx/s                  0 error/s      13,427,860 xmit/s
  receive total        13,427,862 pkt/s                 0 drop/s                0 error/s
          cpu:3        13,427,862 pkt/s                 0 drop/s                0 error/s
  redirect_err                  0 error/s
  xdp_exception                 0 hit/s
  devmap_xmit total    13,427,860 xmit/s                0 drop/s                0 drv_err/s          2.00 bulk_avg
              cpu:3    13,427,860 xmit/s                0 drop/s                0 drv_err/s          2.00 bulk_avg

^C
Totals
  Packets received    : 51,844,591
  Average packets/s   : 5,184,459
  Packets dropped     : 0
  Errors recorded     : 0
  Packets transmitted : 51,844,602
  Average transmit/s  : 5,184,460

The xdp_redirect tracepoint (for success stats) needs to be enabled explicitly using --stats/-s.

Kumar Kartikeya Dwivedi (15):
  samples: bpf: fix a couple of NULL dereferences
  samples: bpf: fix a couple of warnings
  samples: bpf: split out common bpf progs to its own file
  samples: bpf: refactor generic parts out of xdp_redirect_cpu_user
  samples: bpf: convert xdp_redirect_map to use xdp_samples
  samples: bpf: prepare devmap_xmit support in xdp_sample
  samples: bpf: add extended reporting for xdp redirect error
  samples: bpf: add per exception reporting for xdp_exception
  samples: bpf: convert xdp_monitor to use xdp_samples
  samples: bpf: implement terse output mode and make it default
  samples: bpf: print summary of session on exit
  samples: bpf: subtract time spent in collection from polling interval
  samples: bpf: add new options for xdp samples
  samples: bpf: add documentation
  samples: bpf: convert xdp_samples to use raw_tracepoints

 samples/bpf/Makefile                    |    8 +-
 samples/bpf/cookie_uid_helper_example.c |   12 +-
 samples/bpf/tracex4_user.c              |    2 +-
 samples/bpf/xdp_monitor_kern.c          |  253 +-----
 samples/bpf/xdp_monitor_user.c          |  645 +-------------
 samples/bpf/xdp_redirect_cpu_kern.c     |  213 +----
 samples/bpf/xdp_redirect_cpu_user.c     |  608 ++-----------
 samples/bpf/xdp_redirect_map_kern.c     |   23 +-
 samples/bpf/xdp_redirect_map_user.c     |  170 ++--
 samples/bpf/xdp_redirect_user.c         |    4 +-
 samples/bpf/xdp_sample_kern.h           |  263 ++++++
 samples/bpf/xdp_sample_user.c           | 1089 +++++++++++++++++++++++
 samples/bpf/xdp_sample_user.h           |  185 ++++
 13 files changed, 1764 insertions(+), 1711 deletions(-)
 create mode 100644 samples/bpf/xdp_sample_kern.h
 create mode 100644 samples/bpf/xdp_sample_user.c
 create mode 100644 samples/bpf/xdp_sample_user.h

-- 
2.31.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 01/15] samples: bpf: fix a couple of NULL dereferences
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 02/15] samples: bpf: fix a couple of warnings Kumar Kartikeya Dwivedi
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

When giving it just one ifname instead of two, it accesses argv[optind + 1],
which is out of bounds (as argc < optind + 1).

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_redirect_map_user.c | 4 ++--
 samples/bpf/xdp_redirect_user.c     | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 0e8192688dfc..ad3cdc4c07d3 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -169,8 +169,8 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
-	if (optind == argc) {
-		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
+	if (argc <= optind + 1) {
+		usage(basename(argv[0]));
 		return 1;
 	}
 
diff --git a/samples/bpf/xdp_redirect_user.c b/samples/bpf/xdp_redirect_user.c
index 41d705c3a1f7..4e310660632b 100644
--- a/samples/bpf/xdp_redirect_user.c
+++ b/samples/bpf/xdp_redirect_user.c
@@ -130,8 +130,8 @@ int main(int argc, char **argv)
 	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
 		xdp_flags |= XDP_FLAGS_DRV_MODE;
 
-	if (optind == argc) {
-		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
+	if (argc <= optind + 1) {
+		usage(basename(argv[0]));
 		return 1;
 	}
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 02/15] samples: bpf: fix a couple of warnings
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 01/15] samples: bpf: fix a couple of NULL dereferences Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 03/15] samples: bpf: split out common bpf progs to its own file Kumar Kartikeya Dwivedi
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

cookie_uid_helper_example.c: In function ‘main’:
cookie_uid_helper_example.c:178:69: warning: ‘ -j ACCEPT’ directive
	writing 10 bytes into a region of size between 8 and 58
	[-Wformat-overflow=]
  178 |  sprintf(rules, "iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT",
      |								       ^~~~~~~~~~
/home/kkd/src/linux/samples/bpf/cookie_uid_helper_example.c:178:9: note:
	‘sprintf’ output between 53 and 103 bytes into a destination of size 100
  178 |  sprintf(rules, "iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT",
      |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  179 |         file);
      |         ~~~~~

Fix by using snprintf and a sufficiently sized buffer.

tracex4_user.c:35:15: warning: ‘write’ reading 12 bytes from a region of
	size 11 [-Wstringop-overread]
   35 |         key = write(1, "\e[1;1H\e[2J", 12); /* clear screen */
      |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

Use size as 11.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/cookie_uid_helper_example.c | 12 +++++++++---
 samples/bpf/tracex4_user.c              |  2 +-
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c
index cc3bce8d3aac..30fdcd664da2 100644
--- a/samples/bpf/cookie_uid_helper_example.c
+++ b/samples/bpf/cookie_uid_helper_example.c
@@ -1,3 +1,4 @@
+
 /* This test is a demo of using get_socket_uid and get_socket_cookie
  * helper function to do per socket based network traffic monitoring.
  * It requires iptables version higher then 1.6.1. to load pinned eBPF
@@ -167,7 +168,7 @@ static void prog_load(void)
 static void prog_attach_iptables(char *file)
 {
 	int ret;
-	char rules[100];
+	char rules[256];
 
 	if (bpf_obj_pin(prog_fd, file))
 		error(1, errno, "bpf_obj_pin");
@@ -175,8 +176,13 @@ static void prog_attach_iptables(char *file)
 		printf("file path too long: %s\n", file);
 		exit(1);
 	}
-	sprintf(rules, "iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT",
-		file);
+	ret = snprintf(rules, sizeof(rules),
+		       "iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT",
+		       file);
+	if (ret < 0 || ret >= sizeof(rules)) {
+		printf("error constructing iptables command\n");
+		exit(1);
+	}
 	ret = system(rules);
 	if (ret < 0) {
 		printf("iptables rule update failed: %d/n", WEXITSTATUS(ret));
diff --git a/samples/bpf/tracex4_user.c b/samples/bpf/tracex4_user.c
index cea399424bca..566e6440e8c2 100644
--- a/samples/bpf/tracex4_user.c
+++ b/samples/bpf/tracex4_user.c
@@ -32,7 +32,7 @@ static void print_old_objects(int fd)
 	__u64 key, next_key;
 	struct pair v;
 
-	key = write(1, "\e[1;1H\e[2J", 12); /* clear screen */
+	key = write(1, "\e[1;1H\e[2J", 11); /* clear screen */
 
 	key = -1;
 	while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 03/15] samples: bpf: split out common bpf progs to its own file
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 01/15] samples: bpf: fix a couple of NULL dereferences Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 02/15] samples: bpf: fix a couple of warnings Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-30  3:05   ` Andrii Nakryiko
  2021-05-28 23:52 ` [PATCH RFC bpf-next 04/15] samples: bpf: refactor generic parts out of xdp_redirect_cpu_user Kumar Kartikeya Dwivedi
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This is done to later reuse these in a way that can be shared
among multiple samples.

We are using xdp_redirect_cpu_kern.c as a base to build further support on
top (mostly adding a few other things missing that xdp_monitor does in
subsequent patches).

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_sample_kern.h | 220 ++++++++++++++++++++++++++++++++++
 1 file changed, 220 insertions(+)
 create mode 100644 samples/bpf/xdp_sample_kern.h

diff --git a/samples/bpf/xdp_sample_kern.h b/samples/bpf/xdp_sample_kern.h
new file mode 100644
index 000000000000..bb809542ac20
--- /dev/null
+++ b/samples/bpf/xdp_sample_kern.h
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/*  GPLv2, Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc. */
+#pragma once
+
+#include <uapi/linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+#define MAX_CPUS 64
+
+/* Common stats data record to keep userspace more simple */
+struct datarec {
+	__u64 processed;
+	__u64 dropped;
+	__u64 issue;
+	__u64 xdp_pass;
+	__u64 xdp_drop;
+	__u64 xdp_redirect;
+};
+
+/* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
+ * feedback.  Redirect TX errors can be caught via a tracepoint.
+ */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, u32);
+	__type(value, struct datarec);
+	__uint(max_entries, 1);
+} rx_cnt SEC(".maps");
+
+/* Used by trace point */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, u32);
+	__type(value, struct datarec);
+	__uint(max_entries, 2);
+	/* TODO: have entries for all possible errno's */
+} redirect_err_cnt SEC(".maps");
+
+/* Used by trace point */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, u32);
+	__type(value, struct datarec);
+	__uint(max_entries, MAX_CPUS);
+} cpumap_enqueue_cnt SEC(".maps");
+
+/* Used by trace point */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, u32);
+	__type(value, struct datarec);
+	__uint(max_entries, 1);
+} cpumap_kthread_cnt SEC(".maps");
+
+/* Used by trace point */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, u32);
+	__type(value, struct datarec);
+	__uint(max_entries, 1);
+} exception_cnt SEC(".maps");
+
+/*** Trace point code ***/
+
+/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_redirect/format
+ * Code in:                kernel/include/trace/events/xdp.h
+ */
+struct xdp_redirect_ctx {
+	u64 __pad;	// First 8 bytes are not accessible by bpf code
+	int prog_id;	//	offset:8;  size:4; signed:1;
+	u32 act;	//	offset:12  size:4; signed:0;
+	int ifindex;	//	offset:16  size:4; signed:1;
+	int err;	//	offset:20  size:4; signed:1;
+	int to_ifindex;	//	offset:24  size:4; signed:1;
+	u32 map_id;	//	offset:28  size:4; signed:0;
+	int map_index;	//	offset:32  size:4; signed:1;
+};			//	offset:36
+
+enum {
+	XDP_REDIRECT_SUCCESS = 0,
+	XDP_REDIRECT_ERROR = 1
+};
+
+static __always_inline
+int xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx)
+{
+	u32 key = XDP_REDIRECT_ERROR;
+	struct datarec *rec;
+	int err = ctx->err;
+
+	if (!err)
+		key = XDP_REDIRECT_SUCCESS;
+
+	rec = bpf_map_lookup_elem(&redirect_err_cnt, &key);
+	if (!rec)
+		return 0;
+	rec->dropped += 1;
+
+	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("tracepoint/xdp/xdp_redirect_err")
+int trace_xdp_redirect_err(struct xdp_redirect_ctx *ctx)
+{
+	return xdp_redirect_collect_stat(ctx);
+}
+
+SEC("tracepoint/xdp/xdp_redirect_map_err")
+int trace_xdp_redirect_map_err(struct xdp_redirect_ctx *ctx)
+{
+	return xdp_redirect_collect_stat(ctx);
+}
+
+/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_exception/format
+ * Code in:                kernel/include/trace/events/xdp.h
+ */
+struct xdp_exception_ctx {
+	u64 __pad;	// First 8 bytes are not accessible by bpf code
+	int prog_id;	//	offset:8;  size:4; signed:1;
+	u32 act;	//	offset:12; size:4; signed:0;
+	int ifindex;	//	offset:16; size:4; signed:1;
+};
+
+SEC("tracepoint/xdp/xdp_exception")
+int trace_xdp_exception(struct xdp_exception_ctx *ctx)
+{
+	struct datarec *rec;
+	u32 key = 0;
+
+	rec = bpf_map_lookup_elem(&exception_cnt, &key);
+	if (!rec)
+		return 1;
+	rec->dropped += 1;
+
+	return 0;
+}
+
+/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_enqueue/format
+ * Code in:         kernel/include/trace/events/xdp.h
+ */
+struct cpumap_enqueue_ctx {
+	u64 __pad;		// First 8 bytes are not accessible by bpf code
+	int map_id;		//	offset:8;  size:4; signed:1;
+	u32 act;		//	offset:12; size:4; signed:0;
+	int cpu;		//	offset:16; size:4; signed:1;
+	unsigned int drops;	//	offset:20; size:4; signed:0;
+	unsigned int processed;	//	offset:24; size:4; signed:0;
+	int to_cpu;		//	offset:28; size:4; signed:1;
+};
+
+SEC("tracepoint/xdp/xdp_cpumap_enqueue")
+int trace_xdp_cpumap_enqueue(struct cpumap_enqueue_ctx *ctx)
+{
+	u32 to_cpu = ctx->to_cpu;
+	struct datarec *rec;
+
+	if (to_cpu >= MAX_CPUS)
+		return 1;
+
+	rec = bpf_map_lookup_elem(&cpumap_enqueue_cnt, &to_cpu);
+	if (!rec)
+		return 0;
+	rec->processed += ctx->processed;
+	rec->dropped   += ctx->drops;
+
+	/* Record bulk events, then userspace can calc average bulk size */
+	if (ctx->processed > 0)
+		rec->issue += 1;
+
+	/* 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;
+}
+
+/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_kthread/format
+ * Code in:         kernel/include/trace/events/xdp.h
+ */
+struct cpumap_kthread_ctx {
+	u64 __pad;			// First 8 bytes are not accessible
+	int map_id;			//	offset:8;  size:4; signed:1;
+	u32 act;			//	offset:12; size:4; signed:0;
+	int cpu;			//	offset:16; size:4; signed:1;
+	unsigned int drops;		//	offset:20; size:4; signed:0;
+	unsigned int processed;		//	offset:24; size:4; signed:0;
+	int sched;			//	offset:28; size:4; signed:1;
+	unsigned int xdp_pass;		//	offset:32; size:4; signed:0;
+	unsigned int xdp_drop;		//	offset:36; size:4; signed:0;
+	unsigned int xdp_redirect;	//	offset:40; size:4; signed:0;
+};
+
+SEC("tracepoint/xdp/xdp_cpumap_kthread")
+int trace_xdp_cpumap_kthread(struct cpumap_kthread_ctx *ctx)
+{
+	struct datarec *rec;
+	u32 key = 0;
+
+	rec = bpf_map_lookup_elem(&cpumap_kthread_cnt, &key);
+	if (!rec)
+		return 0;
+	rec->processed += ctx->processed;
+	rec->dropped   += ctx->drops;
+	rec->xdp_pass  += ctx->xdp_pass;
+	rec->xdp_drop  += ctx->xdp_drop;
+	rec->xdp_redirect  += ctx->xdp_redirect;
+
+	/* Count times kthread yielded CPU via schedule call */
+	if (ctx->sched)
+		rec->issue++;
+
+	return 0;
+}
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 04/15] samples: bpf: refactor generic parts out of xdp_redirect_cpu_user
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (2 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 03/15] samples: bpf: split out common bpf progs to its own file Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 05/15] samples: bpf: convert xdp_redirect_map to use xdp_samples Kumar Kartikeya Dwivedi
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This will be used as a common core for multiple samples.

Also add a couple of helpers:
get_driver_name - Used to print the driver name for ifindex
get_mac_addr - Used to get the mac address for ifindex

This change also converts xdp_redirect_cpu to use the new xdp_sample
helpers.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/Makefile                |   2 +-
 samples/bpf/xdp_redirect_cpu_kern.c | 213 +---------
 samples/bpf/xdp_redirect_cpu_user.c | 560 ++------------------------
 samples/bpf/xdp_sample_user.c       | 588 ++++++++++++++++++++++++++++
 samples/bpf/xdp_sample_user.h       | 101 +++++
 5 files changed, 730 insertions(+), 734 deletions(-)
 create mode 100644 samples/bpf/xdp_sample_user.c
 create mode 100644 samples/bpf/xdp_sample_user.h

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 520434ea966f..c0c02e12e28b 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -101,7 +101,7 @@ per_socket_stats_example-objs := cookie_uid_helper_example.o
 xdp_redirect-objs := xdp_redirect_user.o
 xdp_redirect_map-objs := xdp_redirect_map_user.o
 xdp_redirect_map_multi-objs := xdp_redirect_map_multi_user.o
-xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o
+xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o xdp_sample_user.o
 xdp_monitor-objs := xdp_monitor_user.o
 xdp_rxq_info-objs := xdp_rxq_info_user.o
 syscall_tp-objs := syscall_tp_user.o
diff --git a/samples/bpf/xdp_redirect_cpu_kern.c b/samples/bpf/xdp_redirect_cpu_kern.c
index 8255025dea97..06cc37f0289c 100644
--- a/samples/bpf/xdp_redirect_cpu_kern.c
+++ b/samples/bpf/xdp_redirect_cpu_kern.c
@@ -14,6 +14,7 @@
 #include <uapi/linux/bpf.h>
 #include <bpf/bpf_helpers.h>
 #include "hash_func01.h"
+#include "xdp_sample_kern.h"
 
 #define MAX_CPUS NR_CPUS
 
@@ -25,51 +26,6 @@ struct {
 	__uint(max_entries, MAX_CPUS);
 } cpu_map SEC(".maps");
 
-/* Common stats data record to keep userspace more simple */
-struct datarec {
-	__u64 processed;
-	__u64 dropped;
-	__u64 issue;
-	__u64 xdp_pass;
-	__u64 xdp_drop;
-	__u64 xdp_redirect;
-};
-
-/* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
- * feedback.  Redirect TX errors can be caught via a tracepoint.
- */
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct datarec);
-	__uint(max_entries, 1);
-} rx_cnt SEC(".maps");
-
-/* Used by trace point */
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct datarec);
-	__uint(max_entries, 2);
-	/* TODO: have entries for all possible errno's */
-} redirect_err_cnt SEC(".maps");
-
-/* Used by trace point */
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct datarec);
-	__uint(max_entries, MAX_CPUS);
-} cpumap_enqueue_cnt SEC(".maps");
-
-/* Used by trace point */
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct datarec);
-	__uint(max_entries, 1);
-} cpumap_kthread_cnt SEC(".maps");
-
 /* Set of maps controlling available CPU, and for iterating through
  * selectable redirect CPUs.
  */
@@ -92,14 +48,6 @@ struct {
 	__uint(max_entries, 1);
 } cpus_iterator SEC(".maps");
 
-/* Used by trace point */
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct datarec);
-	__uint(max_entries, 1);
-} exception_cnt SEC(".maps");
-
 /* Helper parse functions */
 
 /* Parse Ethernet layer 2, extract network layer 3 offset and protocol
@@ -569,162 +517,3 @@ int  xdp_prognum5_lb_hash_ip_pairs(struct xdp_md *ctx)
 }
 
 char _license[] SEC("license") = "GPL";
-
-/*** Trace point code ***/
-
-/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_redirect/format
- * Code in:                kernel/include/trace/events/xdp.h
- */
-struct xdp_redirect_ctx {
-	u64 __pad;	// First 8 bytes are not accessible by bpf code
-	int prog_id;	//	offset:8;  size:4; signed:1;
-	u32 act;	//	offset:12  size:4; signed:0;
-	int ifindex;	//	offset:16  size:4; signed:1;
-	int err;	//	offset:20  size:4; signed:1;
-	int to_ifindex;	//	offset:24  size:4; signed:1;
-	u32 map_id;	//	offset:28  size:4; signed:0;
-	int map_index;	//	offset:32  size:4; signed:1;
-};			//	offset:36
-
-enum {
-	XDP_REDIRECT_SUCCESS = 0,
-	XDP_REDIRECT_ERROR = 1
-};
-
-static __always_inline
-int xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx)
-{
-	u32 key = XDP_REDIRECT_ERROR;
-	struct datarec *rec;
-	int err = ctx->err;
-
-	if (!err)
-		key = XDP_REDIRECT_SUCCESS;
-
-	rec = bpf_map_lookup_elem(&redirect_err_cnt, &key);
-	if (!rec)
-		return 0;
-	rec->dropped += 1;
-
-	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("tracepoint/xdp/xdp_redirect_err")
-int trace_xdp_redirect_err(struct xdp_redirect_ctx *ctx)
-{
-	return xdp_redirect_collect_stat(ctx);
-}
-
-SEC("tracepoint/xdp/xdp_redirect_map_err")
-int trace_xdp_redirect_map_err(struct xdp_redirect_ctx *ctx)
-{
-	return xdp_redirect_collect_stat(ctx);
-}
-
-/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_exception/format
- * Code in:                kernel/include/trace/events/xdp.h
- */
-struct xdp_exception_ctx {
-	u64 __pad;	// First 8 bytes are not accessible by bpf code
-	int prog_id;	//	offset:8;  size:4; signed:1;
-	u32 act;	//	offset:12; size:4; signed:0;
-	int ifindex;	//	offset:16; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_exception")
-int trace_xdp_exception(struct xdp_exception_ctx *ctx)
-{
-	struct datarec *rec;
-	u32 key = 0;
-
-	rec = bpf_map_lookup_elem(&exception_cnt, &key);
-	if (!rec)
-		return 1;
-	rec->dropped += 1;
-
-	return 0;
-}
-
-/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_enqueue/format
- * Code in:         kernel/include/trace/events/xdp.h
- */
-struct cpumap_enqueue_ctx {
-	u64 __pad;		// First 8 bytes are not accessible by bpf code
-	int map_id;		//	offset:8;  size:4; signed:1;
-	u32 act;		//	offset:12; size:4; signed:0;
-	int cpu;		//	offset:16; size:4; signed:1;
-	unsigned int drops;	//	offset:20; size:4; signed:0;
-	unsigned int processed;	//	offset:24; size:4; signed:0;
-	int to_cpu;		//	offset:28; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_cpumap_enqueue")
-int trace_xdp_cpumap_enqueue(struct cpumap_enqueue_ctx *ctx)
-{
-	u32 to_cpu = ctx->to_cpu;
-	struct datarec *rec;
-
-	if (to_cpu >= MAX_CPUS)
-		return 1;
-
-	rec = bpf_map_lookup_elem(&cpumap_enqueue_cnt, &to_cpu);
-	if (!rec)
-		return 0;
-	rec->processed += ctx->processed;
-	rec->dropped   += ctx->drops;
-
-	/* Record bulk events, then userspace can calc average bulk size */
-	if (ctx->processed > 0)
-		rec->issue += 1;
-
-	/* 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;
-}
-
-/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_kthread/format
- * Code in:         kernel/include/trace/events/xdp.h
- */
-struct cpumap_kthread_ctx {
-	u64 __pad;			// First 8 bytes are not accessible
-	int map_id;			//	offset:8;  size:4; signed:1;
-	u32 act;			//	offset:12; size:4; signed:0;
-	int cpu;			//	offset:16; size:4; signed:1;
-	unsigned int drops;		//	offset:20; size:4; signed:0;
-	unsigned int processed;		//	offset:24; size:4; signed:0;
-	int sched;			//	offset:28; size:4; signed:1;
-	unsigned int xdp_pass;		//	offset:32; size:4; signed:0;
-	unsigned int xdp_drop;		//	offset:36; size:4; signed:0;
-	unsigned int xdp_redirect;	//	offset:40; size:4; signed:0;
-};
-
-SEC("tracepoint/xdp/xdp_cpumap_kthread")
-int trace_xdp_cpumap_kthread(struct cpumap_kthread_ctx *ctx)
-{
-	struct datarec *rec;
-	u32 key = 0;
-
-	rec = bpf_map_lookup_elem(&cpumap_kthread_cnt, &key);
-	if (!rec)
-		return 0;
-	rec->processed += ctx->processed;
-	rec->dropped   += ctx->drops;
-	rec->xdp_pass  += ctx->xdp_pass;
-	rec->xdp_drop  += ctx->xdp_drop;
-	rec->xdp_redirect  += ctx->xdp_redirect;
-
-	/* Count times kthread yielded CPU via schedule call */
-	if (ctx->sched)
-		rec->issue++;
-
-	return 0;
-}
diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c
index 576411612523..6dbed962a2e2 100644
--- a/samples/bpf/xdp_redirect_cpu_user.c
+++ b/samples/bpf/xdp_redirect_cpu_user.c
@@ -22,59 +22,21 @@ static const char *__doc__ =
 #include <arpa/inet.h>
 #include <linux/if_link.h>
 
-/* How many xdp_progs are defined in _kern.c */
-#define MAX_PROG 6
-
 #include <bpf/bpf.h>
 #include <bpf/libbpf.h>
 
 #include "bpf_util.h"
+#include "xdp_sample_user.h"
 
 static int ifindex = -1;
 static char ifname_buf[IF_NAMESIZE];
 static char *ifname;
 static __u32 prog_id;
+static int map_fd;
+static int avail_fd;
+static int count_fd;
 
 static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
-static int n_cpus;
-
-enum map_type {
-	CPU_MAP,
-	RX_CNT,
-	REDIRECT_ERR_CNT,
-	CPUMAP_ENQUEUE_CNT,
-	CPUMAP_KTHREAD_CNT,
-	CPUS_AVAILABLE,
-	CPUS_COUNT,
-	CPUS_ITERATOR,
-	EXCEPTION_CNT,
-};
-
-static const char *const map_type_strings[] = {
-	[CPU_MAP] = "cpu_map",
-	[RX_CNT] = "rx_cnt",
-	[REDIRECT_ERR_CNT] = "redirect_err_cnt",
-	[CPUMAP_ENQUEUE_CNT] = "cpumap_enqueue_cnt",
-	[CPUMAP_KTHREAD_CNT] = "cpumap_kthread_cnt",
-	[CPUS_AVAILABLE] = "cpus_available",
-	[CPUS_COUNT] = "cpus_count",
-	[CPUS_ITERATOR] = "cpus_iterator",
-	[EXCEPTION_CNT] = "exception_cnt",
-};
-
-#define NUM_TP 5
-#define NUM_MAP 9
-struct bpf_link *tp_links[NUM_TP] = {};
-static int map_fds[NUM_MAP];
-static int tp_cnt = 0;
-
-/* Exit return codes */
-#define EXIT_OK		0
-#define EXIT_FAIL		1
-#define EXIT_FAIL_OPTION	2
-#define EXIT_FAIL_XDP		3
-#define EXIT_FAIL_BPF		4
-#define EXIT_FAIL_MEM		5
 
 static const struct option long_options[] = {
 	{"help",	no_argument,		NULL, 'h' },
@@ -115,11 +77,8 @@ static void int_exit(int sig)
 			printf("program on interface changed, not removing\n");
 		}
 	}
-	/* Detach tracepoints */
-	while (tp_cnt)
-		bpf_link__destroy(tp_links[--tp_cnt]);
 
-	exit(EXIT_OK);
+	sample_exit(EXIT_OK);
 }
 
 static void print_avail_progs(struct bpf_object *obj)
@@ -155,423 +114,6 @@ static void usage(char *argv[], struct bpf_object *obj)
 	printf("\n");
 }
 
-/* gettime returns the current time of day in nanoseconds.
- * Cost: clock_gettime (ns) => 26ns (CLOCK_MONOTONIC)
- *       clock_gettime (ns) =>  9ns (CLOCK_MONOTONIC_COARSE)
- */
-#define NANOSEC_PER_SEC 1000000000 /* 10^9 */
-static __u64 gettime(void)
-{
-	struct timespec t;
-	int res;
-
-	res = clock_gettime(CLOCK_MONOTONIC, &t);
-	if (res < 0) {
-		fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
-		exit(EXIT_FAIL);
-	}
-	return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
-}
-
-/* Common stats data record shared with _kern.c */
-struct datarec {
-	__u64 processed;
-	__u64 dropped;
-	__u64 issue;
-	__u64 xdp_pass;
-	__u64 xdp_drop;
-	__u64 xdp_redirect;
-};
-struct record {
-	__u64 timestamp;
-	struct datarec total;
-	struct datarec *cpu;
-};
-struct stats_record {
-	struct record rx_cnt;
-	struct record redir_err;
-	struct record kthread;
-	struct record exception;
-	struct record enq[];
-};
-
-static bool map_collect_percpu(int fd, __u32 key, struct record *rec)
-{
-	/* For percpu maps, userspace gets a value per possible CPU */
-	unsigned int nr_cpus = bpf_num_possible_cpus();
-	struct datarec values[nr_cpus];
-	__u64 sum_xdp_redirect = 0;
-	__u64 sum_xdp_pass = 0;
-	__u64 sum_xdp_drop = 0;
-	__u64 sum_processed = 0;
-	__u64 sum_dropped = 0;
-	__u64 sum_issue = 0;
-	int i;
-
-	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
-		fprintf(stderr,
-			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
-		return false;
-	}
-	/* Get time as close as possible to reading map contents */
-	rec->timestamp = gettime();
-
-	/* Record and sum values from each CPU */
-	for (i = 0; i < nr_cpus; i++) {
-		rec->cpu[i].processed = values[i].processed;
-		sum_processed        += values[i].processed;
-		rec->cpu[i].dropped = values[i].dropped;
-		sum_dropped        += values[i].dropped;
-		rec->cpu[i].issue = values[i].issue;
-		sum_issue        += values[i].issue;
-		rec->cpu[i].xdp_pass = values[i].xdp_pass;
-		sum_xdp_pass += values[i].xdp_pass;
-		rec->cpu[i].xdp_drop = values[i].xdp_drop;
-		sum_xdp_drop += values[i].xdp_drop;
-		rec->cpu[i].xdp_redirect = values[i].xdp_redirect;
-		sum_xdp_redirect += values[i].xdp_redirect;
-	}
-	rec->total.processed = sum_processed;
-	rec->total.dropped   = sum_dropped;
-	rec->total.issue     = sum_issue;
-	rec->total.xdp_pass  = sum_xdp_pass;
-	rec->total.xdp_drop  = sum_xdp_drop;
-	rec->total.xdp_redirect = sum_xdp_redirect;
-	return true;
-}
-
-static struct datarec *alloc_record_per_cpu(void)
-{
-	unsigned int nr_cpus = bpf_num_possible_cpus();
-	struct datarec *array;
-
-	array = calloc(nr_cpus, sizeof(struct datarec));
-	if (!array) {
-		fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
-		exit(EXIT_FAIL_MEM);
-	}
-	return array;
-}
-
-static struct stats_record *alloc_stats_record(void)
-{
-	struct stats_record *rec;
-	int i, size;
-
-	size = sizeof(*rec) + n_cpus * sizeof(struct record);
-	rec = malloc(size);
-	if (!rec) {
-		fprintf(stderr, "Mem alloc error\n");
-		exit(EXIT_FAIL_MEM);
-	}
-	memset(rec, 0, size);
-	rec->rx_cnt.cpu    = alloc_record_per_cpu();
-	rec->redir_err.cpu = alloc_record_per_cpu();
-	rec->kthread.cpu   = alloc_record_per_cpu();
-	rec->exception.cpu = alloc_record_per_cpu();
-	for (i = 0; i < n_cpus; i++)
-		rec->enq[i].cpu = alloc_record_per_cpu();
-
-	return rec;
-}
-
-static void free_stats_record(struct stats_record *r)
-{
-	int i;
-
-	for (i = 0; i < n_cpus; i++)
-		free(r->enq[i].cpu);
-	free(r->exception.cpu);
-	free(r->kthread.cpu);
-	free(r->redir_err.cpu);
-	free(r->rx_cnt.cpu);
-	free(r);
-}
-
-static double calc_period(struct record *r, struct record *p)
-{
-	double period_ = 0;
-	__u64 period = 0;
-
-	period = r->timestamp - p->timestamp;
-	if (period > 0)
-		period_ = ((double) period / NANOSEC_PER_SEC);
-
-	return period_;
-}
-
-static __u64 calc_pps(struct datarec *r, struct datarec *p, double period_)
-{
-	__u64 packets = 0;
-	__u64 pps = 0;
-
-	if (period_ > 0) {
-		packets = r->processed - p->processed;
-		pps = packets / period_;
-	}
-	return pps;
-}
-
-static __u64 calc_drop_pps(struct datarec *r, struct datarec *p, double period_)
-{
-	__u64 packets = 0;
-	__u64 pps = 0;
-
-	if (period_ > 0) {
-		packets = r->dropped - p->dropped;
-		pps = packets / period_;
-	}
-	return pps;
-}
-
-static __u64 calc_errs_pps(struct datarec *r,
-			    struct datarec *p, double period_)
-{
-	__u64 packets = 0;
-	__u64 pps = 0;
-
-	if (period_ > 0) {
-		packets = r->issue - p->issue;
-		pps = packets / period_;
-	}
-	return pps;
-}
-
-static void calc_xdp_pps(struct datarec *r, struct datarec *p,
-			 double *xdp_pass, double *xdp_drop,
-			 double *xdp_redirect, double period_)
-{
-	*xdp_pass = 0, *xdp_drop = 0, *xdp_redirect = 0;
-	if (period_ > 0) {
-		*xdp_redirect = (r->xdp_redirect - p->xdp_redirect) / period_;
-		*xdp_pass = (r->xdp_pass - p->xdp_pass) / period_;
-		*xdp_drop = (r->xdp_drop - p->xdp_drop) / period_;
-	}
-}
-
-static void stats_print(struct stats_record *stats_rec,
-			struct stats_record *stats_prev,
-			char *prog_name, char *mprog_name, int mprog_fd)
-{
-	unsigned int nr_cpus = bpf_num_possible_cpus();
-	double pps = 0, drop = 0, err = 0;
-	bool mprog_enabled = false;
-	struct record *rec, *prev;
-	int to_cpu;
-	double t;
-	int i;
-
-	if (mprog_fd > 0)
-		mprog_enabled = true;
-
-	/* Header */
-	printf("Running XDP/eBPF prog_name:%s\n", prog_name);
-	printf("%-15s %-7s %-14s %-11s %-9s\n",
-	       "XDP-cpumap", "CPU:to", "pps", "drop-pps", "extra-info");
-
-	/* XDP rx_cnt */
-	{
-		char *fmt_rx = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s\n";
-		char *fm2_rx = "%-15s %-7s %'-14.0f %'-11.0f\n";
-		char *errstr = "";
-
-		rec  = &stats_rec->rx_cnt;
-		prev = &stats_prev->rx_cnt;
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			pps = calc_pps(r, p, t);
-			drop = calc_drop_pps(r, p, t);
-			err  = calc_errs_pps(r, p, t);
-			if (err > 0)
-				errstr = "cpu-dest/err";
-			if (pps > 0)
-				printf(fmt_rx, "XDP-RX",
-					i, pps, drop, err, errstr);
-		}
-		pps  = calc_pps(&rec->total, &prev->total, t);
-		drop = calc_drop_pps(&rec->total, &prev->total, t);
-		err  = calc_errs_pps(&rec->total, &prev->total, t);
-		printf(fm2_rx, "XDP-RX", "total", pps, drop);
-	}
-
-	/* cpumap enqueue stats */
-	for (to_cpu = 0; to_cpu < n_cpus; to_cpu++) {
-		char *fmt = "%-15s %3d:%-3d %'-14.0f %'-11.0f %'-10.2f %s\n";
-		char *fm2 = "%-15s %3s:%-3d %'-14.0f %'-11.0f %'-10.2f %s\n";
-		char *errstr = "";
-
-		rec  =  &stats_rec->enq[to_cpu];
-		prev = &stats_prev->enq[to_cpu];
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			pps  = calc_pps(r, p, t);
-			drop = calc_drop_pps(r, p, t);
-			err  = calc_errs_pps(r, p, t);
-			if (err > 0) {
-				errstr = "bulk-average";
-				err = pps / err; /* calc average bulk size */
-			}
-			if (pps > 0)
-				printf(fmt, "cpumap-enqueue",
-				       i, to_cpu, pps, drop, err, errstr);
-		}
-		pps = calc_pps(&rec->total, &prev->total, t);
-		if (pps > 0) {
-			drop = calc_drop_pps(&rec->total, &prev->total, t);
-			err  = calc_errs_pps(&rec->total, &prev->total, t);
-			if (err > 0) {
-				errstr = "bulk-average";
-				err = pps / err; /* calc average bulk size */
-			}
-			printf(fm2, "cpumap-enqueue",
-			       "sum", to_cpu, pps, drop, err, errstr);
-		}
-	}
-
-	/* cpumap kthread stats */
-	{
-		char *fmt_k = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s\n";
-		char *fm2_k = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f %s\n";
-		char *e_str = "";
-
-		rec  = &stats_rec->kthread;
-		prev = &stats_prev->kthread;
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			pps  = calc_pps(r, p, t);
-			drop = calc_drop_pps(r, p, t);
-			err  = calc_errs_pps(r, p, t);
-			if (err > 0)
-				e_str = "sched";
-			if (pps > 0)
-				printf(fmt_k, "cpumap_kthread",
-				       i, pps, drop, err, e_str);
-		}
-		pps = calc_pps(&rec->total, &prev->total, t);
-		drop = calc_drop_pps(&rec->total, &prev->total, t);
-		err  = calc_errs_pps(&rec->total, &prev->total, t);
-		if (err > 0)
-			e_str = "sched-sum";
-		printf(fm2_k, "cpumap_kthread", "total", pps, drop, err, e_str);
-	}
-
-	/* XDP redirect err tracepoints (very unlikely) */
-	{
-		char *fmt_err = "%-15s %-7d %'-14.0f %'-11.0f\n";
-		char *fm2_err = "%-15s %-7s %'-14.0f %'-11.0f\n";
-
-		rec  = &stats_rec->redir_err;
-		prev = &stats_prev->redir_err;
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			pps  = calc_pps(r, p, t);
-			drop = calc_drop_pps(r, p, t);
-			if (pps > 0)
-				printf(fmt_err, "redirect_err", i, pps, drop);
-		}
-		pps = calc_pps(&rec->total, &prev->total, t);
-		drop = calc_drop_pps(&rec->total, &prev->total, t);
-		printf(fm2_err, "redirect_err", "total", pps, drop);
-	}
-
-	/* XDP general exception tracepoints */
-	{
-		char *fmt_err = "%-15s %-7d %'-14.0f %'-11.0f\n";
-		char *fm2_err = "%-15s %-7s %'-14.0f %'-11.0f\n";
-
-		rec  = &stats_rec->exception;
-		prev = &stats_prev->exception;
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			pps  = calc_pps(r, p, t);
-			drop = calc_drop_pps(r, p, t);
-			if (pps > 0)
-				printf(fmt_err, "xdp_exception", i, pps, drop);
-		}
-		pps = calc_pps(&rec->total, &prev->total, t);
-		drop = calc_drop_pps(&rec->total, &prev->total, t);
-		printf(fm2_err, "xdp_exception", "total", pps, drop);
-	}
-
-	/* CPUMAP attached XDP program that runs on remote/destination CPU */
-	if (mprog_enabled) {
-		char *fmt_k = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f\n";
-		char *fm2_k = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f\n";
-		double xdp_pass, xdp_drop, xdp_redirect;
-
-		printf("\n2nd remote XDP/eBPF prog_name: %s\n", mprog_name);
-		printf("%-15s %-7s %-14s %-11s %-9s\n",
-		       "XDP-cpumap", "CPU:to", "xdp-pass", "xdp-drop", "xdp-redir");
-
-		rec  = &stats_rec->kthread;
-		prev = &stats_prev->kthread;
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			calc_xdp_pps(r, p, &xdp_pass, &xdp_drop,
-				     &xdp_redirect, t);
-			if (xdp_pass > 0 || xdp_drop > 0 || xdp_redirect > 0)
-				printf(fmt_k, "xdp-in-kthread", i, xdp_pass, xdp_drop,
-				       xdp_redirect);
-		}
-		calc_xdp_pps(&rec->total, &prev->total, &xdp_pass, &xdp_drop,
-			     &xdp_redirect, t);
-		printf(fm2_k, "xdp-in-kthread", "total", xdp_pass, xdp_drop, xdp_redirect);
-	}
-
-	printf("\n");
-	fflush(stdout);
-}
-
-static void stats_collect(struct stats_record *rec)
-{
-	int fd, i;
-
-	fd = map_fds[RX_CNT];
-	map_collect_percpu(fd, 0, &rec->rx_cnt);
-
-	fd = map_fds[REDIRECT_ERR_CNT];
-	map_collect_percpu(fd, 1, &rec->redir_err);
-
-	fd = map_fds[CPUMAP_ENQUEUE_CNT];
-	for (i = 0; i < n_cpus; i++)
-		map_collect_percpu(fd, i, &rec->enq[i]);
-
-	fd = map_fds[CPUMAP_KTHREAD_CNT];
-	map_collect_percpu(fd, 0, &rec->kthread);
-
-	fd = map_fds[EXCEPTION_CNT];
-	map_collect_percpu(fd, 0, &rec->exception);
-}
-
-
-/* Pointer swap trick */
-static inline void swap(struct stats_record **a, struct stats_record **b)
-{
-	struct stats_record *tmp;
-
-	tmp = *a;
-	*a = *b;
-	*b = tmp;
-}
-
 static int create_cpu_entry(__u32 cpu, struct bpf_cpumap_val *value,
 			    __u32 avail_idx, bool new)
 {
@@ -579,10 +121,11 @@ static int create_cpu_entry(__u32 cpu, struct bpf_cpumap_val *value,
 	__u32 key = 0;
 	int ret;
 
+	/* Update to bpf_skel */
 	/* Add a CPU entry to cpumap, as this allocate a cpu entry in
 	 * the kernel for the cpu.
 	 */
-	ret = bpf_map_update_elem(map_fds[CPU_MAP], &cpu, value, 0);
+	ret = bpf_map_update_elem(map_fd, &cpu, value, 0);
 	if (ret) {
 		fprintf(stderr, "Create CPU entry failed (err:%d)\n", ret);
 		exit(EXIT_FAIL_BPF);
@@ -591,21 +134,21 @@ static int create_cpu_entry(__u32 cpu, struct bpf_cpumap_val *value,
 	/* Inform bpf_prog's that a new CPU is available to select
 	 * from via some control maps.
 	 */
-	ret = bpf_map_update_elem(map_fds[CPUS_AVAILABLE], &avail_idx, &cpu, 0);
+	ret = bpf_map_update_elem(avail_fd, &avail_idx, &cpu, 0);
 	if (ret) {
 		fprintf(stderr, "Add to avail CPUs failed\n");
 		exit(EXIT_FAIL_BPF);
 	}
 
 	/* When not replacing/updating existing entry, bump the count */
-	ret = bpf_map_lookup_elem(map_fds[CPUS_COUNT], &key, &curr_cpus_count);
+	ret = bpf_map_lookup_elem(count_fd, &key, &curr_cpus_count);
 	if (ret) {
 		fprintf(stderr, "Failed reading curr cpus_count\n");
 		exit(EXIT_FAIL_BPF);
 	}
 	if (new) {
 		curr_cpus_count++;
-		ret = bpf_map_update_elem(map_fds[CPUS_COUNT], &key,
+		ret = bpf_map_update_elem(count_fd, &key,
 					  &curr_cpus_count, 0);
 		if (ret) {
 			fprintf(stderr, "Failed write curr cpus_count\n");
@@ -629,7 +172,7 @@ static void mark_cpus_unavailable(void)
 	int ret, i;
 
 	for (i = 0; i < n_cpus; i++) {
-		ret = bpf_map_update_elem(map_fds[CPUS_AVAILABLE], &i,
+		ret = bpf_map_update_elem(avail_fd, &i,
 					  &invalid_cpu, 0);
 		if (ret) {
 			fprintf(stderr, "Failed marking CPU unavailable\n");
@@ -653,26 +196,33 @@ static void stress_cpumap(struct bpf_cpumap_val *value)
 	create_cpu_entry(1, value, 0, false);
 }
 
-static void stats_poll(int interval, bool use_separators, char *prog_name,
-		       char *mprog_name, struct bpf_cpumap_val *value,
-		       bool stress_mode)
+static void __stats_poll(int interval, bool use_separators, char *prog_name,
+			 char *mprog_name, struct bpf_cpumap_val *value,
+			 bool stress_mode)
 {
+	int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_CNT |
+		   SAMPLE_CPUMAP_ENQUEUE_CNT | SAMPLE_CPUMAP_KTHREAD_CNT |
+		   SAMPLE_EXCEPTION_CNT;
 	struct stats_record *record, *prev;
-	int mprog_fd;
 
 	record = alloc_stats_record();
 	prev   = alloc_stats_record();
-	stats_collect(record);
+	sample_stats_collect(mask, record);
 
 	/* Trick to pretty printf with thousands separators use %' */
 	if (use_separators)
 		setlocale(LC_NUMERIC, "en_US");
 
-	while (1) {
+	for (;;) {
 		swap(&prev, &record);
-		mprog_fd = value->bpf_prog.fd;
-		stats_collect(record);
-		stats_print(record, prev, prog_name, mprog_name, mprog_fd);
+		sample_stats_collect(mask, record);
+		sample_stats_print(mask, record, prev, prog_name);
+		/* Depends on SAMPLE_CPUMAP_KTHREAD_CNT */
+		sample_stats_print_cpumap_remote(record, prev,
+						 bpf_num_possible_cpus(),
+						 mprog_name);
+		printf("\n");
+		fflush(stdout);
 		sleep(interval);
 		if (stress_mode)
 			stress_cpumap(value);
@@ -682,41 +232,6 @@ static void stats_poll(int interval, bool use_separators, char *prog_name,
 	free_stats_record(prev);
 }
 
-static int init_tracepoints(struct bpf_object *obj)
-{
-	struct bpf_program *prog;
-
-	bpf_object__for_each_program(prog, obj) {
-		if (bpf_program__is_tracepoint(prog) != true)
-			continue;
-
-		tp_links[tp_cnt] = bpf_program__attach(prog);
-		if (libbpf_get_error(tp_links[tp_cnt])) {
-			tp_links[tp_cnt] = NULL;
-			return -EINVAL;
-		}
-		tp_cnt++;
-	}
-
-	return 0;
-}
-
-static int init_map_fds(struct bpf_object *obj)
-{
-	enum map_type type;
-
-	for (type = 0; type < NUM_MAP; type++) {
-		map_fds[type] =
-			bpf_object__find_map_fd_by_name(obj,
-							map_type_strings[type]);
-
-		if (map_fds[type] < 0)
-			return -ENOENT;
-	}
-
-	return 0;
-}
-
 static int load_cpumap_prog(char *file_name, char *prog_name,
 			    char *redir_interface, char *redir_map)
 {
@@ -790,8 +305,6 @@ int main(int argc, char **argv)
 	int *cpu, i;
 	__u32 qsize;
 
-	n_cpus = get_nprocs_conf();
-
 	/* Notice: choosing he queue size is very important with the
 	 * ixgbe driver, because it's driver page recycling trick is
 	 * dependend on pages being returned quickly.  The number of
@@ -812,15 +325,20 @@ int main(int argc, char **argv)
 		return err;
 	}
 
-	if (init_tracepoints(obj) < 0) {
-		fprintf(stderr, "ERR: bpf_program__attach failed\n");
+	if (sample_init(obj) < 0) {
+		fprintf(stderr, "ERR: Failed to initialize sample\n");
 		return err;
 	}
 
-	if (init_map_fds(obj) < 0) {
-		fprintf(stderr, "bpf_object__find_map_fd_by_name failed\n");
-		return err;
+	map_fd = bpf_object__find_map_fd_by_name(obj, "cpu_map");
+	avail_fd = bpf_object__find_map_fd_by_name(obj, "cpus_available");
+	count_fd = bpf_object__find_map_fd_by_name(obj, "cpus_count");
+
+	if (map_fd < 0 || avail_fd < 0 || count_fd < 0) {
+		fprintf(stderr, "failed to find map\n");
+		return EXIT_FAIL;
 	}
+
 	mark_cpus_unavailable();
 
 	cpu = malloc(n_cpus * sizeof(int));
@@ -967,8 +485,8 @@ int main(int argc, char **argv)
 	}
 	prog_id = info.id;
 
-	stats_poll(interval, use_separators, prog_name, mprog_name,
-		   &value, stress_mode);
+	__stats_poll(interval, use_separators, prog_name, mprog_name,
+		     &value, stress_mode);
 
 	err = EXIT_OK;
 out:
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
new file mode 100644
index 000000000000..be60fbddd8c7
--- /dev/null
+++ b/samples/bpf/xdp_sample_user.c
@@ -0,0 +1,588 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <locale.h>
+#include <sys/resource.h>
+#include <sys/sysinfo.h>
+#include <getopt.h>
+#include <net/if.h>
+#include <time.h>
+#include <linux/limits.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+#ifndef SIOCETHTOOL
+#define SIOCETHTOOL 0x8946
+#endif
+
+#include <arpa/inet.h>
+#include <linux/if_link.h>
+
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#include "bpf_util.h"
+#include "xdp_sample_user.h"
+
+struct bpf_link *tp_links[NUM_TP] = {};
+int map_fds[NUM_MAP], tp_cnt, n_cpus;
+
+#define NANOSEC_PER_SEC 1000000000 /* 10^9 */
+static __u64 gettime(void)
+{
+	struct timespec t;
+	int res;
+
+	res = clock_gettime(CLOCK_MONOTONIC, &t);
+	if (res < 0) {
+		fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
+		exit(EXIT_FAIL);
+	}
+	return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
+}
+
+static bool map_collect_percpu(int fd, __u32 key, struct record *rec)
+{
+	/* For percpu maps, userspace gets a value per possible CPU */
+	unsigned int nr_cpus = bpf_num_possible_cpus();
+	struct datarec values[nr_cpus];
+	__u64 sum_xdp_redirect = 0;
+	__u64 sum_xdp_pass = 0;
+	__u64 sum_xdp_drop = 0;
+	__u64 sum_processed = 0;
+	__u64 sum_dropped = 0;
+	__u64 sum_issue = 0;
+	int i;
+
+	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
+		fprintf(stderr,
+			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
+		return false;
+	}
+	/* Get time as close as possible to reading map contents */
+	rec->timestamp = gettime();
+
+	/* Record and sum values from each CPU */
+	for (i = 0; i < nr_cpus; i++) {
+		rec->cpu[i].processed = values[i].processed;
+		sum_processed        += values[i].processed;
+		rec->cpu[i].dropped = values[i].dropped;
+		sum_dropped        += values[i].dropped;
+		rec->cpu[i].issue = values[i].issue;
+		sum_issue        += values[i].issue;
+		rec->cpu[i].xdp_pass = values[i].xdp_pass;
+		sum_xdp_pass += values[i].xdp_pass;
+		rec->cpu[i].xdp_drop = values[i].xdp_drop;
+		sum_xdp_drop += values[i].xdp_drop;
+		rec->cpu[i].xdp_redirect = values[i].xdp_redirect;
+		sum_xdp_redirect += values[i].xdp_redirect;
+	}
+	rec->total.processed = sum_processed;
+	rec->total.dropped   = sum_dropped;
+	rec->total.issue     = sum_issue;
+	rec->total.xdp_pass  = sum_xdp_pass;
+	rec->total.xdp_drop  = sum_xdp_drop;
+	rec->total.xdp_redirect = sum_xdp_redirect;
+	return true;
+}
+
+static struct datarec *alloc_record_per_cpu(void)
+{
+	unsigned int nr_cpus = bpf_num_possible_cpus();
+	struct datarec *array;
+
+	array = calloc(nr_cpus, sizeof(struct datarec));
+	if (!array) {
+		fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
+		exit(EXIT_FAIL_MEM);
+	}
+	return array;
+}
+
+struct stats_record *alloc_stats_record(void)
+{
+	struct stats_record *rec;
+	int i, size;
+
+	size = sizeof(*rec) + n_cpus * sizeof(struct record);
+	rec = malloc(size);
+	if (!rec) {
+		fprintf(stderr, "Mem alloc error\n");
+		exit(EXIT_FAIL_MEM);
+	}
+	memset(rec, 0, size);
+	rec->rx_cnt.cpu    = alloc_record_per_cpu();
+	rec->redir_err.cpu = alloc_record_per_cpu();
+	rec->kthread.cpu   = alloc_record_per_cpu();
+	rec->exception.cpu = alloc_record_per_cpu();
+	for (i = 0; i < n_cpus; i++)
+		rec->enq[i].cpu = alloc_record_per_cpu();
+
+	return rec;
+}
+
+void free_stats_record(struct stats_record *r)
+{
+	int i;
+
+	for (i = 0; i < n_cpus; i++)
+		free(r->enq[i].cpu);
+	free(r->exception.cpu);
+	free(r->kthread.cpu);
+	free(r->redir_err.cpu);
+	free(r->rx_cnt.cpu);
+	free(r);
+}
+
+static double calc_period(struct record *r, struct record *p)
+{
+	double period_ = 0;
+	__u64 period = 0;
+
+	period = r->timestamp - p->timestamp;
+	if (period > 0)
+		period_ = ((double) period / NANOSEC_PER_SEC);
+
+	return period_;
+}
+
+static __u64 calc_pps(struct datarec *r, struct datarec *p, double period_)
+{
+	__u64 packets = 0;
+	__u64 pps = 0;
+
+	if (period_ > 0) {
+		packets = r->processed - p->processed;
+		pps = packets / period_;
+	}
+	return pps;
+}
+
+static __u64 calc_drop_pps(struct datarec *r, struct datarec *p, double period_)
+{
+	__u64 packets = 0;
+	__u64 pps = 0;
+
+	if (period_ > 0) {
+		packets = r->dropped - p->dropped;
+		pps = packets / period_;
+	}
+	return pps;
+}
+
+static __u64 calc_errs_pps(struct datarec *r,
+			    struct datarec *p, double period_)
+{
+	__u64 packets = 0;
+	__u64 pps = 0;
+
+	if (period_ > 0) {
+		packets = r->issue - p->issue;
+		pps = packets / period_;
+	}
+	return pps;
+}
+
+static void calc_xdp_pps(struct datarec *r, struct datarec *p,
+			 double *xdp_pass, double *xdp_drop,
+			 double *xdp_redirect, double period_)
+{
+	*xdp_pass = 0, *xdp_drop = 0, *xdp_redirect = 0;
+	if (period_ > 0) {
+		*xdp_redirect = (r->xdp_redirect - p->xdp_redirect) / period_;
+		*xdp_pass = (r->xdp_pass - p->xdp_pass) / period_;
+		*xdp_drop = (r->xdp_drop - p->xdp_drop) / period_;
+	}
+}
+
+static void stats_print_rx_cnt(struct stats_record *stats_rec,
+			       struct stats_record *stats_prev,
+			       unsigned int nr_cpus)
+{
+	char *fmt_rx = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s\n";
+	char *fm2_rx = "%-15s %-7s %'-14.0f %'-11.0f\n";
+	struct record *rec, *prev;
+	double t, pps, drop, err;
+	char *errstr = "";
+	int i;
+
+	rec = &stats_rec->rx_cnt;
+	prev = &stats_prev->rx_cnt;
+	t = calc_period(rec, prev);
+	for (i = 0; i < nr_cpus; i++) {
+		struct datarec *r = &rec->cpu[i];
+		struct datarec *p = &prev->cpu[i];
+
+		pps = calc_pps(r, p, t);
+		drop = calc_drop_pps(r, p, t);
+		err = calc_errs_pps(r, p, t);
+		if (err > 0)
+			errstr = "cpu-dest/err";
+		if (pps > 0)
+			printf(fmt_rx, "XDP-RX", i, pps, drop, err, errstr);
+	}
+	pps = calc_pps(&rec->total, &prev->total, t);
+	drop = calc_drop_pps(&rec->total, &prev->total, t);
+	err = calc_errs_pps(&rec->total, &prev->total, t);
+	printf(fm2_rx, "XDP-RX", "total", pps, drop);
+}
+
+static void stats_print_cpumap_enqueue(struct stats_record *stats_rec,
+				       struct stats_record *stats_prev,
+				       unsigned int nr_cpus)
+{
+	struct record *rec, *prev;
+	double t, pps, drop, err;
+	int i, to_cpu;
+
+	/* cpumap enqueue stats */
+	for (to_cpu = 0; to_cpu < n_cpus; to_cpu++) {
+		char *fmt = "%-15s %3d:%-3d %'-14.0f %'-11.0f %'-10.2f %s\n";
+		char *fm2 = "%-15s %3s:%-3d %'-14.0f %'-11.0f %'-10.2f %s\n";
+		char *errstr = "";
+
+		rec  =  &stats_rec->enq[to_cpu];
+		prev = &stats_prev->enq[to_cpu];
+		t = calc_period(rec, prev);
+		for (i = 0; i < nr_cpus; i++) {
+			struct datarec *r = &rec->cpu[i];
+			struct datarec *p = &prev->cpu[i];
+
+			pps  = calc_pps(r, p, t);
+			drop = calc_drop_pps(r, p, t);
+			err  = calc_errs_pps(r, p, t);
+			if (err > 0) {
+				errstr = "bulk-average";
+				err = pps / err; /* calc average bulk size */
+			}
+			if (pps > 0)
+				printf(fmt, "cpumap-enqueue",
+				       i, to_cpu, pps, drop, err, errstr);
+		}
+		pps = calc_pps(&rec->total, &prev->total, t);
+		if (pps > 0) {
+			drop = calc_drop_pps(&rec->total, &prev->total, t);
+			err  = calc_errs_pps(&rec->total, &prev->total, t);
+			if (err > 0) {
+				errstr = "bulk-average";
+				err = pps / err; /* calc average bulk size */
+			}
+			printf(fm2, "cpumap-enqueue",
+			       "sum", to_cpu, pps, drop, err, errstr);
+		}
+	}
+}
+
+static void stats_print_cpumap_kthread(struct stats_record *stats_rec,
+				       struct stats_record *stats_prev,
+				       unsigned int nr_cpus)
+{
+	char *fmt_k = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s\n";
+	char *fm2_k = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f %s\n";
+	struct record *rec, *prev;
+	double t, pps, drop, err;
+	char *e_str = "";
+	int i;
+
+	rec = &stats_rec->kthread;
+	prev = &stats_prev->kthread;
+	t = calc_period(rec, prev);
+	for (i = 0; i < nr_cpus; i++) {
+		struct datarec *r = &rec->cpu[i];
+		struct datarec *p = &prev->cpu[i];
+
+		pps = calc_pps(r, p, t);
+		drop = calc_drop_pps(r, p, t);
+		err = calc_errs_pps(r, p, t);
+		if (err > 0)
+			e_str = "sched";
+		if (pps > 0)
+			printf(fmt_k, "cpumap_kthread", i, pps, drop, err,
+			       e_str);
+	}
+	pps = calc_pps(&rec->total, &prev->total, t);
+	drop = calc_drop_pps(&rec->total, &prev->total, t);
+	err = calc_errs_pps(&rec->total, &prev->total, t);
+	if (err > 0)
+		e_str = "sched-sum";
+	printf(fm2_k, "cpumap_kthread", "total", pps, drop, err, e_str);
+}
+
+static void stats_print_redirect_err_cnt(struct stats_record *stats_rec,
+					 struct stats_record *stats_prev,
+					 unsigned int nr_cpus)
+{
+	char *fmt_err = "%-15s %-7d %'-14.0f %'-11.0f\n";
+	char *fm2_err = "%-15s %-7s %'-14.0f %'-11.0f\n";
+	struct record *rec, *prev;
+	double t, pps, drop;
+	int i;
+
+	rec = &stats_rec->redir_err;
+	prev = &stats_prev->redir_err;
+	t = calc_period(rec, prev);
+	for (i = 0; i < nr_cpus; i++) {
+		struct datarec *r = &rec->cpu[i];
+		struct datarec *p = &prev->cpu[i];
+
+		pps = calc_pps(r, p, t);
+		drop = calc_drop_pps(r, p, t);
+		if (pps > 0)
+			printf(fmt_err, "redirect_err", i, pps, drop);
+	}
+	pps = calc_pps(&rec->total, &prev->total, t);
+	drop = calc_drop_pps(&rec->total, &prev->total, t);
+	printf(fm2_err, "redirect_err", "total", pps, drop);
+}
+
+static void stats_print_exception_cnt(struct stats_record *stats_rec,
+				      struct stats_record *stats_prev,
+				      unsigned int nr_cpus)
+{
+	char *fmt_err = "%-15s %-7d %'-14.0f %'-11.0f\n";
+	char *fm2_err = "%-15s %-7s %'-14.0f %'-11.0f\n";
+	struct record *rec, *prev;
+	double t, pps, drop;
+	int i;
+
+	rec = &stats_rec->exception;
+	prev = &stats_prev->exception;
+	t = calc_period(rec, prev);
+	for (i = 0; i < nr_cpus; i++) {
+		struct datarec *r = &rec->cpu[i];
+		struct datarec *p = &prev->cpu[i];
+
+		pps = calc_pps(r, p, t);
+		drop = calc_drop_pps(r, p, t);
+		if (pps > 0)
+			printf(fmt_err, "xdp_exception", i, pps, drop);
+	}
+	pps = calc_pps(&rec->total, &prev->total, t);
+	drop = calc_drop_pps(&rec->total, &prev->total, t);
+	printf(fm2_err, "xdp_exception", "total", pps, drop);
+}
+
+void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
+				      struct stats_record *stats_prev,
+				      unsigned int nr_cpus, char *mprog_name)
+{
+	char *fmt_k = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f\n";
+	char *fm2_k = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f\n";
+	double xdp_pass, xdp_drop, xdp_redirect;
+	struct record *rec, *prev;
+	double t;
+	int i;
+
+	printf("\n2nd remote XDP/eBPF prog_name: %s\n", mprog_name ?: "(none)");
+	printf("%-15s %-7s %-14s %-11s %-9s\n", "XDP-cpumap", "CPU:to",
+	       "xdp-pass", "xdp-drop", "xdp-redir");
+
+	rec = &stats_rec->kthread;
+	prev = &stats_prev->kthread;
+	t = calc_period(rec, prev);
+	for (i = 0; i < nr_cpus; i++) {
+		struct datarec *r = &rec->cpu[i];
+		struct datarec *p = &prev->cpu[i];
+
+		calc_xdp_pps(r, p, &xdp_pass, &xdp_drop, &xdp_redirect, t);
+		if (xdp_pass > 0 || xdp_drop > 0 || xdp_redirect > 0)
+			printf(fmt_k, "xdp-in-kthread", i, xdp_pass, xdp_drop,
+			       xdp_redirect);
+	}
+	calc_xdp_pps(&rec->total, &prev->total, &xdp_pass, &xdp_drop,
+		     &xdp_redirect, t);
+	printf(fm2_k, "xdp-in-kthread", "total", xdp_pass, xdp_drop,
+	       xdp_redirect);
+}
+
+static int init_tracepoints(struct bpf_object *obj)
+{
+	struct bpf_program *prog;
+
+	bpf_object__for_each_program(prog, obj) {
+		if (bpf_program__is_tracepoint(prog) != true)
+			continue;
+
+		tp_links[tp_cnt] = bpf_program__attach(prog);
+		if (libbpf_get_error(tp_links[tp_cnt])) {
+			tp_links[tp_cnt] = NULL;
+			return -EINVAL;
+		}
+		tp_cnt++;
+	}
+
+	return 0;
+}
+
+static int init_map_fds(struct bpf_object *obj)
+{
+	enum map_type type;
+
+	for (type = 0; type < NUM_MAP; type++) {
+		map_fds[type] =
+			bpf_object__find_map_fd_by_name(obj,
+							map_type_strings[type]);
+
+		if (map_fds[type] < 0)
+			return -ENOENT;
+	}
+
+	return 0;
+}
+
+int sample_init(struct bpf_object *obj)
+{
+	n_cpus = get_nprocs_conf();
+	return init_tracepoints(obj) ? : init_map_fds(obj);
+}
+
+void sample_exit(int status)
+{
+	while (tp_cnt)
+		bpf_link__destroy(tp_links[--tp_cnt]);
+
+	exit(status);
+}
+
+void sample_stats_collect(int mask, struct stats_record *rec)
+{
+	int i;
+
+	if (mask & SAMPLE_RX_CNT)
+		map_collect_percpu(map_fds[RX_CNT], 0, &rec->rx_cnt);
+
+	if (mask & SAMPLE_REDIRECT_ERR_CNT)
+		map_collect_percpu(map_fds[REDIRECT_ERR_CNT], 1, &rec->redir_err);
+
+	if (mask & SAMPLE_CPUMAP_ENQUEUE_CNT)
+		for (i = 0; i < n_cpus; i++)
+			map_collect_percpu(map_fds[CPUMAP_ENQUEUE_CNT], i, &rec->enq[i]);
+
+	if (mask & SAMPLE_CPUMAP_KTHREAD_CNT)
+		map_collect_percpu(map_fds[CPUMAP_KTHREAD_CNT], 0, &rec->kthread);
+
+	if (mask & SAMPLE_EXCEPTION_CNT)
+		map_collect_percpu(map_fds[EXCEPTION_CNT], 0, &rec->exception);
+}
+
+void sample_stats_print(int mask, struct stats_record *cur,
+			struct stats_record *prev, char *prog_name)
+{
+	int nr_cpus = bpf_num_possible_cpus();
+
+	printf("Running XDP/eBPF prog_name:%s\n", prog_name ?: "(none)");
+	printf("%-15s %-7s %-14s %-11s %-9s\n",
+	       "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info");
+
+	if (mask & SAMPLE_RX_CNT)
+		stats_print_rx_cnt(cur, prev, nr_cpus);
+
+	if (mask & SAMPLE_REDIRECT_ERR_CNT)
+		stats_print_redirect_err_cnt(cur, prev, nr_cpus);
+
+	if (mask & SAMPLE_CPUMAP_ENQUEUE_CNT)
+		stats_print_cpumap_enqueue(cur, prev, nr_cpus);
+
+	if (mask & SAMPLE_CPUMAP_KTHREAD_CNT)
+		stats_print_cpumap_kthread(cur, prev, nr_cpus);
+
+	if (mask & SAMPLE_EXCEPTION_CNT)
+		stats_print_exception_cnt(cur, prev, nr_cpus);
+}
+
+void sample_stats_poll(int interval, int mask, char *prog_name, int use_separators)
+{
+	struct stats_record *record, *prev;
+
+	record = alloc_stats_record();
+	prev   = alloc_stats_record();
+	sample_stats_collect(mask, record);
+
+	/* Trick to pretty printf with thousands separators use %' */
+	if (use_separators)
+		setlocale(LC_NUMERIC, "en_US");
+
+	for (;;) {
+		swap(&prev, &record);
+		sample_stats_collect(mask, record);
+		sample_stats_print(mask, record, prev, NULL);
+		printf("\n");
+		fflush(stdout);
+		sleep(interval);
+	}
+
+	free_stats_record(record);
+	free_stats_record(prev);
+}
+
+const char *get_driver_name(int ifindex)
+{
+	struct ethtool_drvinfo drv = {};
+	char ifname[IF_NAMESIZE];
+	static char drvname[32];
+	struct ifreq ifr = {};
+	int fd, r;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return NULL;
+
+	if (!if_indextoname(ifindex, ifname))
+		goto end;
+
+	drv.cmd = ETHTOOL_GDRVINFO;
+	strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
+	ifr.ifr_data = (void *)&drv;
+
+	r = ioctl(fd, SIOCETHTOOL, &ifr);
+	if (r)
+		goto end;
+
+	strncpy(drvname, drv.driver, sizeof(drvname));
+
+	close(fd);
+	return drvname;
+
+end:
+	close(fd);
+	return NULL;
+}
+
+int get_mac_addr(int ifindex, void *mac_addr)
+{
+	char ifname[IF_NAMESIZE];
+	struct ifreq ifr = {};
+	int fd, r;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return -errno;
+
+	if (!if_indextoname(ifindex, ifname)) {
+		r = -errno;
+		goto end;
+	}
+
+	strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
+
+	r = ioctl(fd, SIOCGIFHWADDR, &ifr);
+	if (r) {
+		r = -errno;
+		goto end;
+	}
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+
+end:
+	close(fd);
+	return r;
+}
diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h
new file mode 100644
index 000000000000..3427baf70fc0
--- /dev/null
+++ b/samples/bpf/xdp_sample_user.h
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#pragma once
+
+#include <bpf/libbpf.h>
+
+enum map_type {
+	RX_CNT,
+	REDIRECT_ERR_CNT,
+	CPUMAP_ENQUEUE_CNT,
+	CPUMAP_KTHREAD_CNT,
+	EXCEPTION_CNT,
+	NUM_MAP,
+};
+
+enum tp_type {
+	TP_REDIRECT_ERR_CNT,
+	TP_REDIRECT_MAP_ERR_CNT,
+	TP_CPUMAP_ENQUEUE_CNT,
+	TP_CPUMAP_KTHREAD_CNT,
+	TP_EXCEPTION_CNT,
+	NUM_TP,
+};
+
+enum stats_mask {
+	SAMPLE_RX_CNT	        = 1U << 1,
+	SAMPLE_REDIRECT_ERR_CNT	= 1U << 2,
+	SAMPLE_CPUMAP_ENQUEUE_CNT  = 1U << 3,
+	SAMPLE_CPUMAP_KTHREAD_CNT  = 1U << 4,
+	SAMPLE_EXCEPTION_CNT	= 1U << 5,
+};
+
+static const char *const map_type_strings[] = {
+	[RX_CNT] = "rx_cnt",
+	[REDIRECT_ERR_CNT] = "redirect_err_cnt",
+	[CPUMAP_ENQUEUE_CNT] = "cpumap_enqueue_cnt",
+	[CPUMAP_KTHREAD_CNT] = "cpumap_kthread_cnt",
+	[EXCEPTION_CNT] = "exception_cnt",
+};
+
+extern struct bpf_link *tp_links[NUM_TP];
+extern int map_fds[NUM_MAP];
+extern int n_cpus;
+extern int tp_cnt;
+
+/* Exit return codes */
+#define EXIT_OK			0
+#define EXIT_FAIL		1
+#define EXIT_FAIL_OPTION	2
+#define EXIT_FAIL_XDP		3
+#define EXIT_FAIL_BPF		4
+#define EXIT_FAIL_MEM		5
+
+/* Common stats data record shared with _kern.c */
+struct datarec {
+	__u64 processed;
+	__u64 dropped;
+	__u64 issue;
+	__u64 xdp_pass;
+	__u64 xdp_drop;
+	__u64 xdp_redirect;
+};
+
+struct record {
+	__u64 timestamp;
+	struct datarec total;
+	struct datarec *cpu;
+};
+
+struct stats_record {
+	struct record rx_cnt;
+	struct record redir_err;
+	struct record kthread;
+	struct record exception;
+	struct record enq[];
+};
+
+int sample_init(struct bpf_object *obj);
+void sample_exit(int status);
+struct stats_record *alloc_stats_record(void);
+void free_stats_record(struct stats_record *rec);
+void sample_stats_print(int mask, struct stats_record *cur,
+			struct stats_record *prev, char *prog_name);
+void sample_stats_collect(int mask, struct stats_record *rec);
+void sample_stats_poll(int interval, int mask, char *prog_name,
+		       int use_separators);
+void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
+				      struct stats_record *stats_prev,
+				      unsigned int nr_cpus, char *mprog_name);
+
+const char *get_driver_name(int ifindex);
+int get_mac_addr(int ifindex, void *mac_addr);
+
+/* Pointer swap trick */
+static inline void swap(struct stats_record **a, struct stats_record **b)
+{
+	struct stats_record *tmp;
+
+	tmp = *a;
+	*a = *b;
+	*b = tmp;
+}
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 05/15] samples: bpf: convert xdp_redirect_map to use xdp_samples
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (3 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 04/15] samples: bpf: refactor generic parts out of xdp_redirect_cpu_user Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 06/15] samples: bpf: prepare devmap_xmit support in xdp_sample Kumar Kartikeya Dwivedi
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This uses the xdp_sample_* reorg we did in the past commits to report
more statistics than just the per second packet count.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/Makefile                |  2 +-
 samples/bpf/xdp_redirect_map_kern.c | 23 +++----
 samples/bpf/xdp_redirect_map_user.c | 96 +++++++++++------------------
 3 files changed, 44 insertions(+), 77 deletions(-)

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index c0c02e12e28b..ea7100c8b760 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -99,8 +99,8 @@ xdp_tx_iptunnel-objs := xdp_tx_iptunnel_user.o
 test_map_in_map-objs := test_map_in_map_user.o
 per_socket_stats_example-objs := cookie_uid_helper_example.o
 xdp_redirect-objs := xdp_redirect_user.o
-xdp_redirect_map-objs := xdp_redirect_map_user.o
 xdp_redirect_map_multi-objs := xdp_redirect_map_multi_user.o
+xdp_redirect_map-objs := xdp_redirect_map_user.o xdp_sample_user.o
 xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o xdp_sample_user.o
 xdp_monitor-objs := xdp_monitor_user.o
 xdp_rxq_info-objs := xdp_rxq_info_user.o
diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index a92b8e567bdd..cf8b8f6d15da 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,6 +19,8 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+#include "xdp_sample_kern.h"
+
 /* The 2nd xdp prog on egress does not support skb mode, so we define two
  * maps, tx_port_general and tx_port_native.
  */
@@ -36,16 +38,6 @@ struct {
 	__uint(max_entries, 100);
 } tx_port_native SEC(".maps");
 
-/* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
- * feedback.  Redirect TX errors can be caught via a tracepoint.
- */
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, long);
-	__uint(max_entries, 1);
-} rxcnt SEC(".maps");
-
 /* map to store egress interface mac address */
 struct {
 	__uint(type, BPF_MAP_TYPE_ARRAY);
@@ -75,7 +67,7 @@ static __always_inline int xdp_redirect_map(struct xdp_md *ctx, void *redirect_m
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
-	int rc = XDP_DROP;
+	struct datarec *rec;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
@@ -83,15 +75,16 @@ static __always_inline int xdp_redirect_map(struct xdp_md *ctx, void *redirect_m
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
-		return rc;
+		return XDP_DROP;
 
 	/* constant virtual port */
 	vport = 0;
 
 	/* count packet in global counter */
-	value = bpf_map_lookup_elem(&rxcnt, &key);
-	if (value)
-		*value += 1;
+	rec = bpf_map_lookup_elem(&rx_cnt, &key);
+	if (!rec)
+		return XDP_ABORTED;
+	rec->processed++;
 
 	swap_src_dst_mac(data);
 
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index ad3cdc4c07d3..42893385ba96 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -13,15 +13,11 @@
 #include <net/if.h>
 #include <unistd.h>
 #include <libgen.h>
-#include <sys/resource.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
 #include <bpf/libbpf.h>
+#include "xdp_sample_user.h"
 
 static int ifindex_in;
 static int ifindex_out;
@@ -31,7 +27,6 @@ static __u32 prog_id;
 static __u32 dummy_prog_id;
 
 static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
-static int rxcnt_map_fd;
 
 static void int_exit(int sig)
 {
@@ -62,56 +57,8 @@ static void int_exit(int sig)
 		else
 			printf("program on iface OUT changed, not removing\n");
 	}
-	exit(0);
-}
 
-static void poll_stats(int interval, int ifindex)
-{
-	unsigned int nr_cpus = bpf_num_possible_cpus();
-	__u64 values[nr_cpus], prev[nr_cpus];
-
-	memset(prev, 0, sizeof(prev));
-
-	while (1) {
-		__u64 sum = 0;
-		__u32 key = 0;
-		int i;
-
-		sleep(interval);
-		assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0);
-		for (i = 0; i < nr_cpus; i++)
-			sum += (values[i] - prev[i]);
-		if (sum)
-			printf("ifindex %i: %10llu pkt/s\n",
-			       ifindex, sum / interval);
-		memcpy(prev, values, sizeof(values));
-	}
-}
-
-static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
-{
-	char ifname[IF_NAMESIZE];
-	struct ifreq ifr;
-	int fd, ret = -1;
-
-	fd = socket(AF_INET, SOCK_DGRAM, 0);
-	if (fd < 0)
-		return ret;
-
-	if (!if_indextoname(ifindex_out, ifname))
-		goto err_out;
-
-	strcpy(ifr.ifr_name, ifname);
-
-	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
-		goto err_out;
-
-	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
-	ret = 0;
-
-err_out:
-	close(fd);
-	return ret;
+	sample_exit(EXIT_OK);
 }
 
 static void usage(const char *prog)
@@ -128,6 +75,8 @@ static void usage(const char *prog)
 
 int main(int argc, char **argv)
 {
+	int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_CNT |
+		   SAMPLE_EXCEPTION_CNT;
 	struct bpf_prog_load_attr prog_load_attr = {
 		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
@@ -136,8 +85,11 @@ int main(int argc, char **argv)
 	int tx_port_map_fd, tx_mac_map_fd;
 	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
+	char str[2 * IF_NAMESIZE + 1];
 	__u32 info_len = sizeof(info);
+	char ifname_out[IF_NAMESIZE];
 	const char *optstr = "FSNX";
+	char ifname_in[IF_NAMESIZE];
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
@@ -182,14 +134,17 @@ int main(int argc, char **argv)
 	if (!ifindex_out)
 		ifindex_out = strtoul(argv[optind + 1], NULL, 0);
 
-	printf("input: %d output: %d\n", ifindex_in, ifindex_out);
-
 	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
 	prog_load_attr.file = filename;
 
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
+	if (sample_init(obj) < 0) {
+		fprintf(stderr, "Failed to initialize sample\n");
+		return 1;
+	}
+
 	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
 		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
 		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
@@ -210,8 +165,7 @@ int main(int argc, char **argv)
 	}
 
 	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
-	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
+	if (tx_mac_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -281,8 +235,28 @@ int main(int argc, char **argv)
 		goto out;
 	}
 
-	poll_stats(2, ifindex_out);
+	if (!if_indextoname(ifindex_in, ifname_in)) {
+		perror("if_nametoindex");
+		goto out;
+	}
+
+	if (!if_indextoname(ifindex_out, ifname_out)) {
+		perror("if_nametoindex");
+		goto out;
+	}
+
+	strncpy(str, get_driver_name(ifindex_in) ?: "(err)", sizeof(str));
+
+	printf("Redirecting from %s (ifindex %d; driver %s) to %s (ifindex %d; driver %s)\n",
+	       ifname_in, ifindex_in, str, ifname_out, ifindex_out,
+	       get_driver_name(ifindex_out) ?: "(err)");
+
+	snprintf(str, sizeof(str), "%s->%s", ifname_in, ifname_out);
+
+	sample_stats_poll(1, mask, str, true);
 
-out:
 	return 0;
+
+out:
+	return 1;
 }
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 06/15] samples: bpf: prepare devmap_xmit support in xdp_sample
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (4 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 05/15] samples: bpf: convert xdp_redirect_map to use xdp_samples Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 07/15] samples: bpf: add extended reporting for xdp redirect error Kumar Kartikeya Dwivedi
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This will be used to convert xdp_monitor to xdp_sample reorg in the next
patch.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_redirect_map_user.c |  3 +-
 samples/bpf/xdp_sample_kern.h       | 51 +++++++++++++++++++++-
 samples/bpf/xdp_sample_user.c       | 68 +++++++++++++++++++++++++++++
 samples/bpf/xdp_sample_user.h       | 10 ++++-
 4 files changed, 129 insertions(+), 3 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 42893385ba96..b2c7adad99ec 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -76,7 +76,7 @@ static void usage(const char *prog)
 int main(int argc, char **argv)
 {
 	int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_CNT |
-		   SAMPLE_EXCEPTION_CNT;
+		   SAMPLE_EXCEPTION_CNT | SAMPLE_DEVMAP_XMIT_CNT;
 	struct bpf_prog_load_attr prog_load_attr = {
 		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
@@ -148,6 +148,7 @@ int main(int argc, char **argv)
 	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
 		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
 		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+		mask &= ~SAMPLE_DEVMAP_XMIT_CNT;
 	} else {
 		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
 		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
diff --git a/samples/bpf/xdp_sample_kern.h b/samples/bpf/xdp_sample_kern.h
index bb809542ac20..3b85d71434d3 100644
--- a/samples/bpf/xdp_sample_kern.h
+++ b/samples/bpf/xdp_sample_kern.h
@@ -12,7 +12,10 @@ struct datarec {
 	__u64 processed;
 	__u64 dropped;
 	__u64 issue;
-	__u64 xdp_pass;
+	union {
+		__u64 xdp_pass;
+		__u64 info;
+	};
 	__u64 xdp_drop;
 	__u64 xdp_redirect;
 };
@@ -60,6 +63,13 @@ struct {
 	__uint(max_entries, 1);
 } exception_cnt SEC(".maps");
 
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, u32);
+	__type(value, struct datarec);
+	__uint(max_entries, 1);
+} devmap_xmit_cnt SEC(".maps");
+
 /*** Trace point code ***/
 
 /* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_redirect/format
@@ -218,3 +228,42 @@ int trace_xdp_cpumap_kthread(struct cpumap_kthread_ctx *ctx)
 
 	return 0;
 }
+
+/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_devmap_xmit/format
+ * Code in:         kernel/include/trace/events/xdp.h
+ */
+struct devmap_xmit_ctx {
+	u64 __pad;		// First 8 bytes are not accessible by bpf code
+	int from_ifindex;	//	offset:8;  size:4; signed:1;
+	u32 act;		//	offset:12; size:4; signed:0;
+	int to_ifindex;		//	offset:16; size:4; signed:1;
+	int drops;		//	offset:20; size:4; signed:1;
+	int sent;		//	offset:24; size:4; signed:1;
+	int err;		//	offset:28; size:4; signed:1;
+};
+
+SEC("tracepoint/xdp/xdp_devmap_xmit")
+int trace_xdp_devmap_xmit(struct devmap_xmit_ctx *ctx)
+{
+	struct datarec *rec;
+	u32 key = 0;
+
+	rec = bpf_map_lookup_elem(&devmap_xmit_cnt, &key);
+	if (!rec)
+		return 0;
+	rec->processed += ctx->sent;
+	rec->dropped   += ctx->drops;
+
+	/* Record bulk events, then userspace can calc average bulk size */
+	rec->info += 1;
+
+	/* Record error cases, where no frame were sent */
+	if (ctx->err)
+		rec->issue++;
+
+	/* Catch API error of drv ndo_xdp_xmit sent more than count */
+	if (ctx->drops < 0)
+		rec->issue++;
+
+	return 1;
+}
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
index be60fbddd8c7..56cd79ba303a 100644
--- a/samples/bpf/xdp_sample_user.c
+++ b/samples/bpf/xdp_sample_user.c
@@ -124,6 +124,7 @@ struct stats_record *alloc_stats_record(void)
 	rec->redir_err.cpu = alloc_record_per_cpu();
 	rec->kthread.cpu   = alloc_record_per_cpu();
 	rec->exception.cpu = alloc_record_per_cpu();
+	rec->devmap_xmit.cpu = alloc_record_per_cpu();
 	for (i = 0; i < n_cpus; i++)
 		rec->enq[i].cpu = alloc_record_per_cpu();
 
@@ -136,6 +137,7 @@ void free_stats_record(struct stats_record *r)
 
 	for (i = 0; i < n_cpus; i++)
 		free(r->enq[i].cpu);
+	free(r->devmap_xmit.cpu);
 	free(r->exception.cpu);
 	free(r->kthread.cpu);
 	free(r->redir_err.cpu);
@@ -192,6 +194,19 @@ static __u64 calc_errs_pps(struct datarec *r,
 	return pps;
 }
 
+static __u64 calc_info_pps(struct datarec *r,
+			   struct datarec *p, double period_)
+{
+	__u64 packets = 0;
+	__u64 pps = 0;
+
+	if (period_ > 0) {
+		packets = r->info - p->info;
+		pps = packets / period_;
+	}
+	return pps;
+}
+
 static void calc_xdp_pps(struct datarec *r, struct datarec *p,
 			 double *xdp_pass, double *xdp_drop,
 			 double *xdp_redirect, double period_)
@@ -404,6 +419,53 @@ void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
 	       xdp_redirect);
 }
 
+static void stats_print_devmap_xmit(struct stats_record *stats_rec,
+				    struct stats_record *stats_prev,
+				    unsigned int nr_cpus)
+{
+	char *fmt1 = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s %s\n";
+	char *fmt2 = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f %s %s\n";
+	double pps, drop, info, err;
+	struct record *rec, *prev;
+	char *err_str = "";
+	char *i_str = "";
+	double t;
+	int i;
+
+	rec = &stats_rec->devmap_xmit;
+	prev = &stats_prev->devmap_xmit;
+	t = calc_period(rec, prev);
+	for (i = 0; i < nr_cpus; i++) {
+		struct datarec *r = &rec->cpu[i];
+		struct datarec *p = &prev->cpu[i];
+
+		pps = calc_pps(r, p, t);
+		drop = calc_drop_pps(r, p, t);
+		info = calc_info_pps(r, p, t);
+		err = calc_errs_pps(r, p, t);
+		if (info > 0) {
+			i_str = "bulk-average";
+			info = (pps + drop) / info; /* calc avg bulk */
+		}
+		if (err > 0)
+			err_str = "drv-err";
+		if (pps > 0 || drop > 0)
+			printf(fmt1, "devmap-xmit", i, pps, drop, info, i_str,
+			       err_str);
+	}
+	pps = calc_pps(&rec->total, &prev->total, t);
+	drop = calc_drop_pps(&rec->total, &prev->total, t);
+	info = calc_info_pps(&rec->total, &prev->total, t);
+	err = calc_errs_pps(&rec->total, &prev->total, t);
+	if (info > 0) {
+		i_str = "bulk-average";
+		info = (pps + drop) / info; /* calc avg bulk */
+	}
+	if (err > 0)
+		err_str = "drv-err";
+	printf(fmt2, "devmap-xmit", "total", pps, drop, info, i_str, err_str);
+}
+
 static int init_tracepoints(struct bpf_object *obj)
 {
 	struct bpf_program *prog;
@@ -472,6 +534,9 @@ void sample_stats_collect(int mask, struct stats_record *rec)
 
 	if (mask & SAMPLE_EXCEPTION_CNT)
 		map_collect_percpu(map_fds[EXCEPTION_CNT], 0, &rec->exception);
+
+	if (mask & SAMPLE_DEVMAP_XMIT_CNT)
+		map_collect_percpu(map_fds[DEVMAP_XMIT_CNT], 0, &rec->devmap_xmit);
 }
 
 void sample_stats_print(int mask, struct stats_record *cur,
@@ -497,6 +562,9 @@ void sample_stats_print(int mask, struct stats_record *cur,
 
 	if (mask & SAMPLE_EXCEPTION_CNT)
 		stats_print_exception_cnt(cur, prev, nr_cpus);
+
+	if (mask & SAMPLE_DEVMAP_XMIT_CNT)
+		stats_print_devmap_xmit(cur, prev, nr_cpus);
 }
 
 void sample_stats_poll(int interval, int mask, char *prog_name, int use_separators)
diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h
index 3427baf70fc0..75a4ea4b55ad 100644
--- a/samples/bpf/xdp_sample_user.h
+++ b/samples/bpf/xdp_sample_user.h
@@ -9,6 +9,7 @@ enum map_type {
 	CPUMAP_ENQUEUE_CNT,
 	CPUMAP_KTHREAD_CNT,
 	EXCEPTION_CNT,
+	DEVMAP_XMIT_CNT,
 	NUM_MAP,
 };
 
@@ -18,6 +19,7 @@ enum tp_type {
 	TP_CPUMAP_ENQUEUE_CNT,
 	TP_CPUMAP_KTHREAD_CNT,
 	TP_EXCEPTION_CNT,
+	TP_DEVMAP_XMIT_CNT,
 	NUM_TP,
 };
 
@@ -27,6 +29,7 @@ enum stats_mask {
 	SAMPLE_CPUMAP_ENQUEUE_CNT  = 1U << 3,
 	SAMPLE_CPUMAP_KTHREAD_CNT  = 1U << 4,
 	SAMPLE_EXCEPTION_CNT	= 1U << 5,
+	SAMPLE_DEVMAP_XMIT_CNT  = 1U << 6,
 };
 
 static const char *const map_type_strings[] = {
@@ -35,6 +38,7 @@ static const char *const map_type_strings[] = {
 	[CPUMAP_ENQUEUE_CNT] = "cpumap_enqueue_cnt",
 	[CPUMAP_KTHREAD_CNT] = "cpumap_kthread_cnt",
 	[EXCEPTION_CNT] = "exception_cnt",
+	[DEVMAP_XMIT_CNT] = "devmap_xmit_cnt",
 };
 
 extern struct bpf_link *tp_links[NUM_TP];
@@ -55,7 +59,10 @@ struct datarec {
 	__u64 processed;
 	__u64 dropped;
 	__u64 issue;
-	__u64 xdp_pass;
+	union {
+		__u64 xdp_pass;
+		__u64 info;
+	};
 	__u64 xdp_drop;
 	__u64 xdp_redirect;
 };
@@ -71,6 +78,7 @@ struct stats_record {
 	struct record redir_err;
 	struct record kthread;
 	struct record exception;
+	struct record devmap_xmit;
 	struct record enq[];
 };
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 07/15] samples: bpf: add extended reporting for xdp redirect error
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (5 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 06/15] samples: bpf: prepare devmap_xmit support in xdp_sample Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 08/15] samples: bpf: add per exception reporting for xdp_exception Kumar Kartikeya Dwivedi
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This again is needed for xdp_monitor support. We also record the most
common errnos, but don't report for now. A future commit that modifies
output format will arrange for printing it properly.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_sample_kern.h | 53 +++++++++++++++++++++++++++---
 samples/bpf/xdp_sample_user.c | 62 +++++++++++++++++++++++++++--------
 samples/bpf/xdp_sample_user.h | 13 +++++++-
 3 files changed, 108 insertions(+), 20 deletions(-)

diff --git a/samples/bpf/xdp_sample_kern.h b/samples/bpf/xdp_sample_kern.h
index 3b85d71434d3..4131b9cb1ec4 100644
--- a/samples/bpf/xdp_sample_kern.h
+++ b/samples/bpf/xdp_sample_kern.h
@@ -7,6 +7,11 @@
 
 #define MAX_CPUS 64
 
+#define EINVAL 22
+#define ENETDOWN 100
+#define EMSGSIZE 90
+#define EOPNOTSUPP 95
+
 /* Common stats data record to keep userspace more simple */
 struct datarec {
 	__u64 processed;
@@ -35,8 +40,11 @@ struct {
 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
 	__type(key, u32);
 	__type(value, struct datarec);
-	__uint(max_entries, 2);
-	/* TODO: have entries for all possible errno's */
+	__uint(max_entries, 2
+			    + 1 /* EINVAL */
+			    + 1 /* ENETDOWN */
+			    + 1 /* EMSGSIZE */
+			    + 1 /* EOPNOTSUPP */);
 } redirect_err_cnt SEC(".maps");
 
 /* Used by trace point */
@@ -91,6 +99,25 @@ enum {
 	XDP_REDIRECT_ERROR = 1
 };
 
+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;
+	default:
+		return 1;
+	}
+}
+
 static __always_inline
 int xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx)
 {
@@ -98,13 +125,15 @@ int xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx)
 	struct datarec *rec;
 	int err = ctx->err;
 
-	if (!err)
-		key = XDP_REDIRECT_SUCCESS;
+	key = xdp_get_err_key(err);
 
 	rec = bpf_map_lookup_elem(&redirect_err_cnt, &key);
 	if (!rec)
 		return 0;
-	rec->dropped += 1;
+	if (key)
+		rec->dropped++;
+	else
+		rec->processed++;
 
 	return 0; /* Indicate event was filtered (no further processing)*/
 	/*
@@ -127,6 +156,20 @@ int trace_xdp_redirect_map_err(struct xdp_redirect_ctx *ctx)
 	return xdp_redirect_collect_stat(ctx);
 }
 
+/* Likely unloaded when prog starts */
+SEC("tracepoint/xdp/xdp_redirect")
+int trace_xdp_redirect(struct xdp_redirect_ctx *ctx)
+{
+	return xdp_redirect_collect_stat(ctx);
+}
+
+/* Likely unloaded when prog starts */
+SEC("tracepoint/xdp/xdp_redirect_map")
+int trace_xdp_redirect_map(struct xdp_redirect_ctx *ctx)
+{
+	return xdp_redirect_collect_stat(ctx);
+}
+
 /* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_exception/format
  * Code in:                kernel/include/trace/events/xdp.h
  */
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
index 56cd79ba303a..29410d551574 100644
--- a/samples/bpf/xdp_sample_user.c
+++ b/samples/bpf/xdp_sample_user.c
@@ -121,7 +121,8 @@ struct stats_record *alloc_stats_record(void)
 	}
 	memset(rec, 0, size);
 	rec->rx_cnt.cpu    = alloc_record_per_cpu();
-	rec->redir_err.cpu = alloc_record_per_cpu();
+	rec->redir_err[0].cpu = alloc_record_per_cpu();
+	rec->redir_err[1].cpu = alloc_record_per_cpu();
 	rec->kthread.cpu   = alloc_record_per_cpu();
 	rec->exception.cpu = alloc_record_per_cpu();
 	rec->devmap_xmit.cpu = alloc_record_per_cpu();
@@ -140,7 +141,8 @@ void free_stats_record(struct stats_record *r)
 	free(r->devmap_xmit.cpu);
 	free(r->exception.cpu);
 	free(r->kthread.cpu);
-	free(r->redir_err.cpu);
+	free(r->redir_err[1].cpu);
+	free(r->redir_err[0].cpu);
 	free(r->rx_cnt.cpu);
 	free(r);
 }
@@ -332,31 +334,54 @@ static void stats_print_cpumap_kthread(struct stats_record *stats_rec,
 	printf(fm2_k, "cpumap_kthread", "total", pps, drop, err, e_str);
 }
 
+static void stats_print_redirect_cnt(struct stats_record *stats_rec,
+				     struct stats_record *stats_prev,
+				     unsigned int nr_cpus)
+{
+	char *fmt1 = "%-15s %-7d %'-14.0f %'-11.0f %s\n";
+	char *fmt2 = "%-15s %-7s %'-14.0f %'-11.0f %s\n";
+	struct record *rec, *prev;
+	double t, pps;
+	int i;
+
+	rec = &stats_rec->redir_err[0];
+	prev = &stats_prev->redir_err[0];
+	t = calc_period(rec, prev);
+	for (i = 0; i < nr_cpus; i++) {
+		struct datarec *r = &rec->cpu[i];
+		struct datarec *p = &prev->cpu[i];
+
+		pps = calc_pps(r, p, t);
+		if (pps > 0)
+			printf(fmt1, "redirect", i, pps, 0.0, "Success");
+	}
+	pps = calc_pps(&rec->total, &prev->total, t);
+	printf(fmt2, "redirect", "total", pps, 0.0, "Success");
+}
+
 static void stats_print_redirect_err_cnt(struct stats_record *stats_rec,
 					 struct stats_record *stats_prev,
 					 unsigned int nr_cpus)
 {
-	char *fmt_err = "%-15s %-7d %'-14.0f %'-11.0f\n";
-	char *fm2_err = "%-15s %-7s %'-14.0f %'-11.0f\n";
+	char *fmt1 = "%-15s %-7d %'-14.0f %'-11.0f %s\n";
+	char *fmt2 = "%-15s %-7s %'-14.0f %'-11.0f %s\n";
 	struct record *rec, *prev;
-	double t, pps, drop;
+	double t, drop;
 	int i;
 
-	rec = &stats_rec->redir_err;
-	prev = &stats_prev->redir_err;
+	rec = &stats_rec->redir_err[1];
+	prev = &stats_prev->redir_err[1];
 	t = calc_period(rec, prev);
 	for (i = 0; i < nr_cpus; i++) {
 		struct datarec *r = &rec->cpu[i];
 		struct datarec *p = &prev->cpu[i];
 
-		pps = calc_pps(r, p, t);
 		drop = calc_drop_pps(r, p, t);
-		if (pps > 0)
-			printf(fmt_err, "redirect_err", i, pps, drop);
+		if (drop > 0)
+			printf(fmt1, "redirect", i, 0.0, drop, "Error");
 	}
-	pps = calc_pps(&rec->total, &prev->total, t);
 	drop = calc_drop_pps(&rec->total, &prev->total, t);
-	printf(fm2_err, "redirect_err", "total", pps, drop);
+	printf(fmt2, "redirect", "total", 0.0, drop, "Error");
 }
 
 static void stats_print_exception_cnt(struct stats_record *stats_rec,
@@ -522,8 +547,14 @@ void sample_stats_collect(int mask, struct stats_record *rec)
 	if (mask & SAMPLE_RX_CNT)
 		map_collect_percpu(map_fds[RX_CNT], 0, &rec->rx_cnt);
 
-	if (mask & SAMPLE_REDIRECT_ERR_CNT)
-		map_collect_percpu(map_fds[REDIRECT_ERR_CNT], 1, &rec->redir_err);
+	/* Success case */
+	if (mask & SAMPLE_REDIRECT_CNT)
+		map_collect_percpu(map_fds[REDIRECT_ERR_CNT], 0, &rec->redir_err[0]);
+
+	if (mask & SAMPLE_REDIRECT_ERR_CNT) {
+		for (i = 1; i < XDP_REDIRECT_ERR_MAX; i++)
+			map_collect_percpu(map_fds[REDIRECT_ERR_CNT], i, &rec->redir_err[i]);
+	}
 
 	if (mask & SAMPLE_CPUMAP_ENQUEUE_CNT)
 		for (i = 0; i < n_cpus; i++)
@@ -551,6 +582,9 @@ void sample_stats_print(int mask, struct stats_record *cur,
 	if (mask & SAMPLE_RX_CNT)
 		stats_print_rx_cnt(cur, prev, nr_cpus);
 
+	if (mask & SAMPLE_REDIRECT_CNT)
+		stats_print_redirect_cnt(cur, prev, nr_cpus);
+
 	if (mask & SAMPLE_REDIRECT_ERR_CNT)
 		stats_print_redirect_err_cnt(cur, prev, nr_cpus);
 
diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h
index 75a4ea4b55ad..a3a3c746e73e 100644
--- a/samples/bpf/xdp_sample_user.h
+++ b/samples/bpf/xdp_sample_user.h
@@ -14,6 +14,8 @@ enum map_type {
 };
 
 enum tp_type {
+	TP_REDIRECT_CNT,
+	TP_REDIRECT_MAP_CNT,
 	TP_REDIRECT_ERR_CNT,
 	TP_REDIRECT_MAP_ERR_CNT,
 	TP_CPUMAP_ENQUEUE_CNT,
@@ -30,6 +32,7 @@ enum stats_mask {
 	SAMPLE_CPUMAP_KTHREAD_CNT  = 1U << 4,
 	SAMPLE_EXCEPTION_CNT	= 1U << 5,
 	SAMPLE_DEVMAP_XMIT_CNT  = 1U << 6,
+	SAMPLE_REDIRECT_CNT	= 1U << 7,
 };
 
 static const char *const map_type_strings[] = {
@@ -54,6 +57,14 @@ extern int tp_cnt;
 #define EXIT_FAIL_BPF		4
 #define EXIT_FAIL_MEM		5
 
+#define XDP_REDIRECT_ERR_MAX 6
+
+static const char *xdp_redirect_err_names[XDP_REDIRECT_ERR_MAX] = {
+	/* Key=1 keeps unknown errors */
+	"Success", "Unknown", "EINVAL", "ENETDOWN", "EMSGSIZE",
+	"EOPNOTSUPP",
+};
+
 /* Common stats data record shared with _kern.c */
 struct datarec {
 	__u64 processed;
@@ -75,7 +86,7 @@ struct record {
 
 struct stats_record {
 	struct record rx_cnt;
-	struct record redir_err;
+	struct record redir_err[XDP_REDIRECT_ERR_MAX];
 	struct record kthread;
 	struct record exception;
 	struct record devmap_xmit;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 08/15] samples: bpf: add per exception reporting for xdp_exception
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (6 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 07/15] samples: bpf: add extended reporting for xdp redirect error Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 09/15] samples: bpf: convert xdp_monitor to use xdp_samples Kumar Kartikeya Dwivedi
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This is taken from xdp_monitor, in preparation for the conversion in a
subsequent patch.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_sample_kern.h |  8 ++++--
 samples/bpf/xdp_sample_user.c | 47 ++++++++++++++++++++---------------
 samples/bpf/xdp_sample_user.h | 24 ++++++++++++++++--
 3 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/samples/bpf/xdp_sample_kern.h b/samples/bpf/xdp_sample_kern.h
index 4131b9cb1ec4..ec36e7b4a3ba 100644
--- a/samples/bpf/xdp_sample_kern.h
+++ b/samples/bpf/xdp_sample_kern.h
@@ -63,12 +63,13 @@ struct {
 	__uint(max_entries, 1);
 } cpumap_kthread_cnt SEC(".maps");
 
+#define XDP_UNKNOWN (XDP_REDIRECT + 1)
 /* Used by trace point */
 struct {
 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
 	__type(key, u32);
 	__type(value, struct datarec);
-	__uint(max_entries, 1);
+	__uint(max_entries, XDP_UNKNOWN + 1);
 } exception_cnt SEC(".maps");
 
 struct {
@@ -184,7 +185,10 @@ SEC("tracepoint/xdp/xdp_exception")
 int trace_xdp_exception(struct xdp_exception_ctx *ctx)
 {
 	struct datarec *rec;
-	u32 key = 0;
+	u32 key = ctx->act;
+
+	if (key > XDP_REDIRECT)
+		key = XDP_UNKNOWN;
 
 	rec = bpf_map_lookup_elem(&exception_cnt, &key);
 	if (!rec)
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
index 29410d551574..446668edf8d8 100644
--- a/samples/bpf/xdp_sample_user.c
+++ b/samples/bpf/xdp_sample_user.c
@@ -124,7 +124,8 @@ struct stats_record *alloc_stats_record(void)
 	rec->redir_err[0].cpu = alloc_record_per_cpu();
 	rec->redir_err[1].cpu = alloc_record_per_cpu();
 	rec->kthread.cpu   = alloc_record_per_cpu();
-	rec->exception.cpu = alloc_record_per_cpu();
+	for (i = 0; i < XDP_ACTION_MAX; i++)
+		rec->exception[i].cpu = alloc_record_per_cpu();
 	rec->devmap_xmit.cpu = alloc_record_per_cpu();
 	for (i = 0; i < n_cpus; i++)
 		rec->enq[i].cpu = alloc_record_per_cpu();
@@ -139,7 +140,8 @@ void free_stats_record(struct stats_record *r)
 	for (i = 0; i < n_cpus; i++)
 		free(r->enq[i].cpu);
 	free(r->devmap_xmit.cpu);
-	free(r->exception.cpu);
+	for (i = 0; i < XDP_ACTION_MAX; i++)
+		free(r->exception[i].cpu);
 	free(r->kthread.cpu);
 	free(r->redir_err[1].cpu);
 	free(r->redir_err[0].cpu);
@@ -388,27 +390,31 @@ static void stats_print_exception_cnt(struct stats_record *stats_rec,
 				      struct stats_record *stats_prev,
 				      unsigned int nr_cpus)
 {
-	char *fmt_err = "%-15s %-7d %'-14.0f %'-11.0f\n";
-	char *fm2_err = "%-15s %-7s %'-14.0f %'-11.0f\n";
+	char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
+	char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
 	struct record *rec, *prev;
-	double t, pps, drop;
-	int i;
+	double t, drop;
+	int rec_i, i;
 
-	rec = &stats_rec->exception;
-	prev = &stats_prev->exception;
-	t = calc_period(rec, prev);
-	for (i = 0; i < nr_cpus; i++) {
-		struct datarec *r = &rec->cpu[i];
-		struct datarec *p = &prev->cpu[i];
+	for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
+		rec  = &stats_rec->exception[rec_i];
+		prev = &stats_prev->exception[rec_i];
+		t = calc_period(rec, prev);
 
-		pps = calc_pps(r, p, t);
-		drop = calc_drop_pps(r, p, t);
-		if (pps > 0)
-			printf(fmt_err, "xdp_exception", i, pps, drop);
+		for (i = 0; i < nr_cpus; i++) {
+			struct datarec *r = &rec->cpu[i];
+			struct datarec *p = &prev->cpu[i];
+
+			drop = calc_drop_pps(r, p, t);
+			if (drop > 0)
+				printf(fmt1, "xdp_exception", i,
+				       0.0, drop, action2str(rec_i));
+		}
+		drop = calc_drop_pps(&rec->total, &prev->total, t);
+		if (drop > 0)
+			printf(fmt2, "xdp_exception", "total",
+			       0.0, drop, action2str(rec_i));
 	}
-	pps = calc_pps(&rec->total, &prev->total, t);
-	drop = calc_drop_pps(&rec->total, &prev->total, t);
-	printf(fm2_err, "xdp_exception", "total", pps, drop);
 }
 
 void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
@@ -564,7 +570,8 @@ void sample_stats_collect(int mask, struct stats_record *rec)
 		map_collect_percpu(map_fds[CPUMAP_KTHREAD_CNT], 0, &rec->kthread);
 
 	if (mask & SAMPLE_EXCEPTION_CNT)
-		map_collect_percpu(map_fds[EXCEPTION_CNT], 0, &rec->exception);
+		for (i = 0; i < XDP_ACTION_MAX; i++)
+			map_collect_percpu(map_fds[EXCEPTION_CNT], i, &rec->exception[i]);
 
 	if (mask & SAMPLE_DEVMAP_XMIT_CNT)
 		map_collect_percpu(map_fds[DEVMAP_XMIT_CNT], 0, &rec->devmap_xmit);
diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h
index a3a3c746e73e..bc0362575d4b 100644
--- a/samples/bpf/xdp_sample_user.h
+++ b/samples/bpf/xdp_sample_user.h
@@ -59,12 +59,32 @@ extern int tp_cnt;
 
 #define XDP_REDIRECT_ERR_MAX 6
 
-static const char *xdp_redirect_err_names[XDP_REDIRECT_ERR_MAX] = {
+__attribute__((unused)) static const char *xdp_redirect_err_names[XDP_REDIRECT_ERR_MAX] = {
 	/* Key=1 keeps unknown errors */
 	"Success", "Unknown", "EINVAL", "ENETDOWN", "EMSGSIZE",
 	"EOPNOTSUPP",
 };
 
+/* enum xdp_action */
+#define XDP_UNKNOWN (XDP_REDIRECT + 1)
+#define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
+
+static const char *xdp_action_names[XDP_ACTION_MAX] = {
+	[XDP_ABORTED]	= "XDP_ABORTED",
+	[XDP_DROP]	= "XDP_DROP",
+	[XDP_PASS]	= "XDP_PASS",
+	[XDP_TX]	= "XDP_TX",
+	[XDP_REDIRECT]	= "XDP_REDIRECT",
+	[XDP_UNKNOWN]	= "XDP_UNKNOWN",
+};
+
+__attribute__((unused)) static inline const char *action2str(int action)
+{
+	if (action < XDP_ACTION_MAX)
+		return xdp_action_names[action];
+	return NULL;
+}
+
 /* Common stats data record shared with _kern.c */
 struct datarec {
 	__u64 processed;
@@ -88,7 +108,7 @@ struct stats_record {
 	struct record rx_cnt;
 	struct record redir_err[XDP_REDIRECT_ERR_MAX];
 	struct record kthread;
-	struct record exception;
+	struct record exception[XDP_ACTION_MAX];
 	struct record devmap_xmit;
 	struct record enq[];
 };
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 09/15] samples: bpf: convert xdp_monitor to use xdp_samples
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (7 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 08/15] samples: bpf: add per exception reporting for xdp_exception Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 10/15] samples: bpf: implement terse output mode and make it default Kumar Kartikeya Dwivedi
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This reuses the samples support added so far.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/Makefile           |   2 +-
 samples/bpf/xdp_monitor_kern.c | 253 +------------
 samples/bpf/xdp_monitor_user.c | 625 +--------------------------------
 samples/bpf/xdp_sample_kern.h  |   6 +-
 4 files changed, 23 insertions(+), 863 deletions(-)

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index ea7100c8b760..8750233dcf07 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -102,7 +102,7 @@ xdp_redirect-objs := xdp_redirect_user.o
 xdp_redirect_map_multi-objs := xdp_redirect_map_multi_user.o
 xdp_redirect_map-objs := xdp_redirect_map_user.o xdp_sample_user.o
 xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o xdp_sample_user.o
-xdp_monitor-objs := xdp_monitor_user.o
+xdp_monitor-objs := xdp_monitor_user.o xdp_sample_user.o
 xdp_rxq_info-objs := xdp_rxq_info_user.o
 syscall_tp-objs := syscall_tp_user.o
 cpustat-objs := cpustat_user.o
diff --git a/samples/bpf/xdp_monitor_kern.c b/samples/bpf/xdp_monitor_kern.c
index 5c955b812c47..46c4fe4d7878 100644
--- a/samples/bpf/xdp_monitor_kern.c
+++ b/samples/bpf/xdp_monitor_kern.c
@@ -3,255 +3,6 @@
  *
  * XDP monitor tool, based on tracepoints
  */
-#include <uapi/linux/bpf.h>
-#include <bpf/bpf_helpers.h>
+#include "xdp_sample_kern.h"
 
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, u64);
-	__uint(max_entries, 2);
-	/* TODO: have entries for all possible errno's */
-} redirect_err_cnt SEC(".maps");
-
-#define XDP_UNKNOWN	XDP_REDIRECT + 1
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, u64);
-	__uint(max_entries, XDP_UNKNOWN + 1);
-} exception_cnt SEC(".maps");
-
-/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_redirect/format
- * Code in:                kernel/include/trace/events/xdp.h
- */
-struct xdp_redirect_ctx {
-	u64 __pad;		// First 8 bytes are not accessible by bpf code
-	int prog_id;		//	offset:8;  size:4; signed:1;
-	u32 act;		//	offset:12  size:4; signed:0;
-	int ifindex;		//	offset:16  size:4; signed:1;
-	int err;		//	offset:20  size:4; signed:1;
-	int to_ifindex;		//	offset:24  size:4; signed:1;
-	u32 map_id;		//	offset:28  size:4; signed:0;
-	int map_index;		//	offset:32  size:4; signed:1;
-};				//	offset:36
-
-enum {
-	XDP_REDIRECT_SUCCESS = 0,
-	XDP_REDIRECT_ERROR = 1
-};
-
-static __always_inline
-int xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx)
-{
-	u32 key = XDP_REDIRECT_ERROR;
-	int err = ctx->err;
-	u64 *cnt;
-
-	if (!err)
-		key = XDP_REDIRECT_SUCCESS;
-
-	cnt  = bpf_map_lookup_elem(&redirect_err_cnt, &key);
-	if (!cnt)
-		return 1;
-	*cnt += 1;
-
-	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("tracepoint/xdp/xdp_redirect_err")
-int trace_xdp_redirect_err(struct xdp_redirect_ctx *ctx)
-{
-	return xdp_redirect_collect_stat(ctx);
-}
-
-
-SEC("tracepoint/xdp/xdp_redirect_map_err")
-int trace_xdp_redirect_map_err(struct xdp_redirect_ctx *ctx)
-{
-	return xdp_redirect_collect_stat(ctx);
-}
-
-/* Likely unloaded when prog starts */
-SEC("tracepoint/xdp/xdp_redirect")
-int trace_xdp_redirect(struct xdp_redirect_ctx *ctx)
-{
-	return xdp_redirect_collect_stat(ctx);
-}
-
-/* Likely unloaded when prog starts */
-SEC("tracepoint/xdp/xdp_redirect_map")
-int trace_xdp_redirect_map(struct xdp_redirect_ctx *ctx)
-{
-	return xdp_redirect_collect_stat(ctx);
-}
-
-/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_exception/format
- * Code in:                kernel/include/trace/events/xdp.h
- */
-struct xdp_exception_ctx {
-	u64 __pad;	// First 8 bytes are not accessible by bpf code
-	int prog_id;	//	offset:8;  size:4; signed:1;
-	u32 act;	//	offset:12; size:4; signed:0;
-	int ifindex;	//	offset:16; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_exception")
-int trace_xdp_exception(struct xdp_exception_ctx *ctx)
-{
-	u64 *cnt;
-	u32 key;
-
-	key = ctx->act;
-	if (key > XDP_REDIRECT)
-		key = XDP_UNKNOWN;
-
-	cnt = bpf_map_lookup_elem(&exception_cnt, &key);
-	if (!cnt)
-		return 1;
-	*cnt += 1;
-
-	return 0;
-}
-
-/* Common stats data record shared with _user.c */
-struct datarec {
-	u64 processed;
-	u64 dropped;
-	u64 info;
-	u64 err;
-};
-#define MAX_CPUS 64
-
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct datarec);
-	__uint(max_entries, MAX_CPUS);
-} cpumap_enqueue_cnt SEC(".maps");
-
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct datarec);
-	__uint(max_entries, 1);
-} cpumap_kthread_cnt SEC(".maps");
-
-/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_enqueue/format
- * Code in:         kernel/include/trace/events/xdp.h
- */
-struct cpumap_enqueue_ctx {
-	u64 __pad;		// First 8 bytes are not accessible by bpf code
-	int map_id;		//	offset:8;  size:4; signed:1;
-	u32 act;		//	offset:12; size:4; signed:0;
-	int cpu;		//	offset:16; size:4; signed:1;
-	unsigned int drops;	//	offset:20; size:4; signed:0;
-	unsigned int processed;	//	offset:24; size:4; signed:0;
-	int to_cpu;		//	offset:28; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_cpumap_enqueue")
-int trace_xdp_cpumap_enqueue(struct cpumap_enqueue_ctx *ctx)
-{
-	u32 to_cpu = ctx->to_cpu;
-	struct datarec *rec;
-
-	if (to_cpu >= MAX_CPUS)
-		return 1;
-
-	rec = bpf_map_lookup_elem(&cpumap_enqueue_cnt, &to_cpu);
-	if (!rec)
-		return 0;
-	rec->processed += ctx->processed;
-	rec->dropped   += ctx->drops;
-
-	/* Record bulk events, then userspace can calc average bulk size */
-	if (ctx->processed > 0)
-		rec->info += 1;
-
-	return 0;
-}
-
-/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_kthread/format
- * Code in:         kernel/include/trace/events/xdp.h
- */
-struct cpumap_kthread_ctx {
-	u64 __pad;		// First 8 bytes are not accessible by bpf code
-	int map_id;		//	offset:8;  size:4; signed:1;
-	u32 act;		//	offset:12; size:4; signed:0;
-	int cpu;		//	offset:16; size:4; signed:1;
-	unsigned int drops;	//	offset:20; size:4; signed:0;
-	unsigned int processed;	//	offset:24; size:4; signed:0;
-	int sched;		//	offset:28; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_cpumap_kthread")
-int trace_xdp_cpumap_kthread(struct cpumap_kthread_ctx *ctx)
-{
-	struct datarec *rec;
-	u32 key = 0;
-
-	rec = bpf_map_lookup_elem(&cpumap_kthread_cnt, &key);
-	if (!rec)
-		return 0;
-	rec->processed += ctx->processed;
-	rec->dropped   += ctx->drops;
-
-	/* Count times kthread yielded CPU via schedule call */
-	if (ctx->sched)
-		rec->info++;
-
-	return 0;
-}
-
-struct {
-	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
-	__type(key, u32);
-	__type(value, struct datarec);
-	__uint(max_entries, 1);
-} devmap_xmit_cnt SEC(".maps");
-
-/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_devmap_xmit/format
- * Code in:         kernel/include/trace/events/xdp.h
- */
-struct devmap_xmit_ctx {
-	u64 __pad;		// First 8 bytes are not accessible by bpf code
-	int from_ifindex;	//	offset:8;  size:4; signed:1;
-	u32 act;		//	offset:12; size:4; signed:0;
-	int to_ifindex; 	//	offset:16; size:4; signed:1;
-	int drops;		//	offset:20; size:4; signed:1;
-	int sent;		//	offset:24; size:4; signed:1;
-	int err;		//	offset:28; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_devmap_xmit")
-int trace_xdp_devmap_xmit(struct devmap_xmit_ctx *ctx)
-{
-	struct datarec *rec;
-	u32 key = 0;
-
-	rec = bpf_map_lookup_elem(&devmap_xmit_cnt, &key);
-	if (!rec)
-		return 0;
-	rec->processed += ctx->sent;
-	rec->dropped   += ctx->drops;
-
-	/* Record bulk events, then userspace can calc average bulk size */
-	rec->info += 1;
-
-	/* Record error cases, where no frame were sent */
-	if (ctx->err)
-		rec->err++;
-
-	/* Catch API error of drv ndo_xdp_xmit sent more than count */
-	if (ctx->drops < 0)
-		rec->err++;
-
-	return 1;
-}
+char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/xdp_monitor_user.c b/samples/bpf/xdp_monitor_user.c
index 49ebc49aefc3..babb9fcc1a17 100644
--- a/samples/bpf/xdp_monitor_user.c
+++ b/samples/bpf/xdp_monitor_user.c
@@ -30,32 +30,9 @@ static const char *__doc_err_only__=
 #include <bpf/bpf.h>
 #include <bpf/libbpf.h>
 #include "bpf_util.h"
+#include "xdp_sample_user.h"
 
-enum map_type {
-	REDIRECT_ERR_CNT,
-	EXCEPTION_CNT,
-	CPUMAP_ENQUEUE_CNT,
-	CPUMAP_KTHREAD_CNT,
-	DEVMAP_XMIT_CNT,
-};
-
-static const char *const map_type_strings[] = {
-	[REDIRECT_ERR_CNT] = "redirect_err_cnt",
-	[EXCEPTION_CNT] = "exception_cnt",
-	[CPUMAP_ENQUEUE_CNT] = "cpumap_enqueue_cnt",
-	[CPUMAP_KTHREAD_CNT] = "cpumap_kthread_cnt",
-	[DEVMAP_XMIT_CNT] = "devmap_xmit_cnt",
-};
-
-#define NUM_MAP 5
-#define NUM_TP 8
-
-static int tp_cnt;
-static int map_cnt;
-static int verbose = 1;
 static bool debug = false;
-struct bpf_map *map_data[NUM_MAP] = {};
-struct bpf_link *tp_links[NUM_TP] = {};
 struct bpf_object *obj;
 
 static const struct option long_options[] = {
@@ -68,12 +45,8 @@ static const struct option long_options[] = {
 
 static void int_exit(int sig)
 {
-	/* Detach tracepoints */
-	while (tp_cnt)
-		bpf_link__destroy(tp_links[--tp_cnt]);
-
 	bpf_object__close(obj);
-	exit(0);
+	sample_exit(EXIT_OK);
 }
 
 /* C standard specifies two constants, EXIT_SUCCESS(0) and EXIT_FAILURE(1) */
@@ -100,557 +73,6 @@ static void usage(char *argv[])
 	printf("\n");
 }
 
-#define NANOSEC_PER_SEC 1000000000 /* 10^9 */
-static __u64 gettime(void)
-{
-	struct timespec t;
-	int res;
-
-	res = clock_gettime(CLOCK_MONOTONIC, &t);
-	if (res < 0) {
-		fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
-		exit(EXIT_FAILURE);
-	}
-	return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
-}
-
-enum {
-	REDIR_SUCCESS = 0,
-	REDIR_ERROR = 1,
-};
-#define REDIR_RES_MAX 2
-static const char *redir_names[REDIR_RES_MAX] = {
-	[REDIR_SUCCESS]	= "Success",
-	[REDIR_ERROR]	= "Error",
-};
-static const char *err2str(int err)
-{
-	if (err < REDIR_RES_MAX)
-		return redir_names[err];
-	return NULL;
-}
-/* enum xdp_action */
-#define XDP_UNKNOWN	XDP_REDIRECT + 1
-#define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
-static const char *xdp_action_names[XDP_ACTION_MAX] = {
-	[XDP_ABORTED]	= "XDP_ABORTED",
-	[XDP_DROP]	= "XDP_DROP",
-	[XDP_PASS]	= "XDP_PASS",
-	[XDP_TX]	= "XDP_TX",
-	[XDP_REDIRECT]	= "XDP_REDIRECT",
-	[XDP_UNKNOWN]	= "XDP_UNKNOWN",
-};
-static const char *action2str(int action)
-{
-	if (action < XDP_ACTION_MAX)
-		return xdp_action_names[action];
-	return NULL;
-}
-
-/* Common stats data record shared with _kern.c */
-struct datarec {
-	__u64 processed;
-	__u64 dropped;
-	__u64 info;
-	__u64 err;
-};
-#define MAX_CPUS 64
-
-/* Userspace structs for collection of stats from maps */
-struct record {
-	__u64 timestamp;
-	struct datarec total;
-	struct datarec *cpu;
-};
-struct u64rec {
-	__u64 processed;
-};
-struct record_u64 {
-	/* record for _kern side __u64 values */
-	__u64 timestamp;
-	struct u64rec total;
-	struct u64rec *cpu;
-};
-
-struct stats_record {
-	struct record_u64 xdp_redirect[REDIR_RES_MAX];
-	struct record_u64 xdp_exception[XDP_ACTION_MAX];
-	struct record xdp_cpumap_kthread;
-	struct record xdp_cpumap_enqueue[MAX_CPUS];
-	struct record xdp_devmap_xmit;
-};
-
-static bool map_collect_record(int fd, __u32 key, struct record *rec)
-{
-	/* For percpu maps, userspace gets a value per possible CPU */
-	unsigned int nr_cpus = bpf_num_possible_cpus();
-	struct datarec values[nr_cpus];
-	__u64 sum_processed = 0;
-	__u64 sum_dropped = 0;
-	__u64 sum_info = 0;
-	__u64 sum_err = 0;
-	int i;
-
-	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
-		fprintf(stderr,
-			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
-		return false;
-	}
-	/* Get time as close as possible to reading map contents */
-	rec->timestamp = gettime();
-
-	/* Record and sum values from each CPU */
-	for (i = 0; i < nr_cpus; i++) {
-		rec->cpu[i].processed = values[i].processed;
-		sum_processed        += values[i].processed;
-		rec->cpu[i].dropped = values[i].dropped;
-		sum_dropped        += values[i].dropped;
-		rec->cpu[i].info = values[i].info;
-		sum_info        += values[i].info;
-		rec->cpu[i].err = values[i].err;
-		sum_err        += values[i].err;
-	}
-	rec->total.processed = sum_processed;
-	rec->total.dropped   = sum_dropped;
-	rec->total.info      = sum_info;
-	rec->total.err       = sum_err;
-	return true;
-}
-
-static bool map_collect_record_u64(int fd, __u32 key, struct record_u64 *rec)
-{
-	/* For percpu maps, userspace gets a value per possible CPU */
-	unsigned int nr_cpus = bpf_num_possible_cpus();
-	struct u64rec values[nr_cpus];
-	__u64 sum_total = 0;
-	int i;
-
-	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
-		fprintf(stderr,
-			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
-		return false;
-	}
-	/* Get time as close as possible to reading map contents */
-	rec->timestamp = gettime();
-
-	/* Record and sum values from each CPU */
-	for (i = 0; i < nr_cpus; i++) {
-		rec->cpu[i].processed = values[i].processed;
-		sum_total            += values[i].processed;
-	}
-	rec->total.processed = sum_total;
-	return true;
-}
-
-static double calc_period(struct record *r, struct record *p)
-{
-	double period_ = 0;
-	__u64 period = 0;
-
-	period = r->timestamp - p->timestamp;
-	if (period > 0)
-		period_ = ((double) period / NANOSEC_PER_SEC);
-
-	return period_;
-}
-
-static double calc_period_u64(struct record_u64 *r, struct record_u64 *p)
-{
-	double period_ = 0;
-	__u64 period = 0;
-
-	period = r->timestamp - p->timestamp;
-	if (period > 0)
-		period_ = ((double) period / NANOSEC_PER_SEC);
-
-	return period_;
-}
-
-static double calc_pps(struct datarec *r, struct datarec *p, double period)
-{
-	__u64 packets = 0;
-	double pps = 0;
-
-	if (period > 0) {
-		packets = r->processed - p->processed;
-		pps = packets / period;
-	}
-	return pps;
-}
-
-static double calc_pps_u64(struct u64rec *r, struct u64rec *p, double period)
-{
-	__u64 packets = 0;
-	double pps = 0;
-
-	if (period > 0) {
-		packets = r->processed - p->processed;
-		pps = packets / period;
-	}
-	return pps;
-}
-
-static double calc_drop(struct datarec *r, struct datarec *p, double period)
-{
-	__u64 packets = 0;
-	double pps = 0;
-
-	if (period > 0) {
-		packets = r->dropped - p->dropped;
-		pps = packets / period;
-	}
-	return pps;
-}
-
-static double calc_info(struct datarec *r, struct datarec *p, double period)
-{
-	__u64 packets = 0;
-	double pps = 0;
-
-	if (period > 0) {
-		packets = r->info - p->info;
-		pps = packets / period;
-	}
-	return pps;
-}
-
-static double calc_err(struct datarec *r, struct datarec *p, double period)
-{
-	__u64 packets = 0;
-	double pps = 0;
-
-	if (period > 0) {
-		packets = r->err - p->err;
-		pps = packets / period;
-	}
-	return pps;
-}
-
-static void stats_print(struct stats_record *stats_rec,
-			struct stats_record *stats_prev,
-			bool err_only)
-{
-	unsigned int nr_cpus = bpf_num_possible_cpus();
-	int rec_i = 0, i, to_cpu;
-	double t = 0, pps = 0;
-
-	/* Header */
-	printf("%-15s %-7s %-12s %-12s %-9s\n",
-	       "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info");
-
-	/* tracepoint: xdp:xdp_redirect_* */
-	if (err_only)
-		rec_i = REDIR_ERROR;
-
-	for (; rec_i < REDIR_RES_MAX; rec_i++) {
-		struct record_u64 *rec, *prev;
-		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
-		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
-
-		rec  =  &stats_rec->xdp_redirect[rec_i];
-		prev = &stats_prev->xdp_redirect[rec_i];
-		t = calc_period_u64(rec, prev);
-
-		for (i = 0; i < nr_cpus; i++) {
-			struct u64rec *r = &rec->cpu[i];
-			struct u64rec *p = &prev->cpu[i];
-
-			pps = calc_pps_u64(r, p, t);
-			if (pps > 0)
-				printf(fmt1, "XDP_REDIRECT", i,
-				       rec_i ? 0.0: pps, rec_i ? pps : 0.0,
-				       err2str(rec_i));
-		}
-		pps = calc_pps_u64(&rec->total, &prev->total, t);
-		printf(fmt2, "XDP_REDIRECT", "total",
-		       rec_i ? 0.0: pps, rec_i ? pps : 0.0, err2str(rec_i));
-	}
-
-	/* tracepoint: xdp:xdp_exception */
-	for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
-		struct record_u64 *rec, *prev;
-		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
-		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
-
-		rec  =  &stats_rec->xdp_exception[rec_i];
-		prev = &stats_prev->xdp_exception[rec_i];
-		t = calc_period_u64(rec, prev);
-
-		for (i = 0; i < nr_cpus; i++) {
-			struct u64rec *r = &rec->cpu[i];
-			struct u64rec *p = &prev->cpu[i];
-
-			pps = calc_pps_u64(r, p, t);
-			if (pps > 0)
-				printf(fmt1, "Exception", i,
-				       0.0, pps, action2str(rec_i));
-		}
-		pps = calc_pps_u64(&rec->total, &prev->total, t);
-		if (pps > 0)
-			printf(fmt2, "Exception", "total",
-			       0.0, pps, action2str(rec_i));
-	}
-
-	/* cpumap enqueue stats */
-	for (to_cpu = 0; to_cpu < MAX_CPUS; to_cpu++) {
-		char *fmt1 = "%-15s %3d:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
-		char *fmt2 = "%-15s %3s:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
-		struct record *rec, *prev;
-		char *info_str = "";
-		double drop, info;
-
-		rec  =  &stats_rec->xdp_cpumap_enqueue[to_cpu];
-		prev = &stats_prev->xdp_cpumap_enqueue[to_cpu];
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			pps  = calc_pps(r, p, t);
-			drop = calc_drop(r, p, t);
-			info = calc_info(r, p, t);
-			if (info > 0) {
-				info_str = "bulk-average";
-				info = pps / info; /* calc average bulk size */
-			}
-			if (pps > 0)
-				printf(fmt1, "cpumap-enqueue",
-				       i, to_cpu, pps, drop, info, info_str);
-		}
-		pps = calc_pps(&rec->total, &prev->total, t);
-		if (pps > 0) {
-			drop = calc_drop(&rec->total, &prev->total, t);
-			info = calc_info(&rec->total, &prev->total, t);
-			if (info > 0) {
-				info_str = "bulk-average";
-				info = pps / info; /* calc average bulk size */
-			}
-			printf(fmt2, "cpumap-enqueue",
-			       "sum", to_cpu, pps, drop, info, info_str);
-		}
-	}
-
-	/* cpumap kthread stats */
-	{
-		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.0f %s\n";
-		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.0f %s\n";
-		struct record *rec, *prev;
-		double drop, info;
-		char *i_str = "";
-
-		rec  =  &stats_rec->xdp_cpumap_kthread;
-		prev = &stats_prev->xdp_cpumap_kthread;
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			pps  = calc_pps(r, p, t);
-			drop = calc_drop(r, p, t);
-			info = calc_info(r, p, t);
-			if (info > 0)
-				i_str = "sched";
-			if (pps > 0 || drop > 0)
-				printf(fmt1, "cpumap-kthread",
-				       i, pps, drop, info, i_str);
-		}
-		pps = calc_pps(&rec->total, &prev->total, t);
-		drop = calc_drop(&rec->total, &prev->total, t);
-		info = calc_info(&rec->total, &prev->total, t);
-		if (info > 0)
-			i_str = "sched-sum";
-		printf(fmt2, "cpumap-kthread", "total", pps, drop, info, i_str);
-	}
-
-	/* devmap ndo_xdp_xmit stats */
-	{
-		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.2f %s %s\n";
-		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.2f %s %s\n";
-		struct record *rec, *prev;
-		double drop, info, err;
-		char *i_str = "";
-		char *err_str = "";
-
-		rec  =  &stats_rec->xdp_devmap_xmit;
-		prev = &stats_prev->xdp_devmap_xmit;
-		t = calc_period(rec, prev);
-		for (i = 0; i < nr_cpus; i++) {
-			struct datarec *r = &rec->cpu[i];
-			struct datarec *p = &prev->cpu[i];
-
-			pps  = calc_pps(r, p, t);
-			drop = calc_drop(r, p, t);
-			info = calc_info(r, p, t);
-			err  = calc_err(r, p, t);
-			if (info > 0) {
-				i_str = "bulk-average";
-				info = (pps+drop) / info; /* calc avg bulk */
-			}
-			if (err > 0)
-				err_str = "drv-err";
-			if (pps > 0 || drop > 0)
-				printf(fmt1, "devmap-xmit",
-				       i, pps, drop, info, i_str, err_str);
-		}
-		pps = calc_pps(&rec->total, &prev->total, t);
-		drop = calc_drop(&rec->total, &prev->total, t);
-		info = calc_info(&rec->total, &prev->total, t);
-		err  = calc_err(&rec->total, &prev->total, t);
-		if (info > 0) {
-			i_str = "bulk-average";
-			info = (pps+drop) / info; /* calc avg bulk */
-		}
-		if (err > 0)
-			err_str = "drv-err";
-		printf(fmt2, "devmap-xmit", "total", pps, drop,
-		       info, i_str, err_str);
-	}
-
-	printf("\n");
-}
-
-static bool stats_collect(struct stats_record *rec)
-{
-	int fd;
-	int i;
-
-	/* TODO: Detect if someone unloaded the perf event_fd's, as
-	 * this can happen by someone running perf-record -e
-	 */
-
-	fd = bpf_map__fd(map_data[REDIRECT_ERR_CNT]);
-	for (i = 0; i < REDIR_RES_MAX; i++)
-		map_collect_record_u64(fd, i, &rec->xdp_redirect[i]);
-
-	fd = bpf_map__fd(map_data[EXCEPTION_CNT]);
-	for (i = 0; i < XDP_ACTION_MAX; i++) {
-		map_collect_record_u64(fd, i, &rec->xdp_exception[i]);
-	}
-
-	fd = bpf_map__fd(map_data[CPUMAP_ENQUEUE_CNT]);
-	for (i = 0; i < MAX_CPUS; i++)
-		map_collect_record(fd, i, &rec->xdp_cpumap_enqueue[i]);
-
-	fd = bpf_map__fd(map_data[CPUMAP_KTHREAD_CNT]);
-	map_collect_record(fd, 0, &rec->xdp_cpumap_kthread);
-
-	fd = bpf_map__fd(map_data[DEVMAP_XMIT_CNT]);
-	map_collect_record(fd, 0, &rec->xdp_devmap_xmit);
-
-	return true;
-}
-
-static void *alloc_rec_per_cpu(int record_size)
-{
-	unsigned int nr_cpus = bpf_num_possible_cpus();
-	void *array;
-
-	array = calloc(nr_cpus, record_size);
-	if (!array) {
-		fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
-		exit(EXIT_FAIL_MEM);
-	}
-	return array;
-}
-
-static struct stats_record *alloc_stats_record(void)
-{
-	struct stats_record *rec;
-	int rec_sz;
-	int i;
-
-	/* Alloc main stats_record structure */
-	rec = calloc(1, sizeof(*rec));
-	if (!rec) {
-		fprintf(stderr, "Mem alloc error\n");
-		exit(EXIT_FAIL_MEM);
-	}
-
-	/* Alloc stats stored per CPU for each record */
-	rec_sz = sizeof(struct u64rec);
-	for (i = 0; i < REDIR_RES_MAX; i++)
-		rec->xdp_redirect[i].cpu = alloc_rec_per_cpu(rec_sz);
-
-	for (i = 0; i < XDP_ACTION_MAX; i++)
-		rec->xdp_exception[i].cpu = alloc_rec_per_cpu(rec_sz);
-
-	rec_sz = sizeof(struct datarec);
-	rec->xdp_cpumap_kthread.cpu = alloc_rec_per_cpu(rec_sz);
-	rec->xdp_devmap_xmit.cpu    = alloc_rec_per_cpu(rec_sz);
-
-	for (i = 0; i < MAX_CPUS; i++)
-		rec->xdp_cpumap_enqueue[i].cpu = alloc_rec_per_cpu(rec_sz);
-
-	return rec;
-}
-
-static void free_stats_record(struct stats_record *r)
-{
-	int i;
-
-	for (i = 0; i < REDIR_RES_MAX; i++)
-		free(r->xdp_redirect[i].cpu);
-
-	for (i = 0; i < XDP_ACTION_MAX; i++)
-		free(r->xdp_exception[i].cpu);
-
-	free(r->xdp_cpumap_kthread.cpu);
-	free(r->xdp_devmap_xmit.cpu);
-
-	for (i = 0; i < MAX_CPUS; i++)
-		free(r->xdp_cpumap_enqueue[i].cpu);
-
-	free(r);
-}
-
-/* Pointer swap trick */
-static inline void swap(struct stats_record **a, struct stats_record **b)
-{
-	struct stats_record *tmp;
-
-	tmp = *a;
-	*a = *b;
-	*b = tmp;
-}
-
-static void stats_poll(int interval, bool err_only)
-{
-	struct stats_record *rec, *prev;
-
-	rec  = alloc_stats_record();
-	prev = alloc_stats_record();
-	stats_collect(rec);
-
-	if (err_only)
-		printf("\n%s\n", __doc_err_only__);
-
-	/* Trick to pretty printf with thousands separators use %' */
-	setlocale(LC_NUMERIC, "en_US");
-
-	/* Header */
-	if (verbose)
-		printf("\n%s", __doc__);
-
-	/* TODO Need more advanced stats on error types */
-	if (verbose) {
-		printf(" - Stats map0: %s\n", bpf_map__name(map_data[0]));
-		printf(" - Stats map1: %s\n", bpf_map__name(map_data[1]));
-		printf("\n");
-	}
-	fflush(stdout);
-
-	while (1) {
-		swap(&prev, &rec);
-		stats_collect(rec);
-		stats_print(rec, prev, err_only);
-		fflush(stdout);
-		sleep(interval);
-	}
-
-	free_stats_record(rec);
-	free_stats_record(prev);
-}
-
 static void print_bpf_prog_info(void)
 {
 	struct bpf_program *prog;
@@ -666,7 +88,7 @@ static void print_bpf_prog_info(void)
 
 	i = 0;
 	/* Maps info */
-	printf("Loaded BPF prog have %d map(s)\n", map_cnt);
+	printf("Loaded BPF prog have %d map(s)\n", NUM_MAP);
 	bpf_object__for_each_map(map, obj) {
 		const char *name = bpf_map__name(map);
 		int fd		 = bpf_map__fd(map);
@@ -687,10 +109,11 @@ static void print_bpf_prog_info(void)
 
 int main(int argc, char **argv)
 {
-	struct bpf_program *prog;
+	int mask = SAMPLE_REDIRECT_ERR_CNT | SAMPLE_CPUMAP_ENQUEUE_CNT |
+		   SAMPLE_CPUMAP_KTHREAD_CNT | SAMPLE_EXCEPTION_CNT |
+		   SAMPLE_DEVMAP_XMIT_CNT;
 	int longindex = 0, opt;
 	int ret = EXIT_FAILURE;
-	enum map_type type;
 	char filename[256];
 
 	/* Default settings: */
@@ -736,25 +159,9 @@ int main(int argc, char **argv)
 		goto cleanup;
 	}
 
-	for (type = 0; type < NUM_MAP; type++) {
-		map_data[type] =
-			bpf_object__find_map_by_name(obj, map_type_strings[type]);
-
-		if (libbpf_get_error(map_data[type])) {
-			printf("ERROR: finding a map in obj file failed\n");
-			goto cleanup;
-		}
-		map_cnt++;
-	}
-
-	bpf_object__for_each_program(prog, obj) {
-		tp_links[tp_cnt] = bpf_program__attach(prog);
-		if (libbpf_get_error(tp_links[tp_cnt])) {
-			printf("ERROR: bpf_program__attach failed\n");
-			tp_links[tp_cnt] = NULL;
-			goto cleanup;
-		}
-		tp_cnt++;
+	if (sample_init(obj) < 0) {
+		fprintf(stderr, "Failed to initialize sample\n");
+		goto cleanup;
 	}
 
 	if (debug) {
@@ -763,6 +170,8 @@ int main(int argc, char **argv)
 
 	/* Unload/stop tracepoint event by closing bpf_link's */
 	if (errors_only) {
+		printf("%s", __doc_err_only__);
+
 		/* The bpf_link[i] depend on the order of
 		 * the functions was defined in _kern.c
 		 */
@@ -771,17 +180,13 @@ int main(int argc, char **argv)
 
 		bpf_link__destroy(tp_links[3]);	/* tracepoint/xdp/xdp_redirect_map */
 		tp_links[3] = NULL;
+	} else {
+		mask |= SAMPLE_REDIRECT_CNT;
 	}
 
-	stats_poll(interval, errors_only);
-
-	ret = EXIT_SUCCESS;
+	sample_stats_poll(interval, mask, "xdp_monitor", true);
 
 cleanup:
-	/* Detach tracepoints */
-	while (tp_cnt)
-		bpf_link__destroy(tp_links[--tp_cnt]);
-
 	bpf_object__close(obj);
-	return ret;
+	sample_exit(EXIT_OK);
 }
diff --git a/samples/bpf/xdp_sample_kern.h b/samples/bpf/xdp_sample_kern.h
index ec36e7b4a3ba..dd7f7ea63166 100644
--- a/samples/bpf/xdp_sample_kern.h
+++ b/samples/bpf/xdp_sample_kern.h
@@ -5,7 +5,11 @@
 #include <uapi/linux/bpf.h>
 #include <bpf/bpf_helpers.h>
 
-#define MAX_CPUS 64
+#ifndef NR_CPUS
+#define NR_CPUS 64
+#endif
+
+#define MAX_CPUS NR_CPUS
 
 #define EINVAL 22
 #define ENETDOWN 100
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 10/15] samples: bpf: implement terse output mode and make it default
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (8 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 09/15] samples: bpf: convert xdp_monitor to use xdp_samples Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 11/15] samples: bpf: print summary of session on exit Kumar Kartikeya Dwivedi
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

Also allow an easy way to go back to the verbose output using Ctrl+\
(SIGQUIT).

One change we make to exception printing is that we skip the per CPU per
action exception count printing (not collection), as it isn't too useful
in general.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/Makefile                |   2 +-
 samples/bpf/xdp_monitor_user.c      |   2 +-
 samples/bpf/xdp_redirect_cpu_user.c |   6 +-
 samples/bpf/xdp_sample_user.c       | 522 +++++++++++++++++++---------
 samples/bpf/xdp_sample_user.h       |  36 ++
 5 files changed, 402 insertions(+), 166 deletions(-)

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 8750233dcf07..d1977fe56dce 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -194,7 +194,7 @@ BPF_EXTRA_CFLAGS += -I$(srctree)/arch/mips/include/asm/mach-generic
 endif
 endif
 
-TPROGS_CFLAGS += -Wall -O2
+TPROGS_CFLAGS += -Wall -O2 -lm
 TPROGS_CFLAGS += -Wmissing-prototypes
 TPROGS_CFLAGS += -Wstrict-prototypes
 
diff --git a/samples/bpf/xdp_monitor_user.c b/samples/bpf/xdp_monitor_user.c
index babb9fcc1a17..73d6d35f0c65 100644
--- a/samples/bpf/xdp_monitor_user.c
+++ b/samples/bpf/xdp_monitor_user.c
@@ -184,7 +184,7 @@ int main(int argc, char **argv)
 		mask |= SAMPLE_REDIRECT_CNT;
 	}
 
-	sample_stats_poll(interval, mask, "xdp_monitor", true);
+	sample_stats_poll(interval, mask, NULL, true);
 
 cleanup:
 	bpf_object__close(obj);
diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c
index 6dbed962a2e2..3983ed71d879 100644
--- a/samples/bpf/xdp_redirect_cpu_user.c
+++ b/samples/bpf/xdp_redirect_cpu_user.c
@@ -216,16 +216,18 @@ static void __stats_poll(int interval, bool use_separators, char *prog_name,
 	for (;;) {
 		swap(&prev, &record);
 		sample_stats_collect(mask, record);
-		sample_stats_print(mask, record, prev, prog_name);
+		sample_stats_print(mask, record, prev, NULL);
 		/* Depends on SAMPLE_CPUMAP_KTHREAD_CNT */
 		sample_stats_print_cpumap_remote(record, prev,
 						 bpf_num_possible_cpus(),
 						 mprog_name);
-		printf("\n");
+		if (sample_log_level & LL_DEFAULT)
+			printf("\n");
 		fflush(stdout);
 		sleep(interval);
 		if (stress_mode)
 			stress_cpumap(value);
+		sample_reset_mode();
 	}
 
 	free_stats_record(record);
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
index 446668edf8d8..d0b26023f1db 100644
--- a/samples/bpf/xdp_sample_user.c
+++ b/samples/bpf/xdp_sample_user.c
@@ -9,7 +9,9 @@
 #include <stdbool.h>
 #include <string.h>
 #include <unistd.h>
+#include <math.h>
 #include <locale.h>
+#include <sys/signalfd.h>
 #include <sys/resource.h>
 #include <sys/sysinfo.h>
 #include <getopt.h>
@@ -35,6 +37,39 @@
 
 struct bpf_link *tp_links[NUM_TP] = {};
 int map_fds[NUM_MAP], tp_cnt, n_cpus;
+static int sample_sig_fd;
+enum log_level sample_log_level = LL_SIMPLE;
+static bool err_exp;
+
+#define __sample_print(fmt, cond, printer, ...)                                \
+	({                                                                     \
+		if (cond)                                                      \
+			printer(fmt, ##__VA_ARGS__);                           \
+	})
+
+#define print_always(fmt, ...) __sample_print(fmt, 1, printf, ##__VA_ARGS__)
+#define print_default(fmt, ...)                                                \
+	__sample_print(fmt, sample_log_level & LL_DEFAULT, printf, ##__VA_ARGS__)
+#define __print_err(err, fmt, printer, ...)                                    \
+	({                                                                     \
+		__sample_print(fmt, err > 0 || sample_log_level & LL_DEFAULT,  \
+			       printer, ##__VA_ARGS__);                        \
+		err_exp = err_exp ? true : err > 0;                            \
+	})
+#define print_err(err, fmt, ...) __print_err(err, fmt, printf, ##__VA_ARGS__)
+
+#define __COLUMN(x) "%'10" x " %-13s"
+#define FMT_COLUMNf __COLUMN(".0f")
+#define FMT_COLUMNd __COLUMN("d")
+#define FMT_COLUMNl __COLUMN("llu")
+#define RX(rx) rx, "rx/s"
+#define PPS(pps) pps, "pkt/s"
+#define DROP(drop) drop, "drop/s"
+#define ERR(err) err, "error/s"
+#define HITS(hits) hits, "hit/s"
+#define XMIT(xmit) xmit, "xmit/s"
+#define PASS(pass) pass, "pass/s"
+#define REDIR(redir) redir, "redir/s"
 
 #define NANOSEC_PER_SEC 1000000000 /* 10^9 */
 static __u64 gettime(void)
@@ -121,8 +156,8 @@ struct stats_record *alloc_stats_record(void)
 	}
 	memset(rec, 0, size);
 	rec->rx_cnt.cpu    = alloc_record_per_cpu();
-	rec->redir_err[0].cpu = alloc_record_per_cpu();
-	rec->redir_err[1].cpu = alloc_record_per_cpu();
+	for (i = 0; i < XDP_REDIRECT_ERR_MAX; i++)
+		rec->redir_err[i].cpu = alloc_record_per_cpu();
 	rec->kthread.cpu   = alloc_record_per_cpu();
 	for (i = 0; i < XDP_ACTION_MAX; i++)
 		rec->exception[i].cpu = alloc_record_per_cpu();
@@ -143,8 +178,8 @@ void free_stats_record(struct stats_record *r)
 	for (i = 0; i < XDP_ACTION_MAX; i++)
 		free(r->exception[i].cpu);
 	free(r->kthread.cpu);
-	free(r->redir_err[1].cpu);
-	free(r->redir_err[0].cpu);
+	for (i = 0; i < XDP_REDIRECT_ERR_MAX; i++)
+		free(r->redir_err[i].cpu);
 	free(r->rx_cnt.cpu);
 	free(r);
 }
@@ -161,6 +196,13 @@ static double calc_period(struct record *r, struct record *p)
 	return period_;
 }
 
+static double sample_round(double val)
+{
+	if (val - floor(val) < 0.5)
+		return floor(val);
+	return ceil(val);
+}
+
 static __u64 calc_pps(struct datarec *r, struct datarec *p, double period_)
 {
 	__u64 packets = 0;
@@ -168,7 +210,7 @@ static __u64 calc_pps(struct datarec *r, struct datarec *p, double period_)
 
 	if (period_ > 0) {
 		packets = r->processed - p->processed;
-		pps = packets / period_;
+		pps = sample_round(packets / period_);
 	}
 	return pps;
 }
@@ -180,7 +222,7 @@ static __u64 calc_drop_pps(struct datarec *r, struct datarec *p, double period_)
 
 	if (period_ > 0) {
 		packets = r->dropped - p->dropped;
-		pps = packets / period_;
+		pps = sample_round(packets / period_);
 	}
 	return pps;
 }
@@ -193,7 +235,7 @@ static __u64 calc_errs_pps(struct datarec *r,
 
 	if (period_ > 0) {
 		packets = r->issue - p->issue;
-		pps = packets / period_;
+		pps = sample_round(packets / period_);
 	}
 	return pps;
 }
@@ -206,7 +248,7 @@ static __u64 calc_info_pps(struct datarec *r,
 
 	if (period_ > 0) {
 		packets = r->info - p->info;
-		pps = packets / period_;
+		pps = sample_round(packets / period_);
 	}
 	return pps;
 }
@@ -223,41 +265,52 @@ static void calc_xdp_pps(struct datarec *r, struct datarec *p,
 	}
 }
 
-static void stats_print_rx_cnt(struct stats_record *stats_rec,
-			       struct stats_record *stats_prev,
-			       unsigned int nr_cpus)
+static void stats_get_rx_cnt(struct stats_record *stats_rec,
+			     struct stats_record *stats_prev,
+			     unsigned int nr_cpus, struct sample_output *out)
 {
-	char *fmt_rx = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s\n";
-	char *fm2_rx = "%-15s %-7s %'-14.0f %'-11.0f\n";
 	struct record *rec, *prev;
 	double t, pps, drop, err;
-	char *errstr = "";
 	int i;
 
 	rec = &stats_rec->rx_cnt;
 	prev = &stats_prev->rx_cnt;
 	t = calc_period(rec, prev);
+
 	for (i = 0; i < nr_cpus; i++) {
 		struct datarec *r = &rec->cpu[i];
 		struct datarec *p = &prev->cpu[i];
+		char str[256];
 
 		pps = calc_pps(r, p, t);
+		if (!pps)
+			continue;
+
+		snprintf(str, sizeof(str), "cpu:%d", i);
+
 		drop = calc_drop_pps(r, p, t);
 		err = calc_errs_pps(r, p, t);
-		if (err > 0)
-			errstr = "cpu-dest/err";
-		if (pps > 0)
-			printf(fmt_rx, "XDP-RX", i, pps, drop, err, errstr);
+		print_default("          %-12s " FMT_COLUMNf FMT_COLUMNf FMT_COLUMNf "\n",
+			      str, PPS(pps), DROP(drop), ERR(err));
+	}
+
+	if (out) {
+		pps = calc_pps(&rec->total, &prev->total, t);
+		drop = calc_drop_pps(&rec->total, &prev->total, t);
+		err = calc_errs_pps(&rec->total, &prev->total, t);
+
+		out->rx_cnt.pps = pps;
+		out->rx_cnt.drop = drop;
+		out->rx_cnt.err = err;
+		out->totals.rx += pps;
+		out->totals.drop += drop;
+		out->totals.err += err;
 	}
-	pps = calc_pps(&rec->total, &prev->total, t);
-	drop = calc_drop_pps(&rec->total, &prev->total, t);
-	err = calc_errs_pps(&rec->total, &prev->total, t);
-	printf(fm2_rx, "XDP-RX", "total", pps, drop);
 }
 
-static void stats_print_cpumap_enqueue(struct stats_record *stats_rec,
-				       struct stats_record *stats_prev,
-				       unsigned int nr_cpus)
+static void stats_get_cpumap_enqueue(struct stats_record *stats_rec,
+				     struct stats_record *stats_prev,
+				     unsigned int nr_cpus)
 {
 	struct record *rec, *prev;
 	double t, pps, drop, err;
@@ -265,83 +318,88 @@ static void stats_print_cpumap_enqueue(struct stats_record *stats_rec,
 
 	/* cpumap enqueue stats */
 	for (to_cpu = 0; to_cpu < n_cpus; to_cpu++) {
-		char *fmt = "%-15s %3d:%-3d %'-14.0f %'-11.0f %'-10.2f %s\n";
-		char *fm2 = "%-15s %3s:%-3d %'-14.0f %'-11.0f %'-10.2f %s\n";
-		char *errstr = "";
-
 		rec  =  &stats_rec->enq[to_cpu];
 		prev = &stats_prev->enq[to_cpu];
 		t = calc_period(rec, prev);
+
+		pps = calc_pps(&rec->total, &prev->total, t);
+		drop = calc_drop_pps(&rec->total, &prev->total, t);
+		err = calc_errs_pps(&rec->total, &prev->total, t);
+
+		if (pps > 0) {
+			char str[256];
+
+			snprintf(str, sizeof(str), "enqueue to cpu %d", to_cpu);
+
+			if (err > 0)
+				err = pps / err; /* calc average bulk size */
+			print_default("  %-20s " FMT_COLUMNf FMT_COLUMNf __COLUMN(".2f") "\n",
+				      str, PPS(pps), DROP(drop), err, "bulk_avg");
+		}
+
 		for (i = 0; i < nr_cpus; i++) {
 			struct datarec *r = &rec->cpu[i];
 			struct datarec *p = &prev->cpu[i];
+			char str[256];
 
 			pps  = calc_pps(r, p, t);
+			if (!pps)
+				continue;
+
+			snprintf(str, sizeof(str), "cpu:%d->%d", i, to_cpu);
+
 			drop = calc_drop_pps(r, p, t);
 			err  = calc_errs_pps(r, p, t);
-			if (err > 0) {
-				errstr = "bulk-average";
-				err = pps / err; /* calc average bulk size */
-			}
-			if (pps > 0)
-				printf(fmt, "cpumap-enqueue",
-				       i, to_cpu, pps, drop, err, errstr);
-		}
-		pps = calc_pps(&rec->total, &prev->total, t);
-		if (pps > 0) {
-			drop = calc_drop_pps(&rec->total, &prev->total, t);
-			err  = calc_errs_pps(&rec->total, &prev->total, t);
-			if (err > 0) {
-				errstr = "bulk-average";
+			if (err > 0)
 				err = pps / err; /* calc average bulk size */
-			}
-			printf(fm2, "cpumap-enqueue",
-			       "sum", to_cpu, pps, drop, err, errstr);
+			print_default("          %-12s " FMT_COLUMNf FMT_COLUMNf
+				      __COLUMN(".2f") "\n", str, PPS(pps), DROP(drop),
+				      err, "bulk_avg");
 		}
 	}
 }
 
-static void stats_print_cpumap_kthread(struct stats_record *stats_rec,
-				       struct stats_record *stats_prev,
-				       unsigned int nr_cpus)
+static void stats_get_cpumap_kthread(struct stats_record *stats_rec,
+				     struct stats_record *stats_prev,
+				     unsigned int nr_cpus)
 {
-	char *fmt_k = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s\n";
-	char *fm2_k = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f %s\n";
 	struct record *rec, *prev;
 	double t, pps, drop, err;
-	char *e_str = "";
 	int i;
 
 	rec = &stats_rec->kthread;
 	prev = &stats_prev->kthread;
 	t = calc_period(rec, prev);
+
+	pps = calc_pps(&rec->total, &prev->total, t);
+	drop = calc_drop_pps(&rec->total, &prev->total, t);
+	err = calc_errs_pps(&rec->total, &prev->total, t);
+
+	print_default("  %-20s " FMT_COLUMNf FMT_COLUMNf FMT_COLUMNf "\n", "kthread total",
+		      PPS(pps), DROP(drop), err, "sched");
+
 	for (i = 0; i < nr_cpus; i++) {
 		struct datarec *r = &rec->cpu[i];
 		struct datarec *p = &prev->cpu[i];
+		char str[256];
 
 		pps = calc_pps(r, p, t);
+		if (!pps)
+			continue;
+
+		snprintf(str, sizeof(str), "cpu:%d", i);
+
 		drop = calc_drop_pps(r, p, t);
 		err = calc_errs_pps(r, p, t);
-		if (err > 0)
-			e_str = "sched";
-		if (pps > 0)
-			printf(fmt_k, "cpumap_kthread", i, pps, drop, err,
-			       e_str);
+		print_default("          %-12s " FMT_COLUMNf FMT_COLUMNf FMT_COLUMNf "\n",
+			      str, PPS(pps), DROP(drop), err, "sched");
 	}
-	pps = calc_pps(&rec->total, &prev->total, t);
-	drop = calc_drop_pps(&rec->total, &prev->total, t);
-	err = calc_errs_pps(&rec->total, &prev->total, t);
-	if (err > 0)
-		e_str = "sched-sum";
-	printf(fm2_k, "cpumap_kthread", "total", pps, drop, err, e_str);
 }
 
-static void stats_print_redirect_cnt(struct stats_record *stats_rec,
-				     struct stats_record *stats_prev,
-				     unsigned int nr_cpus)
+static void stats_get_redirect_cnt(struct stats_record *stats_rec,
+				   struct stats_record *stats_prev,
+				   unsigned int nr_cpus, struct sample_output *out)
 {
-	char *fmt1 = "%-15s %-7d %'-14.0f %'-11.0f %s\n";
-	char *fmt2 = "%-15s %-7s %'-14.0f %'-11.0f %s\n";
 	struct record *rec, *prev;
 	double t, pps;
 	int i;
@@ -352,68 +410,106 @@ static void stats_print_redirect_cnt(struct stats_record *stats_rec,
 	for (i = 0; i < nr_cpus; i++) {
 		struct datarec *r = &rec->cpu[i];
 		struct datarec *p = &prev->cpu[i];
+		char str[256];
 
 		pps = calc_pps(r, p, t);
-		if (pps > 0)
-			printf(fmt1, "redirect", i, pps, 0.0, "Success");
-	}
-	pps = calc_pps(&rec->total, &prev->total, t);
-	printf(fmt2, "redirect", "total", pps, 0.0, "Success");
-}
+		if (!pps)
+			continue;
 
-static void stats_print_redirect_err_cnt(struct stats_record *stats_rec,
-					 struct stats_record *stats_prev,
-					 unsigned int nr_cpus)
-{
-	char *fmt1 = "%-15s %-7d %'-14.0f %'-11.0f %s\n";
-	char *fmt2 = "%-15s %-7s %'-14.0f %'-11.0f %s\n";
-	struct record *rec, *prev;
-	double t, drop;
-	int i;
+		snprintf(str, sizeof(str), "cpu:%d", i);
 
-	rec = &stats_rec->redir_err[1];
-	prev = &stats_prev->redir_err[1];
-	t = calc_period(rec, prev);
-	for (i = 0; i < nr_cpus; i++) {
-		struct datarec *r = &rec->cpu[i];
-		struct datarec *p = &prev->cpu[i];
+		print_default("           %-11s " FMT_COLUMNf "\n", str, REDIR(pps));
+	}
 
-		drop = calc_drop_pps(r, p, t);
-		if (drop > 0)
-			printf(fmt1, "redirect", i, 0.0, drop, "Error");
+	if (out) {
+		pps = calc_pps(&rec->total, &prev->total, t);
+		out->redir_cnt.suc = pps;
+		out->totals.redir += pps;
 	}
-	drop = calc_drop_pps(&rec->total, &prev->total, t);
-	printf(fmt2, "redirect", "total", 0.0, drop, "Error");
+
 }
 
-static void stats_print_exception_cnt(struct stats_record *stats_rec,
-				      struct stats_record *stats_prev,
-				      unsigned int nr_cpus)
+static void stats_get_redirect_err_cnt(struct stats_record *stats_rec,
+				       struct stats_record *stats_prev,
+				       unsigned int nr_cpus, struct sample_output *out)
 {
-	char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
-	char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
 	struct record *rec, *prev;
-	double t, drop;
+	double t, drop, sum = 0;
 	int rec_i, i;
 
-	for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
-		rec  = &stats_rec->exception[rec_i];
-		prev = &stats_prev->exception[rec_i];
+	for (rec_i = 1; rec_i < XDP_REDIRECT_ERR_MAX; rec_i++) {
+		char str[256];
+		int l = 0;
+
+		rec = &stats_rec->redir_err[rec_i];
+		prev = &stats_prev->redir_err[rec_i];
 		t = calc_period(rec, prev);
 
+		drop = calc_drop_pps(&rec->total, &prev->total, t);
+		if (drop > 0 && !out) {
+			l = snprintf(str, sizeof(str),
+				     sample_log_level & LL_DEFAULT ?
+						   "%s total" :
+						   "%s",
+				     xdp_redirect_err_names[rec_i]);
+			l = l >= sizeof(str) ? sizeof(str) - 1 : l;
+			print_err(drop, "    %-18s " FMT_COLUMNf "\n", str,
+				      ERR(drop));
+		}
+
 		for (i = 0; i < nr_cpus; i++) {
 			struct datarec *r = &rec->cpu[i];
 			struct datarec *p = &prev->cpu[i];
+			double drop;
+			int sp, ll;
 
 			drop = calc_drop_pps(r, p, t);
-			if (drop > 0)
-				printf(fmt1, "xdp_exception", i,
-				       0.0, drop, action2str(rec_i));
+			if (!drop)
+				continue;
+
+			ll = snprintf(str, sizeof(str), "cpu:%d", i);
+			ll = ll >= sizeof(str) ? sizeof(str) - 1 : ll;
+
+			sp = l - ll > 0 ? l - ll : 0;
+			ll = 19 - sp > 0 ? 19 - sp : 0;
+
+			/* Align dynamically under error string */
+			print_default("    %*c%-*s" FMT_COLUMNf "\n", sp, ' ', ll, str, ERR(drop));
 		}
+
+		sum += drop;
+	}
+
+	if (out) {
+		out->redir_cnt.err = sum;
+		out->totals.err += sum;
+	}
+}
+
+static void stats_get_exception_cnt(struct stats_record *stats_rec,
+				    struct stats_record *stats_prev,
+				    unsigned int nr_cpus, struct sample_output *out)
+{
+	double t, drop, sum = 0;
+	struct record *rec, *prev;
+	int rec_i;
+
+
+	for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
+		rec  = &stats_rec->exception[rec_i];
+		prev = &stats_prev->exception[rec_i];
+		t = calc_period(rec, prev);
+
 		drop = calc_drop_pps(&rec->total, &prev->total, t);
-		if (drop > 0)
-			printf(fmt2, "xdp_exception", "total",
-			       0.0, drop, action2str(rec_i));
+		/* Fold out errors after heading */
+		if (drop > 0 && !out)
+			print_always("    %-18s " FMT_COLUMNf "\n", action2str(rec_i), ERR(drop));
+		sum += drop;
+	}
+
+	if (out) {
+		out->except_cnt.hits = sum;
+		out->totals.err += sum;
 	}
 }
 
@@ -421,16 +517,12 @@ void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
 				      struct stats_record *stats_prev,
 				      unsigned int nr_cpus, char *mprog_name)
 {
-	char *fmt_k = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f\n";
-	char *fm2_k = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f\n";
 	double xdp_pass, xdp_drop, xdp_redirect;
 	struct record *rec, *prev;
 	double t;
 	int i;
 
-	printf("\n2nd remote XDP/eBPF prog_name: %s\n", mprog_name ?: "(none)");
-	printf("%-15s %-7s %-14s %-11s %-9s\n", "XDP-cpumap", "CPU:to",
-	       "xdp-pass", "xdp-drop", "xdp-redir");
+	print_default("\n2nd remote XDP/eBPF prog_name: %s\n", mprog_name ?: "(none)");
 
 	rec = &stats_rec->kthread;
 	prev = &stats_prev->kthread;
@@ -438,28 +530,28 @@ void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
 	for (i = 0; i < nr_cpus; i++) {
 		struct datarec *r = &rec->cpu[i];
 		struct datarec *p = &prev->cpu[i];
+		char str[256];
 
 		calc_xdp_pps(r, p, &xdp_pass, &xdp_drop, &xdp_redirect, t);
-		if (xdp_pass > 0 || xdp_drop > 0 || xdp_redirect > 0)
-			printf(fmt_k, "xdp-in-kthread", i, xdp_pass, xdp_drop,
-			       xdp_redirect);
+		if (!xdp_pass || !xdp_drop || !xdp_redirect)
+			continue;
+
+		snprintf(str, sizeof(str), "cpu:%d", i);
+		print_default("                 %-5s " FMT_COLUMNf FMT_COLUMNf FMT_COLUMNf "\n",
+			      str, PASS(xdp_pass), DROP(xdp_drop), REDIR(xdp_redirect));
 	}
 	calc_xdp_pps(&rec->total, &prev->total, &xdp_pass, &xdp_drop,
 		     &xdp_redirect, t);
-	printf(fm2_k, "xdp-in-kthread", "total", xdp_pass, xdp_drop,
-	       xdp_redirect);
+	print_default("  %-20s " FMT_COLUMNf FMT_COLUMNf FMT_COLUMNf "\n",
+		      "xdp_in_kthread total", PASS(xdp_pass), DROP(xdp_drop), REDIR(xdp_redirect));
 }
 
-static void stats_print_devmap_xmit(struct stats_record *stats_rec,
-				    struct stats_record *stats_prev,
-				    unsigned int nr_cpus)
+static void stats_get_devmap_xmit(struct stats_record *stats_rec,
+				  struct stats_record *stats_prev,
+				  unsigned int nr_cpus, struct sample_output *out)
 {
-	char *fmt1 = "%-15s %-7d %'-14.0f %'-11.0f %'-10.0f %s %s\n";
-	char *fmt2 = "%-15s %-7s %'-14.0f %'-11.0f %'-10.0f %s %s\n";
 	double pps, drop, info, err;
 	struct record *rec, *prev;
-	char *err_str = "";
-	char *i_str = "";
 	double t;
 	int i;
 
@@ -469,32 +561,114 @@ static void stats_print_devmap_xmit(struct stats_record *stats_rec,
 	for (i = 0; i < nr_cpus; i++) {
 		struct datarec *r = &rec->cpu[i];
 		struct datarec *p = &prev->cpu[i];
+		char str[256];
 
 		pps = calc_pps(r, p, t);
 		drop = calc_drop_pps(r, p, t);
+
+		if (!pps)
+			continue;
+
+		snprintf(str, sizeof(str), "cpu:%d", i);
+
 		info = calc_info_pps(r, p, t);
 		err = calc_errs_pps(r, p, t);
-		if (info > 0) {
-			i_str = "bulk-average";
+		if (info > 0)
 			info = (pps + drop) / info; /* calc avg bulk */
-		}
-		if (err > 0)
-			err_str = "drv-err";
-		if (pps > 0 || drop > 0)
-			printf(fmt1, "devmap-xmit", i, pps, drop, info, i_str,
-			       err_str);
+		print_default("              %-9s" FMT_COLUMNf FMT_COLUMNf
+			      FMT_COLUMNf __COLUMN(".2f") "\n",
+			      str, XMIT(pps), DROP(drop), err, "drv_err/s",
+			      info, "bulk_avg");
 	}
-	pps = calc_pps(&rec->total, &prev->total, t);
-	drop = calc_drop_pps(&rec->total, &prev->total, t);
-	info = calc_info_pps(&rec->total, &prev->total, t);
-	err = calc_errs_pps(&rec->total, &prev->total, t);
-	if (info > 0) {
-		i_str = "bulk-average";
-		info = (pps + drop) / info; /* calc avg bulk */
+	if (out) {
+		pps = calc_pps(&rec->total, &prev->total, t);
+		drop = calc_drop_pps(&rec->total, &prev->total, t);
+		info = calc_info_pps(&rec->total, &prev->total, t);
+		if (info > 0)
+			info = (pps + drop) / info; /* calc avg bulk */
+		err = calc_errs_pps(&rec->total, &prev->total, t);
+
+		out->xmit_cnt.pps = pps;
+		out->xmit_cnt.drop = drop;
+		out->xmit_cnt.bavg = info;
+		out->xmit_cnt.err = err;
+		out->totals.xmit += pps;
+		out->totals.err += err;
+	}
+}
+
+static void stats_print(const char *prefix, int mask, struct stats_record *r,
+			struct stats_record *p, struct sample_output *out)
+{
+	int nr_cpus = bpf_num_possible_cpus();
+	const char *str;
+
+	print_always("%-23s", prefix ?: "Summary");
+	if (mask & SAMPLE_RX_CNT)
+		print_always(FMT_COLUMNl, RX(out->totals.rx));
+	if (mask & SAMPLE_REDIRECT_CNT)
+		print_always(FMT_COLUMNl, REDIR(out->totals.redir));
+	printf(FMT_COLUMNl, ERR(out->totals.err + out->totals.drop));
+	if (mask & SAMPLE_DEVMAP_XMIT_CNT)
+		printf(FMT_COLUMNl, XMIT(out->totals.xmit));
+	printf("\n");
+
+	if (mask & SAMPLE_RX_CNT) {
+		str = (sample_log_level & LL_DEFAULT) && out->rx_cnt.pps ?
+			"receive total" : "receive";
+		print_err(
+			(out->rx_cnt.err || out->rx_cnt.drop),
+			"  %-20s " FMT_COLUMNl FMT_COLUMNl FMT_COLUMNl "\n",
+			str, PPS(out->rx_cnt.pps), DROP(out->rx_cnt.drop),
+			ERR(out->rx_cnt.err));
+
+		stats_get_rx_cnt(r, p, nr_cpus, NULL);
+	}
+
+	if (mask & SAMPLE_CPUMAP_ENQUEUE_CNT)
+		stats_get_cpumap_enqueue(r, p, nr_cpus);
+	if (mask & SAMPLE_CPUMAP_KTHREAD_CNT)
+		stats_get_cpumap_kthread(r, p, nr_cpus);
+
+	if (mask & SAMPLE_REDIRECT_CNT) {
+		str = out->redir_cnt.suc ? "redirect total" : "redirect";
+		print_default("  %-20s " FMT_COLUMNl "\n", str, REDIR(out->redir_cnt.suc));
+
+		stats_get_redirect_cnt(r, p, nr_cpus, NULL);
+	}
+
+	if (mask & SAMPLE_REDIRECT_ERR_CNT) {
+		str = (sample_log_level & LL_DEFAULT) && out->redir_cnt.err ?
+			"redirect_err total" : "redirect_err";
+		print_err(out->redir_cnt.err, "  %-20s " FMT_COLUMNl "\n", str,
+			  ERR(out->redir_cnt.err));
+
+		stats_get_redirect_err_cnt(r, p, nr_cpus, NULL);
+	}
+
+	if (mask & SAMPLE_EXCEPTION_CNT) {
+		str = out->except_cnt.hits ? "xdp_exception total" : "xdp_exception";
+		print_err(out->except_cnt.hits, "  %-20s " FMT_COLUMNl "\n",
+			  str, HITS(out->except_cnt.hits));
+
+		stats_get_exception_cnt(r, p, nr_cpus, NULL);
+	}
+
+	if (mask & SAMPLE_DEVMAP_XMIT_CNT) {
+		str = (sample_log_level & LL_DEFAULT) && out->xmit_cnt.pps ?
+			"devmap_xmit total" : "devmap_xmit";
+		print_err(out->xmit_cnt.err,
+			  "  %-20s " FMT_COLUMNl FMT_COLUMNl FMT_COLUMNl __COLUMN(".2f") "\n",
+			  str, XMIT(out->xmit_cnt.pps), DROP(out->xmit_cnt.drop),
+			  out->xmit_cnt.err, "drv_err/s", out->xmit_cnt.bavg, "bulk_avg");
+
+		stats_get_devmap_xmit(r, p, nr_cpus, NULL);
+	}
+
+	if (sample_log_level & LL_DEFAULT || ((sample_log_level & LL_SIMPLE) && err_exp)) {
+		err_exp = false;
+		printf("\n");
 	}
-	if (err > 0)
-		err_str = "drv-err";
-	printf(fmt2, "devmap-xmit", "total", pps, drop, info, i_str, err_str);
 }
 
 static int init_tracepoints(struct bpf_object *obj)
@@ -534,15 +708,47 @@ static int init_map_fds(struct bpf_object *obj)
 
 int sample_init(struct bpf_object *obj)
 {
+	sigset_t st;
+
 	n_cpus = get_nprocs_conf();
+
+	sigemptyset(&st);
+	sigaddset(&st, SIGQUIT);
+
+	if (sigprocmask(SIG_BLOCK, &st, NULL) < 0)
+		return -errno;
+
+	sample_sig_fd = signalfd(-1, &st, SFD_CLOEXEC|SFD_NONBLOCK);
+	if (sample_sig_fd < 0)
+		return -errno;
+
 	return init_tracepoints(obj) ? : init_map_fds(obj);
 }
 
+void sample_reset_mode(void)
+{
+	struct signalfd_siginfo si;
+	int r;
+
+	r = read(sample_sig_fd, &si, sizeof(si));
+	if (r < 0) {
+		if (errno == EAGAIN)
+			return;
+		return;
+	}
+
+	if (si.ssi_signo == SIGQUIT) {
+		sample_log_level ^= LL_DEBUG - 1;
+		printf("\n");
+	}
+}
+
 void sample_exit(int status)
 {
 	while (tp_cnt)
 		bpf_link__destroy(tp_links[--tp_cnt]);
 
+	close(sample_sig_fd);
 	exit(status);
 }
 
@@ -580,32 +786,24 @@ void sample_stats_collect(int mask, struct stats_record *rec)
 void sample_stats_print(int mask, struct stats_record *cur,
 			struct stats_record *prev, char *prog_name)
 {
-	int nr_cpus = bpf_num_possible_cpus();
-
-	printf("Running XDP/eBPF prog_name:%s\n", prog_name ?: "(none)");
-	printf("%-15s %-7s %-14s %-11s %-9s\n",
-	       "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info");
+	struct sample_output out = {};
 
 	if (mask & SAMPLE_RX_CNT)
-		stats_print_rx_cnt(cur, prev, nr_cpus);
+		stats_get_rx_cnt(cur, prev, 0, &out);
 
 	if (mask & SAMPLE_REDIRECT_CNT)
-		stats_print_redirect_cnt(cur, prev, nr_cpus);
+		stats_get_redirect_cnt(cur, prev, 0, &out);
 
 	if (mask & SAMPLE_REDIRECT_ERR_CNT)
-		stats_print_redirect_err_cnt(cur, prev, nr_cpus);
-
-	if (mask & SAMPLE_CPUMAP_ENQUEUE_CNT)
-		stats_print_cpumap_enqueue(cur, prev, nr_cpus);
-
-	if (mask & SAMPLE_CPUMAP_KTHREAD_CNT)
-		stats_print_cpumap_kthread(cur, prev, nr_cpus);
+		stats_get_redirect_err_cnt(cur, prev, 0, &out);
 
 	if (mask & SAMPLE_EXCEPTION_CNT)
-		stats_print_exception_cnt(cur, prev, nr_cpus);
+		stats_get_exception_cnt(cur, prev, 0, &out);
 
 	if (mask & SAMPLE_DEVMAP_XMIT_CNT)
-		stats_print_devmap_xmit(cur, prev, nr_cpus);
+		stats_get_devmap_xmit(cur, prev, 0, &out);
+
+	stats_print(prog_name, mask, cur, prev, &out);
 }
 
 void sample_stats_poll(int interval, int mask, char *prog_name, int use_separators)
@@ -623,10 +821,10 @@ void sample_stats_poll(int interval, int mask, char *prog_name, int use_separato
 	for (;;) {
 		swap(&prev, &record);
 		sample_stats_collect(mask, record);
-		sample_stats_print(mask, record, prev, NULL);
-		printf("\n");
+		sample_stats_print(mask, record, prev, prog_name);
 		fflush(stdout);
 		sleep(interval);
+		sample_reset_mode();
 	}
 
 	free_stats_record(record);
diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h
index bc0362575d4b..6ca934b346ef 100644
--- a/samples/bpf/xdp_sample_user.h
+++ b/samples/bpf/xdp_sample_user.h
@@ -44,10 +44,17 @@ static const char *const map_type_strings[] = {
 	[DEVMAP_XMIT_CNT] = "devmap_xmit_cnt",
 };
 
+enum log_level {
+	LL_DEFAULT = 1U << 0,
+	LL_SIMPLE  = 1U << 1,
+	LL_DEBUG   = 1U << 2,
+};
+
 extern struct bpf_link *tp_links[NUM_TP];
 extern int map_fds[NUM_MAP];
 extern int n_cpus;
 extern int tp_cnt;
+extern enum log_level sample_log_level;
 
 /* Exit return codes */
 #define EXIT_OK			0
@@ -113,6 +120,34 @@ struct stats_record {
 	struct record enq[];
 };
 
+struct sample_output {
+	struct {
+		__u64 rx;
+		__u64 redir;
+		__u64 drop;
+		__u64 err;
+		__u64 xmit;
+	} totals;
+	struct {
+		__u64 pps;
+		__u64 drop;
+		__u64 err;
+	} rx_cnt;
+	struct {
+		__u64 suc;
+		__u64 err;
+	} redir_cnt;
+	struct {
+		__u64 hits;
+	} except_cnt;
+	struct {
+		__u64 pps;
+		__u64 drop;
+		__u64 err;
+		double bavg;
+	} xmit_cnt;
+};
+
 int sample_init(struct bpf_object *obj);
 void sample_exit(int status);
 struct stats_record *alloc_stats_record(void);
@@ -125,6 +160,7 @@ void sample_stats_poll(int interval, int mask, char *prog_name,
 void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
 				      struct stats_record *stats_prev,
 				      unsigned int nr_cpus, char *mprog_name);
+void sample_reset_mode(void);
 
 const char *get_driver_name(int ifindex);
 int get_mac_addr(int ifindex, void *mac_addr);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 11/15] samples: bpf: print summary of session on exit
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (9 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 10/15] samples: bpf: implement terse output mode and make it default Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 12/15] samples: bpf: subtract time spent in collection from polling interval Kumar Kartikeya Dwivedi
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This collects total statistics and prints the totals and averages for
main attributes when exiting. These are collected on each polling
interval.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_redirect_cpu_user.c |   2 +-
 samples/bpf/xdp_redirect_map_user.c |   2 +-
 samples/bpf/xdp_sample_user.c       | 141 +++++++++++++++++++++++++---
 samples/bpf/xdp_sample_user.h       |  22 +++--
 4 files changed, 145 insertions(+), 22 deletions(-)

diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c
index 3983ed71d879..4c9f32229508 100644
--- a/samples/bpf/xdp_redirect_cpu_user.c
+++ b/samples/bpf/xdp_redirect_cpu_user.c
@@ -216,7 +216,7 @@ static void __stats_poll(int interval, bool use_separators, char *prog_name,
 	for (;;) {
 		swap(&prev, &record);
 		sample_stats_collect(mask, record);
-		sample_stats_print(mask, record, prev, NULL);
+		sample_stats_print(mask, record, prev, NULL, interval);
 		/* Depends on SAMPLE_CPUMAP_KTHREAD_CNT */
 		sample_stats_print_cpumap_remote(record, prev,
 						 bpf_num_possible_cpus(),
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index b2c7adad99ec..ed53dd2cd93a 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -75,7 +75,7 @@ static void usage(const char *prog)
 
 int main(int argc, char **argv)
 {
-	int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_CNT |
+	int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT |
 		   SAMPLE_EXCEPTION_CNT | SAMPLE_DEVMAP_XMIT_CNT;
 	struct bpf_prog_load_attr prog_load_attr = {
 		.prog_type	= BPF_PROG_TYPE_UNSPEC,
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
index d0b26023f1db..909257ffe54c 100644
--- a/samples/bpf/xdp_sample_user.c
+++ b/samples/bpf/xdp_sample_user.c
@@ -26,8 +26,10 @@
 #define SIOCETHTOOL 0x8946
 #endif
 
+#include <fcntl.h>
 #include <arpa/inet.h>
 #include <linux/if_link.h>
+#include <sys/utsname.h>
 
 #include <bpf/bpf.h>
 #include <bpf/libbpf.h>
@@ -39,6 +41,7 @@ struct bpf_link *tp_links[NUM_TP] = {};
 int map_fds[NUM_MAP], tp_cnt, n_cpus;
 static int sample_sig_fd;
 enum log_level sample_log_level = LL_SIMPLE;
+static struct sample_output sum_out;
 static bool err_exp;
 
 #define __sample_print(fmt, cond, printer, ...)                                \
@@ -58,6 +61,9 @@ static bool err_exp;
 	})
 #define print_err(err, fmt, ...) __print_err(err, fmt, printf, ##__VA_ARGS__)
 
+#define print_link_err(err, str, width, type)                                  \
+	__print_err(err, str, print_link, width, type)
+
 #define __COLUMN(x) "%'10" x " %-13s"
 #define FMT_COLUMNf __COLUMN(".0f")
 #define FMT_COLUMNd __COLUMN("d")
@@ -71,6 +77,66 @@ static bool err_exp;
 #define PASS(pass) pass, "pass/s"
 #define REDIR(redir) redir, "redir/s"
 
+static const char *elixir_search[NUM_TP] = {
+	[TP_REDIRECT_CNT] = "_trace_xdp_redirect",
+	[TP_REDIRECT_MAP_CNT] = "_trace_xdp_redirect_map",
+	[TP_REDIRECT_ERR_CNT] = "_trace_xdp_redirect_err",
+	[TP_REDIRECT_MAP_ERR_CNT] = "_trace_xdp_redirect_map_err",
+	[TP_CPUMAP_ENQUEUE_CNT] = "trace_xdp_cpumap_enqueue",
+	[TP_CPUMAP_KTHREAD_CNT] = "trace_xdp_cpumap_kthread",
+	[TP_EXCEPTION_CNT] = "trace_xdp_exception",
+	[TP_DEVMAP_XMIT_CNT] = "trace_xdp_devmap_xmit",
+};
+
+static const char *make_url(enum tp_type i)
+{
+	const char *key = elixir_search[i];
+	static struct utsname uts = {};
+	static char url[128];
+	static bool uts_init;
+	int maj, min;
+	char c[2];
+
+	if (!uts_init) {
+		if (uname(&uts) < 0)
+			return NULL;
+		uts_init = true;
+	}
+
+	if (!key || sscanf(uts.release, "%d.%d%1s", &maj, &min, c) != 3)
+		return NULL;
+
+	snprintf(url, sizeof(url), "https://elixir.bootlin.com/linux/v%d.%d/C/ident/%s",
+		 maj, min, key);
+
+	return url;
+}
+
+static void print_link(const char *str, int width, enum tp_type i)
+{
+	static int t = -1;
+	const char *s;
+	int fd, l;
+
+	if (t < 0) {
+		fd = open("/proc/self/fd/1", O_RDONLY);
+		if (fd < 0)
+			return;
+		t = isatty(fd);
+		close(fd);
+	}
+
+	s = make_url(i);
+	if (!s || !t) {
+		printf("  %-*s", width, str);
+		return;
+	}
+
+	l = strlen(str);
+	width = width - l > 0 ? width - l : 0;
+	printf("  \x1B]8;;%s\a%s\x1B]8;;\a%*c", s, str, width, ' ');
+}
+
 #define NANOSEC_PER_SEC 1000000000 /* 10^9 */
 static __u64 gettime(void)
 {
@@ -333,8 +399,11 @@ static void stats_get_cpumap_enqueue(struct stats_record *stats_rec,
 
 			if (err > 0)
 				err = pps / err; /* calc average bulk size */
-			print_default("  %-20s " FMT_COLUMNf FMT_COLUMNf __COLUMN(".2f") "\n",
-				      str, PPS(pps), DROP(drop), err, "bulk_avg");
+
+			print_link_err(drop, str, 20, TP_CPUMAP_ENQUEUE_CNT);
+			print_err(drop,
+				  " " FMT_COLUMNf FMT_COLUMNf __COLUMN(".2f") "\n",
+				  PPS(pps), DROP(drop), err, "bulk_avg");
 		}
 
 		for (i = 0; i < nr_cpus; i++) {
@@ -375,8 +444,9 @@ static void stats_get_cpumap_kthread(struct stats_record *stats_rec,
 	drop = calc_drop_pps(&rec->total, &prev->total, t);
 	err = calc_errs_pps(&rec->total, &prev->total, t);
 
-	print_default("  %-20s " FMT_COLUMNf FMT_COLUMNf FMT_COLUMNf "\n", "kthread total",
-		      PPS(pps), DROP(drop), err, "sched");
+	print_link_err(drop, pps ? "kthread total" : "kthread", 20, TP_CPUMAP_KTHREAD_CNT);
+	print_err(drop, " " FMT_COLUMNf FMT_COLUMNf FMT_COLUMNf "\n",
+			  PPS(pps), DROP(drop), err, "sched");
 
 	for (i = 0; i < nr_cpus; i++) {
 		struct datarec *r = &rec->cpu[i];
@@ -632,7 +702,9 @@ static void stats_print(const char *prefix, int mask, struct stats_record *r,
 
 	if (mask & SAMPLE_REDIRECT_CNT) {
 		str = out->redir_cnt.suc ? "redirect total" : "redirect";
-		print_default("  %-20s " FMT_COLUMNl "\n", str, REDIR(out->redir_cnt.suc));
+		print_link_err(0, str, 20, mask & _SAMPLE_REDIRECT_MAP ?
+				TP_REDIRECT_MAP_CNT : TP_REDIRECT_CNT);
+		print_default(" " FMT_COLUMNl "\n", REDIR(out->redir_cnt.suc));
 
 		stats_get_redirect_cnt(r, p, nr_cpus, NULL);
 	}
@@ -640,6 +712,8 @@ static void stats_print(const char *prefix, int mask, struct stats_record *r,
 	if (mask & SAMPLE_REDIRECT_ERR_CNT) {
 		str = (sample_log_level & LL_DEFAULT) && out->redir_cnt.err ?
 			"redirect_err total" : "redirect_err";
+		print_link_err(out->redir_cnt.err, str, 20, mask & _SAMPLE_REDIRECT_MAP ?
+			       TP_REDIRECT_MAP_ERR_CNT : TP_REDIRECT_ERR_CNT);
 		print_err(out->redir_cnt.err, "  %-20s " FMT_COLUMNl "\n", str,
 			  ERR(out->redir_cnt.err));
 
@@ -648,8 +722,9 @@ static void stats_print(const char *prefix, int mask, struct stats_record *r,
 
 	if (mask & SAMPLE_EXCEPTION_CNT) {
 		str = out->except_cnt.hits ? "xdp_exception total" : "xdp_exception";
-		print_err(out->except_cnt.hits, "  %-20s " FMT_COLUMNl "\n",
-			  str, HITS(out->except_cnt.hits));
+
+		print_link_err(out->except_cnt.hits, str, 20, TP_EXCEPTION_CNT);
+		print_err(out->except_cnt.hits, " " FMT_COLUMNl "\n", HITS(out->except_cnt.hits));
 
 		stats_get_exception_cnt(r, p, nr_cpus, NULL);
 	}
@@ -657,9 +732,11 @@ static void stats_print(const char *prefix, int mask, struct stats_record *r,
 	if (mask & SAMPLE_DEVMAP_XMIT_CNT) {
 		str = (sample_log_level & LL_DEFAULT) && out->xmit_cnt.pps ?
 			"devmap_xmit total" : "devmap_xmit";
+
+		print_link_err(out->xmit_cnt.err, str, 20, TP_DEVMAP_XMIT_CNT);
 		print_err(out->xmit_cnt.err,
-			  "  %-20s " FMT_COLUMNl FMT_COLUMNl FMT_COLUMNl __COLUMN(".2f") "\n",
-			  str, XMIT(out->xmit_cnt.pps), DROP(out->xmit_cnt.drop),
+			  " " FMT_COLUMNl FMT_COLUMNl FMT_COLUMNl __COLUMN(".2f") "\n",
+			  XMIT(out->xmit_cnt.pps), DROP(out->xmit_cnt.drop),
 			  out->xmit_cnt.err, "drv_err/s", out->xmit_cnt.bavg, "bulk_avg");
 
 		stats_get_devmap_xmit(r, p, nr_cpus, NULL);
@@ -747,7 +824,7 @@ void sample_exit(int status)
 {
 	while (tp_cnt)
 		bpf_link__destroy(tp_links[--tp_cnt]);
-
+	sample_summary_print();
 	close(sample_sig_fd);
 	exit(status);
 }
@@ -783,8 +860,46 @@ void sample_stats_collect(int mask, struct stats_record *rec)
 		map_collect_percpu(map_fds[DEVMAP_XMIT_CNT], 0, &rec->devmap_xmit);
 }
 
+void sample_summary_update(struct sample_output *out, int interval)
+{
+	sum_out.totals.rx += out->totals.rx;
+	sum_out.totals.redir += out->totals.redir;
+	sum_out.totals.drop += out->totals.drop;
+	sum_out.totals.err += out->totals.err;
+	sum_out.totals.xmit += out->totals.xmit;
+	sum_out.rx_cnt.pps += interval;
+}
+
+void sample_summary_print(void)
+{
+	double period = sum_out.rx_cnt.pps;
+
+	print_always("\nTotals\n");
+	if (sum_out.totals.rx) {
+		double pkts = sum_out.totals.rx;
+
+		print_always("  Packets received    : %'-10llu\n", sum_out.totals.rx);
+		print_always("  Average packets/s   : %'-10.0f\n", sample_round(pkts/period));
+	}
+	if (sum_out.totals.redir) {
+		double pkts = sum_out.totals.redir;
+
+		print_always("  Packets redirected  : %'-10llu\n", sum_out.totals.redir);
+		print_always("  Average redir/s     : %'-10.0f\n", sample_round(pkts/period));
+	}
+	print_always("  Packets dropped     : %'-10llu\n", sum_out.totals.drop);
+	print_always("  Errors recorded     : %'-10llu\n", sum_out.totals.err);
+	if (sum_out.totals.xmit) {
+		double pkts = sum_out.totals.xmit;
+
+		print_always("  Packets transmitted : %'-10llu\n", sum_out.totals.xmit);
+		print_always("  Average transmit/s  : %'-10.0f\n", sample_round(pkts/period));
+	}
+}
+
 void sample_stats_print(int mask, struct stats_record *cur,
-			struct stats_record *prev, char *prog_name)
+			struct stats_record *prev, char *prog_name,
+			int interval)
 {
 	struct sample_output out = {};
 
@@ -803,6 +918,8 @@ void sample_stats_print(int mask, struct stats_record *cur,
 	if (mask & SAMPLE_DEVMAP_XMIT_CNT)
 		stats_get_devmap_xmit(cur, prev, 0, &out);
 
+	sample_summary_update(&out, interval);
+
 	stats_print(prog_name, mask, cur, prev, &out);
 }
 
@@ -821,7 +938,7 @@ void sample_stats_poll(int interval, int mask, char *prog_name, int use_separato
 	for (;;) {
 		swap(&prev, &record);
 		sample_stats_collect(mask, record);
-		sample_stats_print(mask, record, prev, prog_name);
+		sample_stats_print(mask, record, prev, prog_name, interval);
 		fflush(stdout);
 		sleep(interval);
 		sample_reset_mode();
diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h
index 6ca934b346ef..abe4ec25c310 100644
--- a/samples/bpf/xdp_sample_user.h
+++ b/samples/bpf/xdp_sample_user.h
@@ -26,13 +26,16 @@ enum tp_type {
 };
 
 enum stats_mask {
-	SAMPLE_RX_CNT	        = 1U << 1,
-	SAMPLE_REDIRECT_ERR_CNT	= 1U << 2,
-	SAMPLE_CPUMAP_ENQUEUE_CNT  = 1U << 3,
-	SAMPLE_CPUMAP_KTHREAD_CNT  = 1U << 4,
-	SAMPLE_EXCEPTION_CNT	= 1U << 5,
-	SAMPLE_DEVMAP_XMIT_CNT  = 1U << 6,
-	SAMPLE_REDIRECT_CNT	= 1U << 7,
+	_SAMPLE_REDIRECT_MAP        = 1U << 0,
+	SAMPLE_RX_CNT               = 1U << 1,
+	SAMPLE_REDIRECT_ERR_CNT     = 1U << 2,
+	SAMPLE_CPUMAP_ENQUEUE_CNT   = 1U << 3,
+	SAMPLE_CPUMAP_KTHREAD_CNT   = 1U << 4,
+	SAMPLE_EXCEPTION_CNT        = 1U << 5,
+	SAMPLE_DEVMAP_XMIT_CNT      = 1U << 6,
+	SAMPLE_REDIRECT_CNT         = 1U << 7,
+	SAMPLE_REDIRECT_MAP_CNT     = SAMPLE_REDIRECT_CNT | _SAMPLE_REDIRECT_MAP,
+	SAMPLE_REDIRECT_ERR_MAP_CNT = SAMPLE_REDIRECT_ERR_CNT | _SAMPLE_REDIRECT_MAP,
 };
 
 static const char *const map_type_strings[] = {
@@ -153,8 +156,11 @@ void sample_exit(int status);
 struct stats_record *alloc_stats_record(void);
 void free_stats_record(struct stats_record *rec);
 void sample_stats_print(int mask, struct stats_record *cur,
-			struct stats_record *prev, char *prog_name);
+			struct stats_record *prev, char *prog_name,
+			int interval);
 void sample_stats_collect(int mask, struct stats_record *rec);
+void sample_summary_update(struct sample_output *out, int interval);
+void sample_summary_print(void);
 void sample_stats_poll(int interval, int mask, char *prog_name,
 		       int use_separators);
 void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 12/15] samples: bpf: subtract time spent in collection from polling interval
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (10 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 11/15] samples: bpf: print summary of session on exit Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 13/15] samples: bpf: add new options for xdp samples Kumar Kartikeya Dwivedi
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This improves sleeping precision and reduces the possibility of
reporting incorrect statistics to the user.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_redirect_cpu_user.c |  7 ++++++-
 samples/bpf/xdp_sample_user.c       | 27 ++++++++++++++++++++++++++-
 samples/bpf/xdp_sample_user.h       |  2 ++
 3 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c
index 4c9f32229508..103ac5c24163 100644
--- a/samples/bpf/xdp_redirect_cpu_user.c
+++ b/samples/bpf/xdp_redirect_cpu_user.c
@@ -214,6 +214,9 @@ static void __stats_poll(int interval, bool use_separators, char *prog_name,
 		setlocale(LC_NUMERIC, "en_US");
 
 	for (;;) {
+		struct timespec ots, nts;
+
+		clock_gettime(CLOCK_MONOTONIC, &ots);
 		swap(&prev, &record);
 		sample_stats_collect(mask, record);
 		sample_stats_print(mask, record, prev, NULL, interval);
@@ -224,7 +227,9 @@ static void __stats_poll(int interval, bool use_separators, char *prog_name,
 		if (sample_log_level & LL_DEFAULT)
 			printf("\n");
 		fflush(stdout);
-		sleep(interval);
+		clock_gettime(CLOCK_MONOTONIC, &nts);
+		sample_calc_timediff(&nts, &ots, interval);
+		nanosleep(&nts, NULL);
 		if (stress_mode)
 			stress_cpumap(value);
 		sample_reset_mode();
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
index 909257ffe54c..96d36c708ee3 100644
--- a/samples/bpf/xdp_sample_user.c
+++ b/samples/bpf/xdp_sample_user.c
@@ -923,6 +923,26 @@ void sample_stats_print(int mask, struct stats_record *cur,
 	stats_print(prog_name, mask, cur, prev, &out);
 }
 
+static void calc_timediff(struct timespec *cur, const struct timespec *prev)
+{
+	if (cur->tv_nsec - prev->tv_nsec < 0) {
+		cur->tv_sec = cur->tv_sec - prev->tv_sec - 1;
+		cur->tv_nsec = cur->tv_nsec - prev->tv_nsec + NANOSEC_PER_SEC;
+	} else {
+		cur->tv_sec -= prev->tv_sec;
+		cur->tv_nsec -= prev->tv_nsec;
+	}
+}
+
+void sample_calc_timediff(struct timespec *cur, const struct timespec *prev, int interval)
+{
+	struct timespec ts = { .tv_sec = interval };
+
+	calc_timediff(cur, prev);
+	calc_timediff(&ts, cur);
+	*cur = ts;
+}
+
 void sample_stats_poll(int interval, int mask, char *prog_name, int use_separators)
 {
 	struct stats_record *record, *prev;
@@ -936,11 +956,16 @@ void sample_stats_poll(int interval, int mask, char *prog_name, int use_separato
 		setlocale(LC_NUMERIC, "en_US");
 
 	for (;;) {
+		struct timespec ots, nts;
+
+		clock_gettime(CLOCK_MONOTONIC, &ots);
 		swap(&prev, &record);
 		sample_stats_collect(mask, record);
 		sample_stats_print(mask, record, prev, prog_name, interval);
 		fflush(stdout);
-		sleep(interval);
+		clock_gettime(CLOCK_MONOTONIC, &nts);
+		sample_calc_timediff(&nts, &ots, interval);
+		nanosleep(&nts, NULL);
 		sample_reset_mode();
 	}
 
diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h
index abe4ec25c310..588bd2f15352 100644
--- a/samples/bpf/xdp_sample_user.h
+++ b/samples/bpf/xdp_sample_user.h
@@ -161,6 +161,8 @@ void sample_stats_print(int mask, struct stats_record *cur,
 void sample_stats_collect(int mask, struct stats_record *rec);
 void sample_summary_update(struct sample_output *out, int interval);
 void sample_summary_print(void);
+void sample_calc_timediff(struct timespec *cur, const struct timespec *prev,
+			  int interval);
 void sample_stats_poll(int interval, int mask, char *prog_name,
 		       int use_separators);
 void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 13/15] samples: bpf: add new options for xdp samples
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (11 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 12/15] samples: bpf: subtract time spent in collection from polling interval Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 14/15] samples: bpf: add documentation Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 15/15] samples: bpf: convert xdp_samples to use raw_tracepoints Kumar Kartikeya Dwivedi
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

These are -i for polling interval and -v for verbose output by default.
Some of these tools already supported -s, but we now use that to enable
performance impacting success case reporting tracepoint. This is
disabled by default, but can be enabled explicitly by user.

Use separators (-z) is also dropped.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_monitor_user.c      | 16 ++++---
 samples/bpf/xdp_redirect_cpu_user.c | 41 ++++++++++++-----
 samples/bpf/xdp_redirect_map_user.c | 71 +++++++++++++++++++++++------
 3 files changed, 96 insertions(+), 32 deletions(-)

diff --git a/samples/bpf/xdp_monitor_user.c b/samples/bpf/xdp_monitor_user.c
index 73d6d35f0c65..b37d8f7379ec 100644
--- a/samples/bpf/xdp_monitor_user.c
+++ b/samples/bpf/xdp_monitor_user.c
@@ -38,9 +38,10 @@ struct bpf_object *obj;
 static const struct option long_options[] = {
 	{"help",	no_argument,		NULL, 'h' },
 	{"debug",	no_argument,		NULL, 'D' },
-	{"stats",	no_argument,		NULL, 'S' },
-	{"sec", 	required_argument,	NULL, 's' },
-	{0, 0, NULL,  0 }
+	{"stats",	no_argument,		NULL, 's' },
+	{"interval",	required_argument,	NULL, 'i' },
+	{"verbose",	no_argument,		NULL, 'v' },
+	{}
 };
 
 static void int_exit(int sig)
@@ -121,18 +122,21 @@ int main(int argc, char **argv)
 	int interval = 2;
 
 	/* Parse commands line args */
-	while ((opt = getopt_long(argc, argv, "hDSs:",
+	while ((opt = getopt_long(argc, argv, "hDi:vs",
 				  long_options, &longindex)) != -1) {
 		switch (opt) {
 		case 'D':
 			debug = true;
 			break;
-		case 'S':
+		case 's':
 			errors_only = false;
 			break;
-		case 's':
+		case 'i':
 			interval = atoi(optarg);
 			break;
+		case 'v':
+			sample_log_level ^= LL_DEBUG - 1;
+			break;
 		case 'h':
 		default:
 			usage(argv);
diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c
index 103ac5c24163..d56b89254cd1 100644
--- a/samples/bpf/xdp_redirect_cpu_user.c
+++ b/samples/bpf/xdp_redirect_cpu_user.c
@@ -42,19 +42,20 @@ static const struct option long_options[] = {
 	{"help",	no_argument,		NULL, 'h' },
 	{"dev",		required_argument,	NULL, 'd' },
 	{"skb-mode",	no_argument,		NULL, 'S' },
-	{"sec",		required_argument,	NULL, 's' },
 	{"progname",	required_argument,	NULL, 'p' },
 	{"qsize",	required_argument,	NULL, 'q' },
 	{"cpu",		required_argument,	NULL, 'c' },
 	{"stress-mode", no_argument,		NULL, 'x' },
-	{"no-separators", no_argument,		NULL, 'z' },
 	{"force",	no_argument,		NULL, 'F' },
 	{"mprog-disable", no_argument,		NULL, 'n' },
 	{"mprog-name",	required_argument,	NULL, 'e' },
 	{"mprog-filename", required_argument,	NULL, 'f' },
 	{"redirect-device", required_argument,	NULL, 'r' },
 	{"redirect-map", required_argument,	NULL, 'm' },
-	{0, 0, NULL,  0 }
+	{"interval", required_argument,		NULL, 'i' },
+	{"verbose", no_argument,		NULL, 'v' },
+	{"stats", no_argument,			NULL, 's' },
+	{}
 };
 
 static void int_exit(int sig)
@@ -196,7 +197,7 @@ static void stress_cpumap(struct bpf_cpumap_val *value)
 	create_cpu_entry(1, value, 0, false);
 }
 
-static void __stats_poll(int interval, bool use_separators, char *prog_name,
+static void __stats_poll(int interval, bool redir_suc, char *prog_name,
 			 char *mprog_name, struct bpf_cpumap_val *value,
 			 bool stress_mode)
 {
@@ -210,8 +211,10 @@ static void __stats_poll(int interval, bool use_separators, char *prog_name,
 	sample_stats_collect(mask, record);
 
 	/* Trick to pretty printf with thousands separators use %' */
-	if (use_separators)
-		setlocale(LC_NUMERIC, "en_US");
+	setlocale(LC_NUMERIC, "en_US");
+
+	if (redir_suc)
+		mask |= SAMPLE_REDIRECT_CNT;
 
 	for (;;) {
 		struct timespec ots, nts;
@@ -298,12 +301,12 @@ int main(int argc, char **argv)
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
 	struct bpf_cpumap_val value;
-	bool use_separators = true;
 	bool stress_mode = false;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
 	int err = EXIT_FAIL;
 	char filename[256];
+	bool redir = false;
 	int added_cpus = 0;
 	int longindex = 0;
 	int interval = 2;
@@ -356,7 +359,7 @@ int main(int argc, char **argv)
 	memset(cpu, 0, n_cpus * sizeof(int));
 
 	/* Parse commands line args */
-	while ((opt = getopt_long(argc, argv, "hSd:s:p:q:c:xzFf:e:r:m:",
+	while ((opt = getopt_long(argc, argv, "hSd:sp:q:c:xi:vFf:e:r:m:",
 				  long_options, &longindex)) != -1) {
 		switch (opt) {
 		case 'd':
@@ -375,6 +378,9 @@ int main(int argc, char **argv)
 			}
 			break;
 		case 's':
+			redir = true;
+			break;
+		case 'i':
 			interval = atoi(optarg);
 			break;
 		case 'S':
@@ -383,9 +389,6 @@ int main(int argc, char **argv)
 		case 'x':
 			stress_mode = true;
 			break;
-		case 'z':
-			use_separators = false;
-			break;
 		case 'p':
 			/* Selecting eBPF prog to load */
 			prog_name = optarg;
@@ -422,6 +425,9 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'v':
+			sample_log_level ^= LL_DEBUG - 1;
+			break;
 		case 'h':
 		error:
 		default:
@@ -492,7 +498,18 @@ int main(int argc, char **argv)
 	}
 	prog_id = info.id;
 
-	__stats_poll(interval, use_separators, prog_name, mprog_name,
+	if (!redir) {
+		/* The bpf_link[i] depend on the order of
+		 * the functions was defined in _kern.c
+		 */
+		bpf_link__destroy(tp_links[2]);	/* tracepoint/xdp/xdp_redirect */
+		tp_links[2] = NULL;
+
+		bpf_link__destroy(tp_links[3]);	/* tracepoint/xdp/xdp_redirect_map */
+		tp_links[3] = NULL;
+	}
+
+	__stats_poll(interval, redir, prog_name, mprog_name,
 		     &value, stress_mode);
 
 	err = EXIT_OK;
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index ed53dd2cd93a..eb4013fa58cb 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -13,6 +13,7 @@
 #include <net/if.h>
 #include <unistd.h>
 #include <libgen.h>
+#include <getopt.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -28,6 +29,18 @@ static __u32 dummy_prog_id;
 
 static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
 
+static const struct option long_options[] = {
+	{"help",	no_argument,		NULL, 'h' },
+	{"skb-mode",	no_argument,		NULL, 'S' },
+	{"native-mode", no_argument,		NULL, 'N' },
+	{"force",	no_argument,		NULL, 'F' },
+	{"load-egress", no_argument,		NULL, 'X' },
+	{"stats",	no_argument,		NULL, 's' },
+	{"interval",	required_argument,	NULL, 'i' },
+	{"verbose",	no_argument,		NULL, 'v' },
+	{}
+};
+
 static void int_exit(int sig)
 {
 	__u32 curr_prog_id = 0;
@@ -61,16 +74,25 @@ static void int_exit(int sig)
 	sample_exit(EXIT_OK);
 }
 
-static void usage(const char *prog)
+static void usage(char *argv[])
 {
-	fprintf(stderr,
-		"usage: %s [OPTS] <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n\n"
-		"OPTS:\n"
-		"    -S    use skb-mode\n"
-		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n"
-		"    -X    load xdp program on egress\n",
-		prog);
+	int i;
+
+	printf("\n");
+	printf(" Usage: %s (options-see-below)\n",
+	       argv[0]);
+	printf(" Listing options:\n");
+	for (i = 0; long_options[i].name != 0; i++) {
+		printf(" --%-15s", long_options[i].name);
+		if (long_options[i].flag != NULL)
+			printf(" flag (internal value:%d)",
+			       *long_options[i].flag);
+		else
+			printf("short-option: -%c",
+			       long_options[i].val);
+		printf("\n");
+	}
+	printf("\n");
 }
 
 int main(int argc, char **argv)
@@ -88,13 +110,14 @@ int main(int argc, char **argv)
 	char str[2 * IF_NAMESIZE + 1];
 	__u32 info_len = sizeof(info);
 	char ifname_out[IF_NAMESIZE];
-	const char *optstr = "FSNX";
 	char ifname_in[IF_NAMESIZE];
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
+	int interval = 2;
 
-	while ((opt = getopt(argc, argv, optstr)) != -1) {
+	while ((opt = getopt_long(argc, argv, "FSNXi:vs",
+				  long_options, NULL)) != -1) {
 		switch (opt) {
 		case 'S':
 			xdp_flags |= XDP_FLAGS_SKB_MODE;
@@ -108,8 +131,17 @@ int main(int argc, char **argv)
 		case 'X':
 			xdp_devmap_attached = true;
 			break;
+		case 'i':
+			interval = atoi(optarg);
+			break;
+		case 'v':
+			sample_log_level ^= LL_DEBUG - 1;
+			break;
+		case 's':
+			mask |= SAMPLE_REDIRECT_MAP_CNT;
+			break;
 		default:
-			usage(basename(argv[0]));
+			usage(argv);
 			return 1;
 		}
 	}
@@ -122,7 +154,7 @@ int main(int argc, char **argv)
 	}
 
 	if (argc <= optind + 1) {
-		usage(basename(argv[0]));
+		usage(argv);
 		return 1;
 	}
 
@@ -252,9 +284,20 @@ int main(int argc, char **argv)
 	       ifname_in, ifindex_in, str, ifname_out, ifindex_out,
 	       get_driver_name(ifindex_out) ?: "(err)");
 
+	if ((mask & SAMPLE_REDIRECT_CNT) == 0) {
+		/* The bpf_link[i] depend on the order of
+		 * the functions was defined in _kern.c
+		 */
+		bpf_link__destroy(tp_links[2]);	/* tracepoint/xdp/xdp_redirect */
+		tp_links[2] = NULL;
+
+		bpf_link__destroy(tp_links[3]);	/* tracepoint/xdp/xdp_redirect_map */
+		tp_links[3] = NULL;
+	}
+
 	snprintf(str, sizeof(str), "%s->%s", ifname_in, ifname_out);
 
-	sample_stats_poll(1, mask, str, true);
+	sample_stats_poll(interval, mask, str, true);
 
 	return 0;
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 14/15] samples: bpf: add documentation
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (12 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 13/15] samples: bpf: add new options for xdp samples Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-28 23:52 ` [PATCH RFC bpf-next 15/15] samples: bpf: convert xdp_samples to use raw_tracepoints Kumar Kartikeya Dwivedi
  14 siblings, 0 replies; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

This prints some help text meant to explain the output.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_monitor_user.c      | 10 ++++--
 samples/bpf/xdp_redirect_cpu_user.c |  8 +++--
 samples/bpf/xdp_redirect_map_user.c |  6 ++--
 samples/bpf/xdp_sample_user.c       | 52 +++++++++++++++++++++++++++++
 samples/bpf/xdp_sample_user.h       |  1 +
 5 files changed, 69 insertions(+), 8 deletions(-)

diff --git a/samples/bpf/xdp_monitor_user.c b/samples/bpf/xdp_monitor_user.c
index b37d8f7379ec..71d59e714bae 100644
--- a/samples/bpf/xdp_monitor_user.c
+++ b/samples/bpf/xdp_monitor_user.c
@@ -35,6 +35,10 @@ static const char *__doc_err_only__=
 static bool debug = false;
 struct bpf_object *obj;
 
+static int mask = SAMPLE_REDIRECT_ERR_CNT | SAMPLE_CPUMAP_ENQUEUE_CNT |
+		  SAMPLE_CPUMAP_KTHREAD_CNT | SAMPLE_EXCEPTION_CNT |
+		  SAMPLE_DEVMAP_XMIT_CNT;
+
 static const struct option long_options[] = {
 	{"help",	no_argument,		NULL, 'h' },
 	{"debug",	no_argument,		NULL, 'D' },
@@ -56,6 +60,9 @@ static void int_exit(int sig)
 static void usage(char *argv[])
 {
 	int i;
+
+	sample_print_help(mask);
+
 	printf("\nDOCUMENTATION:\n%s\n", __doc__);
 	printf("\n");
 	printf(" Usage: %s (options-see-below)\n",
@@ -110,9 +117,6 @@ static void print_bpf_prog_info(void)
 
 int main(int argc, char **argv)
 {
-	int mask = SAMPLE_REDIRECT_ERR_CNT | SAMPLE_CPUMAP_ENQUEUE_CNT |
-		   SAMPLE_CPUMAP_KTHREAD_CNT | SAMPLE_EXCEPTION_CNT |
-		   SAMPLE_DEVMAP_XMIT_CNT;
 	int longindex = 0, opt;
 	int ret = EXIT_FAILURE;
 	char filename[256];
diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c
index d56b89254cd1..9233b8a2bf2d 100644
--- a/samples/bpf/xdp_redirect_cpu_user.c
+++ b/samples/bpf/xdp_redirect_cpu_user.c
@@ -37,6 +37,9 @@ static int avail_fd;
 static int count_fd;
 
 static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_CNT |
+		  SAMPLE_CPUMAP_ENQUEUE_CNT | SAMPLE_CPUMAP_KTHREAD_CNT |
+		  SAMPLE_EXCEPTION_CNT;
 
 static const struct option long_options[] = {
 	{"help",	no_argument,		NULL, 'h' },
@@ -96,6 +99,8 @@ static void usage(char *argv[], struct bpf_object *obj)
 {
 	int i;
 
+	sample_print_help(mask);
+
 	printf("\nDOCUMENTATION:\n%s\n", __doc__);
 	printf("\n");
 	printf(" Usage: %s (options-see-below)\n", argv[0]);
@@ -201,9 +206,6 @@ static void __stats_poll(int interval, bool redir_suc, char *prog_name,
 			 char *mprog_name, struct bpf_cpumap_val *value,
 			 bool stress_mode)
 {
-	int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_CNT |
-		   SAMPLE_CPUMAP_ENQUEUE_CNT | SAMPLE_CPUMAP_KTHREAD_CNT |
-		   SAMPLE_EXCEPTION_CNT;
 	struct stats_record *record, *prev;
 
 	record = alloc_stats_record();
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index eb4013fa58cb..f4bdefa83709 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -28,6 +28,8 @@ static __u32 prog_id;
 static __u32 dummy_prog_id;
 
 static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
+static int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT |
+		  SAMPLE_EXCEPTION_CNT | SAMPLE_DEVMAP_XMIT_CNT;
 
 static const struct option long_options[] = {
 	{"help",	no_argument,		NULL, 'h' },
@@ -78,6 +80,8 @@ static void usage(char *argv[])
 {
 	int i;
 
+	sample_print_help(mask);
+
 	printf("\n");
 	printf(" Usage: %s (options-see-below)\n",
 	       argv[0]);
@@ -97,8 +101,6 @@ static void usage(char *argv[])
 
 int main(int argc, char **argv)
 {
-	int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT |
-		   SAMPLE_EXCEPTION_CNT | SAMPLE_DEVMAP_XMIT_CNT;
 	struct bpf_prog_load_attr prog_load_attr = {
 		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
index 96d36c708ee3..aa02d9bbea6c 100644
--- a/samples/bpf/xdp_sample_user.c
+++ b/samples/bpf/xdp_sample_user.c
@@ -77,6 +77,58 @@ static bool err_exp;
 #define PASS(pass) pass, "pass/s"
 #define REDIR(redir) redir, "redir/s"
 
+void sample_print_help(int mask)
+{
+	printf("Output format description\n\n"
+	       "By default, redirect success statistics are disabled, use -s to enable.\n"
+	       "The terse output mode is default, verbose mode can be activated using -v\n"
+	       "Use SIGQUIT (Ctrl + \\) to switch the mode dynamically at runtime\n\n"
+	       "Terse mode displays at most the following fields:\n"
+	       "  rx/s     Number of packets received per second\n"
+	       "  redir/s  Number of packets successfully redirected per second\n"
+	       "  error/s  Aggregated count of errors per second (including dropped packets)\n"
+	       "  xmit/s   Number of packets transmitted on the output device per second\n\n"
+	       "Output description for verbose mode:\n"
+	       "  FIELD         DESCRIPTION\n");
+	if (mask & SAMPLE_RX_CNT) {
+		printf("  receive\tDisplays the number of packets received & errors encountered\n"
+		       " \t\tWhenever an error or packet drop occurs, details of per CPU error\n"
+		       " \t\tand drop statistics will be expanded inline in terse mode.\n"
+		       " \t\t\tpkt/s     - Packets received per second\n"
+		       " \t\t\tdrop/s    - Packets dropped per second\n"
+		       " \t\t\terror/s   - Errors encountered per second\n\n");
+	}
+	if (mask & (SAMPLE_REDIRECT_CNT|SAMPLE_REDIRECT_ERR_CNT)) {
+		printf("  redirect\tDisplays the number of packets successfully redirected\n"
+		       "  \t\tErrors encountered are expanded under redirect_err field\n"
+		       "  \t\tNote that passing -s to enable it has a per packet overhead\n"
+		       "  \t\t\tredir/s   - Packets redirected successfully per second\n\n"
+		       "  redirect_err\tDisplays the number of packets that failed redirection\n"
+		       "  \t\tThe errno is expanded under this field with per CPU count\n"
+		       "  \t\tThe recognized errors are EOPNOTSUPP, EINVAL, ENETDOWN and EMSGSIZE\n"
+		       "  \t\t\terror/s   - Packets that failed redirection per second\n\n");
+	}
+
+	if (mask & SAMPLE_EXCEPTION_CNT) {
+		printf("  xdp_exception\tDisplays xdp_exception tracepoint events\n"
+		       "  \t\tThis can occur due to internal driver errors, unrecognized\n"
+		       "  \t\tXDP actions and due to explicit user trigger by use of XDP_ABORTED\n"
+		       "  \t\tEach action is expanded below this field with its count\n"
+		       "  \t\t\thit/s     - Number of times the tracepoint was hit per second\n\n");
+	}
+
+	if (mask & SAMPLE_DEVMAP_XMIT_CNT) {
+		printf("  devmap_xmit\tDisplays devmap_xmit tracepoint events\n"
+		       "  \t\tThis tracepoint is invoked for successful transmissions on output\n"
+		       "  \t\tdevice but these statistics are not available for generic XDP mode,\n"
+		       "  \t\thence they will be omitted from the output when using SKB mode\n"
+		       "  \t\t\txmit/s    - Number of packets that were transmitted per second\n"
+		       "  \t\t\tdrop/s    - Number of packets that failed transmissions per second\n"
+		       "  \t\t\tdrv_err/s - Number of internal driver errors per second\n"
+		       "  \t\t\tbulk_avg  - Average number of packets processed for each event\n\n");
+	}
+}
+
 static const char *elixir_search[NUM_TP] = {
 	[TP_REDIRECT_CNT] = "_trace_xdp_redirect",
 	[TP_REDIRECT_MAP_CNT] = "_trace_xdp_redirect_map",
diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h
index 588bd2f15352..41be57d7b663 100644
--- a/samples/bpf/xdp_sample_user.h
+++ b/samples/bpf/xdp_sample_user.h
@@ -169,6 +169,7 @@ void sample_stats_print_cpumap_remote(struct stats_record *stats_rec,
 				      struct stats_record *stats_prev,
 				      unsigned int nr_cpus, char *mprog_name);
 void sample_reset_mode(void);
+void sample_print_help(int mask);
 
 const char *get_driver_name(int ifindex);
 int get_mac_addr(int ifindex, void *mac_addr);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH RFC bpf-next 15/15] samples: bpf: convert xdp_samples to use raw_tracepoints
  2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
                   ` (13 preceding siblings ...)
  2021-05-28 23:52 ` [PATCH RFC bpf-next 14/15] samples: bpf: add documentation Kumar Kartikeya Dwivedi
@ 2021-05-28 23:52 ` Kumar Kartikeya Dwivedi
  2021-05-30  3:07   ` Andrii Nakryiko
  14 siblings, 1 reply; 18+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2021-05-28 23:52 UTC (permalink / raw)
  To: bpf
  Cc: Kumar Kartikeya Dwivedi, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen, netdev

These are faster, and hence speeds up cases where user passes --stats to
enable success case redirect accounting. We can extend this to all other
tracepoints as well, so make that part of this change.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 samples/bpf/xdp_sample_kern.h | 145 +++++++++++-----------------------
 samples/bpf/xdp_sample_user.c |   2 +-
 2 files changed, 45 insertions(+), 102 deletions(-)

diff --git a/samples/bpf/xdp_sample_kern.h b/samples/bpf/xdp_sample_kern.h
index dd7f7ea63166..08fbc55df3fd 100644
--- a/samples/bpf/xdp_sample_kern.h
+++ b/samples/bpf/xdp_sample_kern.h
@@ -3,6 +3,9 @@
 #pragma once
 
 #include <uapi/linux/bpf.h>
+#include <net/xdp.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_tracing.h>
 #include <bpf/bpf_helpers.h>
 
 #ifndef NR_CPUS
@@ -85,20 +88,6 @@ struct {
 
 /*** Trace point code ***/
 
-/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_redirect/format
- * Code in:                kernel/include/trace/events/xdp.h
- */
-struct xdp_redirect_ctx {
-	u64 __pad;	// First 8 bytes are not accessible by bpf code
-	int prog_id;	//	offset:8;  size:4; signed:1;
-	u32 act;	//	offset:12  size:4; signed:0;
-	int ifindex;	//	offset:16  size:4; signed:1;
-	int err;	//	offset:20  size:4; signed:1;
-	int to_ifindex;	//	offset:24  size:4; signed:1;
-	u32 map_id;	//	offset:28  size:4; signed:0;
-	int map_index;	//	offset:32  size:4; signed:1;
-};			//	offset:36
-
 enum {
 	XDP_REDIRECT_SUCCESS = 0,
 	XDP_REDIRECT_ERROR = 1
@@ -124,11 +113,11 @@ __u32 xdp_get_err_key(int err)
 }
 
 static __always_inline
-int xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx)
+int xdp_redirect_collect_stat(struct bpf_raw_tracepoint_args *ctx)
 {
 	u32 key = XDP_REDIRECT_ERROR;
+	int err = ctx->args[3];
 	struct datarec *rec;
-	int err = ctx->err;
 
 	key = xdp_get_err_key(err);
 
@@ -149,47 +138,35 @@ int xdp_redirect_collect_stat(struct xdp_redirect_ctx *ctx)
 	 */
 }
 
-SEC("tracepoint/xdp/xdp_redirect_err")
-int trace_xdp_redirect_err(struct xdp_redirect_ctx *ctx)
+SEC("raw_tracepoint/xdp_redirect_err")
+int trace_xdp_redirect_err(struct bpf_raw_tracepoint_args *ctx)
 {
 	return xdp_redirect_collect_stat(ctx);
 }
 
-SEC("tracepoint/xdp/xdp_redirect_map_err")
-int trace_xdp_redirect_map_err(struct xdp_redirect_ctx *ctx)
+SEC("raw_tracepoint/xdp_redirect_map_err")
+int trace_xdp_redirect_map_err(struct bpf_raw_tracepoint_args *ctx)
 {
 	return xdp_redirect_collect_stat(ctx);
 }
 
-/* Likely unloaded when prog starts */
-SEC("tracepoint/xdp/xdp_redirect")
-int trace_xdp_redirect(struct xdp_redirect_ctx *ctx)
+SEC("raw_tracepoint/xdp_redirect")
+int trace_xdp_redirect(struct bpf_raw_tracepoint_args *ctx)
 {
 	return xdp_redirect_collect_stat(ctx);
 }
 
-/* Likely unloaded when prog starts */
-SEC("tracepoint/xdp/xdp_redirect_map")
-int trace_xdp_redirect_map(struct xdp_redirect_ctx *ctx)
+SEC("raw_tracepoint/xdp_redirect_map")
+int trace_xdp_redirect_map(struct bpf_raw_tracepoint_args *ctx)
 {
 	return xdp_redirect_collect_stat(ctx);
 }
 
-/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_exception/format
- * Code in:                kernel/include/trace/events/xdp.h
- */
-struct xdp_exception_ctx {
-	u64 __pad;	// First 8 bytes are not accessible by bpf code
-	int prog_id;	//	offset:8;  size:4; signed:1;
-	u32 act;	//	offset:12; size:4; signed:0;
-	int ifindex;	//	offset:16; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_exception")
-int trace_xdp_exception(struct xdp_exception_ctx *ctx)
+SEC("raw_tracepoint/xdp_exception")
+int trace_xdp_exception(struct bpf_raw_tracepoint_args *ctx)
 {
+	u32 key = ctx->args[2];
 	struct datarec *rec;
-	u32 key = ctx->act;
 
 	if (key > XDP_REDIRECT)
 		key = XDP_UNKNOWN;
@@ -202,23 +179,10 @@ int trace_xdp_exception(struct xdp_exception_ctx *ctx)
 	return 0;
 }
 
-/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_enqueue/format
- * Code in:         kernel/include/trace/events/xdp.h
- */
-struct cpumap_enqueue_ctx {
-	u64 __pad;		// First 8 bytes are not accessible by bpf code
-	int map_id;		//	offset:8;  size:4; signed:1;
-	u32 act;		//	offset:12; size:4; signed:0;
-	int cpu;		//	offset:16; size:4; signed:1;
-	unsigned int drops;	//	offset:20; size:4; signed:0;
-	unsigned int processed;	//	offset:24; size:4; signed:0;
-	int to_cpu;		//	offset:28; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_cpumap_enqueue")
-int trace_xdp_cpumap_enqueue(struct cpumap_enqueue_ctx *ctx)
+SEC("raw_tracepoint/xdp_cpumap_enqueue")
+int trace_xdp_cpumap_enqueue(struct bpf_raw_tracepoint_args *ctx)
 {
-	u32 to_cpu = ctx->to_cpu;
+	u32 to_cpu = ctx->args[3];
 	struct datarec *rec;
 
 	if (to_cpu >= MAX_CPUS)
@@ -227,11 +191,11 @@ int trace_xdp_cpumap_enqueue(struct cpumap_enqueue_ctx *ctx)
 	rec = bpf_map_lookup_elem(&cpumap_enqueue_cnt, &to_cpu);
 	if (!rec)
 		return 0;
-	rec->processed += ctx->processed;
-	rec->dropped   += ctx->drops;
+	rec->processed += ctx->args[1];
+	rec->dropped   += ctx->args[2];
 
 	/* Record bulk events, then userspace can calc average bulk size */
-	if (ctx->processed > 0)
+	if (ctx->args[1] > 0)
 		rec->issue += 1;
 
 	/* Inception: It's possible to detect overload situations, via
@@ -242,78 +206,57 @@ int trace_xdp_cpumap_enqueue(struct cpumap_enqueue_ctx *ctx)
 	return 0;
 }
 
-/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_kthread/format
- * Code in:         kernel/include/trace/events/xdp.h
- */
-struct cpumap_kthread_ctx {
-	u64 __pad;			// First 8 bytes are not accessible
-	int map_id;			//	offset:8;  size:4; signed:1;
-	u32 act;			//	offset:12; size:4; signed:0;
-	int cpu;			//	offset:16; size:4; signed:1;
-	unsigned int drops;		//	offset:20; size:4; signed:0;
-	unsigned int processed;		//	offset:24; size:4; signed:0;
-	int sched;			//	offset:28; size:4; signed:1;
-	unsigned int xdp_pass;		//	offset:32; size:4; signed:0;
-	unsigned int xdp_drop;		//	offset:36; size:4; signed:0;
-	unsigned int xdp_redirect;	//	offset:40; size:4; signed:0;
-};
-
-SEC("tracepoint/xdp/xdp_cpumap_kthread")
-int trace_xdp_cpumap_kthread(struct cpumap_kthread_ctx *ctx)
+SEC("raw_tracepoint/xdp_cpumap_kthread")
+int trace_xdp_cpumap_kthread(struct bpf_raw_tracepoint_args *ctx)
 {
+	struct xdp_cpumap_stats *stats;
 	struct datarec *rec;
 	u32 key = 0;
 
+	stats = (struct xdp_cpumap_stats *) ctx->args[4];
+	if (!stats)
+		return 0;
+
 	rec = bpf_map_lookup_elem(&cpumap_kthread_cnt, &key);
 	if (!rec)
 		return 0;
-	rec->processed += ctx->processed;
-	rec->dropped   += ctx->drops;
-	rec->xdp_pass  += ctx->xdp_pass;
-	rec->xdp_drop  += ctx->xdp_drop;
-	rec->xdp_redirect  += ctx->xdp_redirect;
+	rec->processed += ctx->args[1];
+	rec->dropped   += ctx->args[2];
+
+	rec->xdp_pass  += BPF_CORE_READ(stats, pass);
+	rec->xdp_drop  += BPF_CORE_READ(stats, drop);
+	rec->xdp_redirect  += BPF_CORE_READ(stats, redirect);
 
 	/* Count times kthread yielded CPU via schedule call */
-	if (ctx->sched)
+	if (ctx->args[3])
 		rec->issue++;
 
 	return 0;
 }
 
-/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_devmap_xmit/format
- * Code in:         kernel/include/trace/events/xdp.h
- */
-struct devmap_xmit_ctx {
-	u64 __pad;		// First 8 bytes are not accessible by bpf code
-	int from_ifindex;	//	offset:8;  size:4; signed:1;
-	u32 act;		//	offset:12; size:4; signed:0;
-	int to_ifindex;		//	offset:16; size:4; signed:1;
-	int drops;		//	offset:20; size:4; signed:1;
-	int sent;		//	offset:24; size:4; signed:1;
-	int err;		//	offset:28; size:4; signed:1;
-};
-
-SEC("tracepoint/xdp/xdp_devmap_xmit")
-int trace_xdp_devmap_xmit(struct devmap_xmit_ctx *ctx)
+SEC("raw_tracepoint/xdp_devmap_xmit")
+int trace_xdp_devmap_xmit(struct bpf_raw_tracepoint_args *ctx)
 {
 	struct datarec *rec;
 	u32 key = 0;
+	int drops;
 
 	rec = bpf_map_lookup_elem(&devmap_xmit_cnt, &key);
 	if (!rec)
 		return 0;
-	rec->processed += ctx->sent;
-	rec->dropped   += ctx->drops;
+	rec->processed += ctx->args[2];
+	rec->dropped   += ctx->args[3];
 
 	/* Record bulk events, then userspace can calc average bulk size */
 	rec->info += 1;
 
 	/* Record error cases, where no frame were sent */
-	if (ctx->err)
+	if (ctx->args[4])
 		rec->issue++;
 
+	drops = ctx->args[3];
 	/* Catch API error of drv ndo_xdp_xmit sent more than count */
-	if (ctx->drops < 0)
+	if (drops < 0)
 		rec->issue++;
 
 	return 1;
diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c
index aa02d9bbea6c..539c0c78fcb0 100644
--- a/samples/bpf/xdp_sample_user.c
+++ b/samples/bpf/xdp_sample_user.c
@@ -805,7 +805,7 @@ static int init_tracepoints(struct bpf_object *obj)
 	struct bpf_program *prog;
 
 	bpf_object__for_each_program(prog, obj) {
-		if (bpf_program__is_tracepoint(prog) != true)
+		if (!bpf_program__is_raw_tracepoint(prog))
 			continue;
 
 		tp_links[tp_cnt] = bpf_program__attach(prog);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH RFC bpf-next 03/15] samples: bpf: split out common bpf progs to its own file
  2021-05-28 23:52 ` [PATCH RFC bpf-next 03/15] samples: bpf: split out common bpf progs to its own file Kumar Kartikeya Dwivedi
@ 2021-05-30  3:05   ` Andrii Nakryiko
  0 siblings, 0 replies; 18+ messages in thread
From: Andrii Nakryiko @ 2021-05-30  3:05 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Song Liu, Yonghong Song, John Fastabend,
	KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen,
	Networking

On Fri, May 28, 2021 at 4:53 PM Kumar Kartikeya Dwivedi
<memxor@gmail.com> wrote:
>
> This is done to later reuse these in a way that can be shared
> among multiple samples.
>
> We are using xdp_redirect_cpu_kern.c as a base to build further support on
> top (mostly adding a few other things missing that xdp_monitor does in
> subsequent patches).
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
>  samples/bpf/xdp_sample_kern.h | 220 ++++++++++++++++++++++++++++++++++
>  1 file changed, 220 insertions(+)
>  create mode 100644 samples/bpf/xdp_sample_kern.h
>
> diff --git a/samples/bpf/xdp_sample_kern.h b/samples/bpf/xdp_sample_kern.h

instead of doing it as a header, can you please use BPF static linking
instead? I think that's a better approach and a good showcase for
anyone that would like to use static linking for their BPF programs

> new file mode 100644
> index 000000000000..bb809542ac20
> --- /dev/null
> +++ b/samples/bpf/xdp_sample_kern.h
> @@ -0,0 +1,220 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*  GPLv2, Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc. */
> +#pragma once
> +
> +#include <uapi/linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +
> +#define MAX_CPUS 64
> +
> +/* Common stats data record to keep userspace more simple */
> +struct datarec {
> +       __u64 processed;
> +       __u64 dropped;
> +       __u64 issue;
> +       __u64 xdp_pass;
> +       __u64 xdp_drop;
> +       __u64 xdp_redirect;
> +};
> +
> +/* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
> + * feedback.  Redirect TX errors can be caught via a tracepoint.
> + */
> +struct {
> +       __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
> +       __type(key, u32);
> +       __type(value, struct datarec);
> +       __uint(max_entries, 1);
> +} rx_cnt SEC(".maps");
> +
> +/* Used by trace point */
> +struct {
> +       __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
> +       __type(key, u32);
> +       __type(value, struct datarec);
> +       __uint(max_entries, 2);
> +       /* TODO: have entries for all possible errno's */
> +} redirect_err_cnt SEC(".maps");
> +
> +/* Used by trace point */
> +struct {
> +       __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
> +       __type(key, u32);
> +       __type(value, struct datarec);
> +       __uint(max_entries, MAX_CPUS);
> +} cpumap_enqueue_cnt SEC(".maps");

One way to squeeze a bit more performance would be to instead use
global variables instead of maps:

struct datarec cpu_map_enqueue_cnts[MAX_CPUS][MAX_CPUS];

and other PERCPU_ARRAY arrays could be just one-dimensional arrays.

You'd need to ensure each value sits on its own cache-line, of course.

> +
> +/* Used by trace point */
> +struct {
> +       __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
> +       __type(key, u32);
> +       __type(value, struct datarec);
> +       __uint(max_entries, 1);
> +} cpumap_kthread_cnt SEC(".maps");
> +

[...]

> +
> +/* Tracepoint: /sys/kernel/debug/tracing/events/xdp/xdp_cpumap_enqueue/format
> + * Code in:         kernel/include/trace/events/xdp.h
> + */
> +struct cpumap_enqueue_ctx {
> +       u64 __pad;              // First 8 bytes are not accessible by bpf code
> +       int map_id;             //      offset:8;  size:4; signed:1;
> +       u32 act;                //      offset:12; size:4; signed:0;
> +       int cpu;                //      offset:16; size:4; signed:1;
> +       unsigned int drops;     //      offset:20; size:4; signed:0;
> +       unsigned int processed; //      offset:24; size:4; signed:0;
> +       int to_cpu;             //      offset:28; size:4; signed:1;
> +};

if you used vmlinux.h, this is already in there as struct
trace_event_raw_xdp_cpumap_enqueue, similarly for other tracepoints

> +
> +SEC("tracepoint/xdp/xdp_cpumap_enqueue")
> +int trace_xdp_cpumap_enqueue(struct cpumap_enqueue_ctx *ctx)
> +{
> +       u32 to_cpu = ctx->to_cpu;
> +       struct datarec *rec;
> +
> +       if (to_cpu >= MAX_CPUS)
> +               return 1;
> +
> +       rec = bpf_map_lookup_elem(&cpumap_enqueue_cnt, &to_cpu);
> +       if (!rec)
> +               return 0;
> +       rec->processed += ctx->processed;
> +       rec->dropped   += ctx->drops;
> +
> +       /* Record bulk events, then userspace can calc average bulk size */
> +       if (ctx->processed > 0)
> +               rec->issue += 1;
> +
> +       /* 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;
> +}
> +

[...]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH RFC bpf-next 15/15] samples: bpf: convert xdp_samples to use raw_tracepoints
  2021-05-28 23:52 ` [PATCH RFC bpf-next 15/15] samples: bpf: convert xdp_samples to use raw_tracepoints Kumar Kartikeya Dwivedi
@ 2021-05-30  3:07   ` Andrii Nakryiko
  0 siblings, 0 replies; 18+ messages in thread
From: Andrii Nakryiko @ 2021-05-30  3:07 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Martin KaFai Lau, Song Liu, Yonghong Song, John Fastabend,
	KP Singh, David S. Miller, Jakub Kicinski,
	Jesper Dangaard Brouer, Toke Høiland-Jørgensen,
	Networking

On Fri, May 28, 2021 at 4:54 PM Kumar Kartikeya Dwivedi
<memxor@gmail.com> wrote:
>
> These are faster, and hence speeds up cases where user passes --stats to
> enable success case redirect accounting. We can extend this to all other
> tracepoints as well, so make that part of this change.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
>  samples/bpf/xdp_sample_kern.h | 145 +++++++++++-----------------------
>  samples/bpf/xdp_sample_user.c |   2 +-
>  2 files changed, 45 insertions(+), 102 deletions(-)
>

[...]

>
> -/* Tracepoint format: /sys/kernel/debug/tracing/events/xdp/xdp_exception/format
> - * Code in:                kernel/include/trace/events/xdp.h
> - */
> -struct xdp_exception_ctx {
> -       u64 __pad;      // First 8 bytes are not accessible by bpf code
> -       int prog_id;    //      offset:8;  size:4; signed:1;
> -       u32 act;        //      offset:12; size:4; signed:0;
> -       int ifindex;    //      offset:16; size:4; signed:1;
> -};
> -
> -SEC("tracepoint/xdp/xdp_exception")
> -int trace_xdp_exception(struct xdp_exception_ctx *ctx)
> +SEC("raw_tracepoint/xdp_exception")
> +int trace_xdp_exception(struct bpf_raw_tracepoint_args *ctx)
>  {

check out use of BPF_PROG macro for raw_tracepoint and fentry/fexit
programs, it looks nicer, IMO.

> +       u32 key = ctx->args[2];
>         struct datarec *rec;
> -       u32 key = ctx->act;
>
>         if (key > XDP_REDIRECT)
>                 key = XDP_UNKNOWN;

[...]

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2021-05-30  3:07 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-28 23:52 [PATCH RFC bpf-next 00/15] Improve XDP samples usability and output Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 01/15] samples: bpf: fix a couple of NULL dereferences Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 02/15] samples: bpf: fix a couple of warnings Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 03/15] samples: bpf: split out common bpf progs to its own file Kumar Kartikeya Dwivedi
2021-05-30  3:05   ` Andrii Nakryiko
2021-05-28 23:52 ` [PATCH RFC bpf-next 04/15] samples: bpf: refactor generic parts out of xdp_redirect_cpu_user Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 05/15] samples: bpf: convert xdp_redirect_map to use xdp_samples Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 06/15] samples: bpf: prepare devmap_xmit support in xdp_sample Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 07/15] samples: bpf: add extended reporting for xdp redirect error Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 08/15] samples: bpf: add per exception reporting for xdp_exception Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 09/15] samples: bpf: convert xdp_monitor to use xdp_samples Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 10/15] samples: bpf: implement terse output mode and make it default Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 11/15] samples: bpf: print summary of session on exit Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 12/15] samples: bpf: subtract time spent in collection from polling interval Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 13/15] samples: bpf: add new options for xdp samples Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 14/15] samples: bpf: add documentation Kumar Kartikeya Dwivedi
2021-05-28 23:52 ` [PATCH RFC bpf-next 15/15] samples: bpf: convert xdp_samples to use raw_tracepoints Kumar Kartikeya Dwivedi
2021-05-30  3:07   ` Andrii Nakryiko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.