* [PATCH v5 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE
@ 2026-10-31 17:30 Zhu Yanjun
2026-10-31 17:30 ` [PATCH v5 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
2026-10-31 17:30 ` [PATCH v5 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
0 siblings, 2 replies; 5+ messages in thread
From: Zhu Yanjun @ 2026-10-31 17:30 UTC (permalink / raw)
To: jgg, leon, shuah, zyjzyj2000, linux-rdma, linux-kselftest,
yanjun.zhu
Add a new self-test case to verify RXE (Soft-RoCE) traffic using
netkit virtual interfaces and an eBPF tcx ingress program to dump
and parse RDMA transport headers (BTH, etc.).
The test results:
# make -C tools/testing/selftests/ TARGETS=rdma run_tests
make: Entering directory '/root/linux/tools/testing/selftests'
clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -I../../../../usr/include -I../../../../include/uapi -I../bpf -I/root/linux/tools/testing/selftests/rdma -c rxe_pkg_kernel_dump.c -o /root/linux/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.o
bpftool gen skeleton /root/linux/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.o > /root/linux/tools/testing/selftests/rdma/rxe_pkg_kernel.skel.h
gcc -D_GNU_SOURCE= -I/root/linux/tools/testing/selftests/../../../tools/testing/selftests -g -Wall -I../../../../usr/include -I../../../../include/uapi -I../bpf -I/root/linux/tools/testing/selftests/rdma rxe_pkg_user_dump.c -lbpf -lelf -lz -o /root/linux/tools/testing/selftests/rdma/rxe_netkit_dump
# timeout set to 45
# selftests: rdma: rxe_rping_between_netns.sh
# client DISCONNECT EVENT...
ok 1 selftests: rdma: rxe_rping_between_netns.sh
# timeout set to 45
# selftests: rdma: rxe_ipv6.sh
ok 2 selftests: rdma: rxe_ipv6.sh
# timeout set to 45
# selftests: rdma: rxe_socket_with_netns.sh
ok 3 selftests: rdma: rxe_socket_with_netns.sh
# timeout set to 45
# selftests: rdma: rxe_test_NETDEV_UNREGISTER.sh
ok 4 selftests: rdma: rxe_test_NETDEV_UNREGISTER.sh
# timeout set to 45
# selftests: rdma: rxe_sent_rcvd_bytes.sh
# server DISCONNECT EVENT...
# wait for RDMA_READ_ADV state 10
# client DISCONNECT EVENT...
ok 5 selftests: rdma: rxe_sent_rcvd_bytes.sh
# timeout set to 45
# selftests: rdma: rxe_netns_names.sh
# 1..0 # SKIP rdma netns exclusive mode is not supported
ok 6 selftests: rdma: rxe_netns_names.sh # SKIP
# timeout set to 45
# selftests: rdma: rxe_pkg_netkit.sh
# server DISCONNECT EVENT...
# wait for RDMA_READ_ADV state 10
# ./rxe_pkg_netkit.sh: line 28: 9866 Killed "$CUR_WD/rxe_netkit_dump"
ok 7 selftests: rdma: rxe_pkg_netkit.sh
make: Leaving directory '/root/linux/tools/testing/selftests'
---
V4 - > V5: Fix err and length problems
V3 - > V4: Fix problems in user space app
V2 - > V3: Fix problems in user space application
V1 - > V2: Fix problems from sashiko
---
Zhu Yanjun (2):
selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
selftests/rdma: Add netkit-based automated test script for RXE
tools/testing/selftests/rdma/Makefile | 23 +-
tools/testing/selftests/rdma/config | 3 +
.../selftests/rdma/rxe_pkg_kernel_dump.c | 62 +
.../testing/selftests/rdma/rxe_pkg_netkit.sh | 63 +
.../selftests/rdma/rxe_pkg_user_dump.c | 1275 +++++++++++++++++
5 files changed, 1425 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
create mode 100755 tools/testing/selftests/rdma/rxe_pkg_netkit.sh
create mode 100644 tools/testing/selftests/rdma/rxe_pkg_user_dump.c
--
2.54.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
2026-10-31 17:30 [PATCH v5 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
@ 2026-10-31 17:30 ` Zhu Yanjun
2026-10-31 17:47 ` sashiko-bot
2026-10-31 17:30 ` [PATCH v5 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
1 sibling, 1 reply; 5+ messages in thread
From: Zhu Yanjun @ 2026-10-31 17:30 UTC (permalink / raw)
To: jgg, leon, shuah, zyjzyj2000, linux-rdma, linux-kselftest,
yanjun.zhu
Add eBPF-based kernel and user-space packet dump utilities along with a
netkit test script (rxe_pkg_netkit.sh) to tools/testing/selftests/rdma/.
This enables capturing and parsing Soft-RoCE (RXE) traffic over netkit
devices for testing purposes.
Signed-off-by: Zhu Yanjun <yanjun.zhu@linux.dev>
---
tools/testing/selftests/rdma/Makefile | 24 +-
tools/testing/selftests/rdma/config | 3 +
tools/testing/selftests/rdma/rxe_pkg_common.h | 17 +
.../selftests/rdma/rxe_pkg_kernel_dump.c | 78 +
.../selftests/rdma/rxe_pkg_user_dump.c | 1388 +++++++++++++++++
5 files changed, 1509 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/rdma/rxe_pkg_common.h
create mode 100644 tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
create mode 100644 tools/testing/selftests/rdma/rxe_pkg_user_dump.c
diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile
index a91c14c45006..f7783c20f40d 100644
--- a/tools/testing/selftests/rdma/Makefile
+++ b/tools/testing/selftests/rdma/Makefile
@@ -4,6 +4,28 @@ TEST_PROGS := rxe_rping_between_netns.sh \
rxe_socket_with_netns.sh \
rxe_test_NETDEV_UNREGISTER.sh \
rxe_sent_rcvd_bytes.sh \
- rxe_netns_names.sh
+ rxe_netns_names.sh \
+ rxe_pkg_netkit.sh
+TEST_GEN_PROGS_EXTENDED := rxe_netkit_dump
include ../lib.mk
+
+CLANG ?= clang
+LLC ?= llc
+BPFTOOL ?= bpftool
+ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/')
+
+INCLUDES := -I../../../../usr/include -I../../../../include/uapi -I../bpf -I$(OUTPUT)
+CFLAGS += -g -Wall $(INCLUDES)
+LDFLAGS += -lbpf -lelf -lz
+
+$(OUTPUT)/rxe_netkit_dump: rxe_pkg_user_dump.c $(OUTPUT)/rxe_pkg_kernel.skel.h
+ $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@
+
+$(OUTPUT)/rxe_pkg_kernel_dump.o: rxe_pkg_kernel_dump.c rxe_pkg_common.h
+ $(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) -c $< -o $@
+
+$(OUTPUT)/rxe_pkg_kernel.skel.h: $(OUTPUT)/rxe_pkg_kernel_dump.o
+ $(BPFTOOL) gen skeleton $< > $@
+
+EXTRA_CLEAN := $(OUTPUT)/rxe_pkg_kernel_dump.o $(OUTPUT)/rxe_pkg_kernel.skel.h $(OUTPUT)/rxe_pkg_kernel.skel.h.tmp
diff --git a/tools/testing/selftests/rdma/config b/tools/testing/selftests/rdma/config
index e1ff54ec0f57..a98ffa95b0b7 100644
--- a/tools/testing/selftests/rdma/config
+++ b/tools/testing/selftests/rdma/config
@@ -3,3 +3,6 @@ CONFIG_VETH
CONFIG_DUMMY
CONFIG_NET_NS
CONFIG_RDMA_RXE
+CONFIG_BPF=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_NETKIT=y
diff --git a/tools/testing/selftests/rdma/rxe_pkg_common.h b/tools/testing/selftests/rdma/rxe_pkg_common.h
new file mode 100644
index 000000000000..0dca4147d5f9
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_common.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/*
+ * Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
+ */
+
+// rxe_pkg_common.h
+#ifndef RXE_PKG_COMMON_H
+#define RXE_PKG_COMMON_H
+
+#define MAX_PKT_LEN 512
+
+struct pkg_event {
+ __u32 len;
+ __u8 data[MAX_PKT_LEN];
+};
+
+#endif
diff --git a/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
new file mode 100644
index 000000000000..2030529f94af
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/*
+ * Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
+ */
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <arpa/inet.h>
+
+#include "rxe_pkg_common.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 256 * 1024);/* Shared by all CPUs */
+} rb SEC(".maps");
+
+SEC("netkit/primary")
+int handle_netkit_ingress(struct __sk_buff *skb)
+{
+ /* Get the header pointer of skb */
+ void *data_end = (void *)(long)skb->data_end;
+ void *data = (void *)(long)skb->data;
+
+ struct ethhdr *eth = data;
+
+ /* Verify the data */
+ if (data + sizeof(*eth) > data_end)
+ return BPF_OK;
+
+ /* Check if it is IP skb */
+ if (eth->h_proto == bpf_htons(ETH_P_IP)) {
+ struct iphdr *iph = data + sizeof(*eth);
+
+ if ((void *)iph + sizeof(*iph) <= data_end) {
+ bpf_printk("Got IP packet from netkit ingress, %d\n", skb->len);
+
+ __u32 len = skb->len;
+
+ /* It is a rping test, the size of packets should be
+ * less than MAX_PKT_LEN
+ */
+ if (len == 0 || len > MAX_PKT_LEN) {
+ bpf_printk("len is not in 0..MAX_PKT_LEN\n");
+ return BPF_OK;
+ }
+
+ struct pkg_event *e;
+
+ /* The len is fixed to MAX_PKT_LEN */
+ e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
+
+ if (!e) {
+ bpf_printk("bpf_ringbuf_reserve fails\n");
+ return BPF_OK;
+ }
+
+ e->len = len;
+
+ if (bpf_skb_load_bytes(skb, 0, e->data, len) < 0) {
+ bpf_ringbuf_discard(e, 0);
+ bpf_printk("bpf_skb_load_bytes fails\n");
+ return BPF_OK;
+ }
+
+ bpf_ringbuf_submit(e, 0);
+ }
+ }
+
+ /* BPF_OK: Pass
+ * BPF_DROP: Drop skb
+ */
+ return BPF_OK;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/rdma/rxe_pkg_user_dump.c b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
new file mode 100644
index 000000000000..4d3fede105f7
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
@@ -0,0 +1,1388 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/*
+ * Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
+ */
+
+#include <stdio.h>
+#include <sys/resource.h>
+#include <net/if.h>
+#include "rxe_pkg_kernel.skel.h"
+
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <ctype.h>
+
+#include <net/ethernet.h>
+#include <arpa/inet.h>
+
+#include <signal.h>
+
+#include "rxe_pkg_common.h"
+
+typedef __u8 u8;
+typedef __u16 u16;
+typedef __u32 u32;
+typedef __u64 u64;
+
+struct rxe_bth {
+ __u8 opcode;
+ __u8 flags;
+ __be16 pkey;
+ __be32 qpn;
+ __be32 apsn;
+};
+
+struct rxe_immdt {
+ __be32 imm;
+};
+
+struct rxe_reth {
+ __be64 va;
+ __be32 rkey;
+ __be32 len;
+};
+
+struct rxe_aeth {
+ __be32 smsn;
+};
+
+/******************************************************************************
+ * Atomic Ack Extended Transport Header
+ ******************************************************************************/
+struct rxe_atmack {
+ __be64 orig;
+};
+
+/******************************************************************************
+ * Invalidate Extended Transport Header
+ ******************************************************************************/
+struct rxe_ieth {
+ __be32 rkey;
+};
+
+/******************************************************************************
+ * Datagram Extended Transport Header
+ ******************************************************************************/
+struct rxe_deth {
+ __be32 qkey;
+ __be32 sqp;
+};
+
+#define IB_OPCODE(transport, op) \
+ IB_OPCODE_ ## transport ## _ ## op = \
+ IB_OPCODE_ ## transport + IB_OPCODE_ ## op
+
+enum {
+ /* transport types -- just used to define real constants */
+ IB_OPCODE_RC = 0x00,
+ IB_OPCODE_UC = 0x20,
+ IB_OPCODE_RD = 0x40,
+ IB_OPCODE_UD = 0x60,
+ /* per IBTA 1.3 vol 1 Table 38, A10.3.2 */
+ IB_OPCODE_CNP = 0x80,
+ /* Manufacturer specific */
+ IB_OPCODE_MSP = 0xe0,
+
+ /* operations -- just used to define real constants */
+ IB_OPCODE_SEND_FIRST = 0x00,
+ IB_OPCODE_SEND_MIDDLE = 0x01,
+ IB_OPCODE_SEND_LAST = 0x02,
+ IB_OPCODE_SEND_LAST_WITH_IMMEDIATE = 0x03,
+ IB_OPCODE_SEND_ONLY = 0x04,
+ IB_OPCODE_SEND_ONLY_WITH_IMMEDIATE = 0x05,
+ IB_OPCODE_RDMA_WRITE_FIRST = 0x06,
+ IB_OPCODE_RDMA_WRITE_MIDDLE = 0x07,
+ IB_OPCODE_RDMA_WRITE_LAST = 0x08,
+ IB_OPCODE_RDMA_WRITE_LAST_WITH_IMMEDIATE = 0x09,
+ IB_OPCODE_RDMA_WRITE_ONLY = 0x0a,
+ IB_OPCODE_RDMA_WRITE_ONLY_WITH_IMMEDIATE = 0x0b,
+ IB_OPCODE_RDMA_READ_REQUEST = 0x0c,
+ IB_OPCODE_RDMA_READ_RESPONSE_FIRST = 0x0d,
+ IB_OPCODE_RDMA_READ_RESPONSE_MIDDLE = 0x0e,
+ IB_OPCODE_RDMA_READ_RESPONSE_LAST = 0x0f,
+ IB_OPCODE_RDMA_READ_RESPONSE_ONLY = 0x10,
+ IB_OPCODE_ACKNOWLEDGE = 0x11,
+ IB_OPCODE_ATOMIC_ACKNOWLEDGE = 0x12,
+ IB_OPCODE_COMPARE_SWAP = 0x13,
+ IB_OPCODE_FETCH_ADD = 0x14,
+ /* opcode 0x15 is reserved */
+ IB_OPCODE_SEND_LAST_WITH_INVALIDATE = 0x16,
+ IB_OPCODE_SEND_ONLY_WITH_INVALIDATE = 0x17,
+
+ /* real constants follow -- see comment about above IB_OPCODE()
+ * macro for more details
+ */
+
+ /* RC */
+ IB_OPCODE(RC, SEND_FIRST),
+ IB_OPCODE(RC, SEND_MIDDLE),
+ IB_OPCODE(RC, SEND_LAST),
+ IB_OPCODE(RC, SEND_LAST_WITH_IMMEDIATE),
+ IB_OPCODE(RC, SEND_ONLY),
+ IB_OPCODE(RC, SEND_ONLY_WITH_IMMEDIATE),
+ IB_OPCODE(RC, RDMA_WRITE_FIRST),
+ IB_OPCODE(RC, RDMA_WRITE_MIDDLE),
+ IB_OPCODE(RC, RDMA_WRITE_LAST),
+ IB_OPCODE(RC, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+ IB_OPCODE(RC, RDMA_WRITE_ONLY),
+ IB_OPCODE(RC, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+ IB_OPCODE(RC, RDMA_READ_REQUEST),
+ IB_OPCODE(RC, RDMA_READ_RESPONSE_FIRST),
+ IB_OPCODE(RC, RDMA_READ_RESPONSE_MIDDLE),
+ IB_OPCODE(RC, RDMA_READ_RESPONSE_LAST),
+ IB_OPCODE(RC, RDMA_READ_RESPONSE_ONLY),
+ IB_OPCODE(RC, ACKNOWLEDGE),
+ IB_OPCODE(RC, ATOMIC_ACKNOWLEDGE),
+ IB_OPCODE(RC, COMPARE_SWAP),
+ IB_OPCODE(RC, FETCH_ADD),
+ IB_OPCODE(RC, SEND_LAST_WITH_INVALIDATE),
+ IB_OPCODE(RC, SEND_ONLY_WITH_INVALIDATE),
+
+ /* UC */
+ IB_OPCODE(UC, SEND_FIRST),
+ IB_OPCODE(UC, SEND_MIDDLE),
+ IB_OPCODE(UC, SEND_LAST),
+ IB_OPCODE(UC, SEND_LAST_WITH_IMMEDIATE),
+ IB_OPCODE(UC, SEND_ONLY),
+ IB_OPCODE(UC, SEND_ONLY_WITH_IMMEDIATE),
+ IB_OPCODE(UC, RDMA_WRITE_FIRST),
+ IB_OPCODE(UC, RDMA_WRITE_MIDDLE),
+ IB_OPCODE(UC, RDMA_WRITE_LAST),
+ IB_OPCODE(UC, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+ IB_OPCODE(UC, RDMA_WRITE_ONLY),
+ IB_OPCODE(UC, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+
+ /* RD */
+ IB_OPCODE(RD, SEND_FIRST),
+ IB_OPCODE(RD, SEND_MIDDLE),
+ IB_OPCODE(RD, SEND_LAST),
+ IB_OPCODE(RD, SEND_LAST_WITH_IMMEDIATE),
+ IB_OPCODE(RD, SEND_ONLY),
+ IB_OPCODE(RD, SEND_ONLY_WITH_IMMEDIATE),
+ IB_OPCODE(RD, RDMA_WRITE_FIRST),
+ IB_OPCODE(RD, RDMA_WRITE_MIDDLE),
+ IB_OPCODE(RD, RDMA_WRITE_LAST),
+ IB_OPCODE(RD, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+ IB_OPCODE(RD, RDMA_WRITE_ONLY),
+ IB_OPCODE(RD, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+ IB_OPCODE(RD, RDMA_READ_REQUEST),
+ IB_OPCODE(RD, RDMA_READ_RESPONSE_FIRST),
+ IB_OPCODE(RD, RDMA_READ_RESPONSE_MIDDLE),
+ IB_OPCODE(RD, RDMA_READ_RESPONSE_LAST),
+ IB_OPCODE(RD, RDMA_READ_RESPONSE_ONLY),
+ IB_OPCODE(RD, ACKNOWLEDGE),
+ IB_OPCODE(RD, ATOMIC_ACKNOWLEDGE),
+ IB_OPCODE(RD, COMPARE_SWAP),
+ IB_OPCODE(RD, FETCH_ADD),
+
+ /* UD */
+ IB_OPCODE(UD, SEND_ONLY),
+ IB_OPCODE(UD, SEND_ONLY_WITH_IMMEDIATE)
+};
+
+enum rxe_hdr_type {
+ RXE_LRH,
+ RXE_GRH,
+ RXE_BTH,
+ RXE_RETH,
+ RXE_AETH,
+ RXE_ATMETH,
+ RXE_ATMACK,
+ RXE_IETH,
+ RXE_RDETH,
+ RXE_DETH,
+ RXE_IMMDT,
+ RXE_PAYLOAD,
+ NUM_HDR_TYPES
+};
+
+#define RXE_NUM_OPCODE 256
+
+struct rxe_opcode_info {
+ char *name;
+ int length;
+ int offset[NUM_HDR_TYPES];
+};
+
+/******************************************************************************
+ * Atomic Extended Transport Header
+ ******************************************************************************/
+struct rxe_atmeth {
+ __be64 va;
+ __be32 rkey;
+ __be64 swap_add;
+ __be64 comp;
+} __attribute__((packed));
+
+/******************************************************************************
+ * Reliable Datagram Extended Transport Header
+ ******************************************************************************/
+struct rxe_rdeth {
+ __be32 een;
+};
+
+enum rxe_hdr_length {
+ RXE_BTH_BYTES = sizeof(struct rxe_bth),
+ RXE_DETH_BYTES = sizeof(struct rxe_deth),
+ RXE_IMMDT_BYTES = sizeof(struct rxe_immdt),
+ RXE_RETH_BYTES = sizeof(struct rxe_reth),
+ RXE_AETH_BYTES = sizeof(struct rxe_aeth),
+ RXE_ATMACK_BYTES = sizeof(struct rxe_atmack),
+ RXE_ATMETH_BYTES = sizeof(struct rxe_atmeth),
+ RXE_IETH_BYTES = sizeof(struct rxe_ieth),
+ RXE_RDETH_BYTES = sizeof(struct rxe_rdeth),
+};
+
+struct rxe_opcode_info rxe_opcode[RXE_NUM_OPCODE] = {
+ [IB_OPCODE_RC_SEND_FIRST] = {
+ .name = "IB_OPCODE_RC_SEND_FIRST",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_SEND_MIDDLE] = {
+ .name = "IB_OPCODE_RC_SEND_MIDDLE",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_SEND_LAST] = {
+ .name = "IB_OPCODE_RC_SEND_LAST",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_IMMDT] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_SEND_ONLY] = {
+ .name = "IB_OPCODE_RC_SEND_ONLY",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_SEND_ONLY_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_RC_SEND_ONLY_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_IMMDT] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_WRITE_FIRST] = {
+ .name = "IB_OPCODE_RC_RDMA_WRITE_FIRST",
+ .length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_WRITE_MIDDLE] = {
+ .name = "IB_OPCODE_RC_RDMA_WRITE_MIDDLE",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_WRITE_LAST] = {
+ .name = "IB_OPCODE_RC_RDMA_WRITE_LAST",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_IMMDT] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_WRITE_ONLY] = {
+ .name = "IB_OPCODE_RC_RDMA_WRITE_ONLY",
+ .length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_WRITE_ONLY_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_RC_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RETH] = RXE_BTH_BYTES,
+ [RXE_IMMDT] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_READ_REQUEST] = {
+ .name = "IB_OPCODE_RC_RDMA_READ_REQUEST",
+ .length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST] = {
+ .name = "IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST",
+ .length = RXE_BTH_BYTES + RXE_AETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_AETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE] = {
+ .name = "IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST] = {
+ .name = "IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST",
+ .length = RXE_BTH_BYTES + RXE_AETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_AETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY] = {
+ .name = "IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY",
+ .length = RXE_BTH_BYTES + RXE_AETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_AETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_ACKNOWLEDGE] = {
+ .name = "IB_OPCODE_RC_ACKNOWLEDGE",
+ .length = RXE_BTH_BYTES + RXE_AETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_AETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE] = {
+ .name = "IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE",
+ .length = RXE_BTH_BYTES + RXE_ATMACK_BYTES + RXE_AETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_AETH] = RXE_BTH_BYTES,
+ [RXE_ATMACK] = RXE_BTH_BYTES +
+ RXE_AETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_ATMACK_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_COMPARE_SWAP] = {
+ .name = "IB_OPCODE_RC_COMPARE_SWAP",
+ .length = RXE_BTH_BYTES + RXE_ATMETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_ATMETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_ATMETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_FETCH_ADD] = {
+ .name = "IB_OPCODE_RC_FETCH_ADD",
+ .length = RXE_BTH_BYTES + RXE_ATMETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_ATMETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_ATMETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE] = {
+ .name = "IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE",
+ .length = RXE_BTH_BYTES + RXE_IETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_IETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_IETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RC_SEND_ONLY_WITH_INVALIDATE] = {
+ .name = "IB_OPCODE_RC_SEND_ONLY_INV",
+ .length = RXE_BTH_BYTES + RXE_IETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_IETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_IETH_BYTES,
+ }
+ },
+
+ /* UC */
+ [IB_OPCODE_UC_SEND_FIRST] = {
+ .name = "IB_OPCODE_UC_SEND_FIRST",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_SEND_MIDDLE] = {
+ .name = "IB_OPCODE_UC_SEND_MIDDLE",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_SEND_LAST] = {
+ .name = "IB_OPCODE_UC_SEND_LAST",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_IMMDT] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_SEND_ONLY] = {
+ .name = "IB_OPCODE_UC_SEND_ONLY",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_SEND_ONLY_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_UC_SEND_ONLY_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_IMMDT] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_RDMA_WRITE_FIRST] = {
+ .name = "IB_OPCODE_UC_RDMA_WRITE_FIRST",
+ .length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_RDMA_WRITE_MIDDLE] = {
+ .name = "IB_OPCODE_UC_RDMA_WRITE_MIDDLE",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_RDMA_WRITE_LAST] = {
+ .name = "IB_OPCODE_UC_RDMA_WRITE_LAST",
+ .length = RXE_BTH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_IMMDT] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_RDMA_WRITE_ONLY] = {
+ .name = "IB_OPCODE_UC_RDMA_WRITE_ONLY",
+ .length = RXE_BTH_BYTES + RXE_RETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES,
+ }
+ },
+ [IB_OPCODE_UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RETH] = RXE_BTH_BYTES,
+ [RXE_IMMDT] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+
+ /* RD */
+ [IB_OPCODE_RD_SEND_FIRST] = {
+ .name = "IB_OPCODE_RD_SEND_FIRST",
+ .length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_SEND_MIDDLE] = {
+ .name = "IB_OPCODE_RD_SEND_MIDDLE",
+ .length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_SEND_LAST] = {
+ .name = "IB_OPCODE_RD_SEND_LAST",
+ .length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_SEND_LAST_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_RD_SEND_LAST_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_IMMDT] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_SEND_ONLY] = {
+ .name = "IB_OPCODE_RD_SEND_ONLY",
+ .length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_SEND_ONLY_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_RD_SEND_ONLY_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_IMMDT] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_WRITE_FIRST] = {
+ .name = "IB_OPCODE_RD_RDMA_WRITE_FIRST",
+ .length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_RETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_RETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_WRITE_MIDDLE] = {
+ .name = "IB_OPCODE_RD_RDMA_WRITE_MIDDLE",
+ .length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_WRITE_LAST] = {
+ .name = "IB_OPCODE_RD_RDMA_WRITE_LAST",
+ .length = RXE_BTH_BYTES + RXE_DETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_WRITE_LAST_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_RD_RDMA_WRITE_LAST_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_IMMDT] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_WRITE_ONLY] = {
+ .name = "IB_OPCODE_RD_RDMA_WRITE_ONLY",
+ .length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_RETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_RETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_WRITE_ONLY_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_RD_RDMA_WRITE_ONLY_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_RETH_BYTES +
+ RXE_DETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_RETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_IMMDT] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_RETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_RETH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_READ_REQUEST] = {
+ .name = "IB_OPCODE_RD_RDMA_READ_REQUEST",
+ .length = RXE_BTH_BYTES + RXE_RETH_BYTES + RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_RETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_READ_RESPONSE_FIRST] = {
+ .name = "IB_OPCODE_RD_RDMA_READ_RESPONSE_FIRST",
+ .length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_AETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_READ_RESPONSE_MIDDLE] = {
+ .name = "IB_OPCODE_RD_RDMA_READ_RESPONSE_MIDDLE",
+ .length = RXE_BTH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_READ_RESPONSE_LAST] = {
+ .name = "IB_OPCODE_RD_RDMA_READ_RESPONSE_LAST",
+ .length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_AETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_RDMA_READ_RESPONSE_ONLY] = {
+ .name = "IB_OPCODE_RD_RDMA_READ_RESPONSE_ONLY",
+ .length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_AETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_ACKNOWLEDGE] = {
+ .name = "IB_OPCODE_RD_ACKNOWLEDGE",
+ .length = RXE_BTH_BYTES + RXE_AETH_BYTES + RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_AETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_ATOMIC_ACKNOWLEDGE] = {
+ .name = "IB_OPCODE_RD_ATOMIC_ACKNOWLEDGE",
+ .length = RXE_BTH_BYTES + RXE_ATMACK_BYTES + RXE_AETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_AETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_ATMACK] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_AETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_COMPARE_SWAP] = {
+ .name = "RD_COMPARE_SWAP",
+ .length = RXE_BTH_BYTES + RXE_ATMETH_BYTES + RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_ATMETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_ATMETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ }
+ },
+ [IB_OPCODE_RD_FETCH_ADD] = {
+ .name = "IB_OPCODE_RD_FETCH_ADD",
+ .length = RXE_BTH_BYTES + RXE_ATMETH_BYTES + RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_RDETH] = RXE_BTH_BYTES,
+ [RXE_DETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES,
+ [RXE_ATMETH] = RXE_BTH_BYTES +
+ RXE_RDETH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_ATMETH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_RDETH_BYTES,
+ }
+ },
+
+ /* UD */
+ [IB_OPCODE_UD_SEND_ONLY] = {
+ .name = "IB_OPCODE_UD_SEND_ONLY",
+ .length = RXE_BTH_BYTES + RXE_DETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_DETH] = RXE_BTH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_DETH_BYTES,
+ }
+ },
+ [IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE] = {
+ .name = "IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE",
+ .length = RXE_BTH_BYTES + RXE_IMMDT_BYTES + RXE_DETH_BYTES,
+ .offset = {
+ [RXE_BTH] = 0,
+ [RXE_DETH] = RXE_BTH_BYTES,
+ [RXE_IMMDT] = RXE_BTH_BYTES +
+ RXE_DETH_BYTES,
+ [RXE_PAYLOAD] = RXE_BTH_BYTES +
+ RXE_DETH_BYTES +
+ RXE_IMMDT_BYTES,
+ }
+ },
+
+};
+
+static inline u8 __bth_opcode(const void *arg)
+{
+ const struct rxe_bth *bth = arg;
+
+ return bth->opcode;
+}
+
+static inline u8 bth_opcode(const struct rxe_bth *bth)
+{
+ return __bth_opcode(bth);
+}
+
+static inline u8 bth_flags(const struct rxe_bth *bth)
+{
+ return bth->flags;
+}
+
+static inline u16 __bth_pkey(const void *arg)
+{
+ const struct rxe_bth *bth = arg;
+
+ return ntohs(bth->pkey);
+}
+
+static inline u16 bth_pkey(const struct rxe_bth *bth)
+{
+ return __bth_pkey(bth);
+}
+
+#define BTH_QPN_MASK (0x00ffffff)
+#define BTH_PSN_MASK ((1 << 24) - 1)
+
+static inline u32 __bth_qpn(const void *arg)
+{
+ const struct rxe_bth *bth = arg;
+
+ return BTH_QPN_MASK & ntohl(bth->qpn);
+}
+
+static inline u32 bth_qpn(const struct rxe_bth *bth)
+{
+ return __bth_qpn(bth);
+}
+
+static inline u32 __bth_psn(const void *arg)
+{
+ const struct rxe_bth *bth = arg;
+
+ return BTH_PSN_MASK & ntohl(bth->apsn);
+}
+
+static inline u32 bth_psn(const struct rxe_bth *bth)
+{
+ return __bth_psn(bth);
+}
+
+static inline void print_bth(const struct rxe_bth *bth)
+{
+ printf("struct rxe_bth {\n");
+ printf("\t__u8 opcode; 0x%x\n", bth_opcode(bth));
+ printf("\t__u8 flags; 0x%x\n", bth_flags(bth));
+ printf("\t__be16 pkey; 0x%x\n", bth_pkey(bth));
+ printf("\t__be32 qpn; 0x%x\n", bth_qpn(bth));
+ printf("\t__be32 apsn; 0x%x\n", bth_psn(bth));
+ printf("};\n");
+}
+
+static inline u64 reth_va(const struct rxe_reth *reth)
+{
+ return be64toh(reth->va);
+}
+
+static inline u32 reth_rkey(const struct rxe_reth *reth)
+{
+ return ntohl(reth->rkey);
+}
+
+static inline u32 reth_len(const struct rxe_reth *reth)
+{
+ return ntohl(reth->len);
+}
+
+static inline void print_reth(const struct rxe_reth *reth)
+{
+ printf("struct rxe_reth {\n");
+ printf("\t__be64 va; 0x%llx\n", reth_va(reth));
+ printf("\t__be32 rkey; 0x%x\n", reth_rkey(reth));
+ printf("\t__be32 len; 0x%x\n", reth_len(reth));
+
+ printf("};\n");
+}
+
+#define AETH_MSN_MASK (0x00ffffff)
+
+static inline u32 aeth_msn(struct rxe_aeth *aeth)
+{
+ return AETH_MSN_MASK & ntohl(aeth->smsn);
+}
+
+static inline void print_aeth(struct rxe_aeth *aeth)
+{
+ printf("struct rxe_aeth {\n");
+ printf("\t___be32 smsn; 0x%x\n", aeth_msn(aeth));
+ printf("};\n");
+}
+
+static inline u64 atmeth_va(struct rxe_atmeth *atmeth)
+{
+ return be64toh(atmeth->va);
+}
+
+static inline u32 atmeth_rkey(struct rxe_atmeth *atmeth)
+{
+ return ntohl(atmeth->rkey);
+}
+
+static inline u64 atmeth_swap_add(struct rxe_atmeth *atmeth)
+{
+ return be64toh(atmeth->swap_add);
+}
+
+static inline u64 atmeth_comp(struct rxe_atmeth *atmeth)
+{
+ return be64toh(atmeth->comp);
+}
+
+static inline void print_atmeth(struct rxe_atmeth *atmeth)
+{
+ printf("struct rxe_atmeth {\n");
+ printf("\t__be64 va; 0x%llx\n", atmeth_va(atmeth));
+ printf("\t__be32 rkey; 0x%x\n", atmeth_rkey(atmeth));
+ printf("\t__be64 swap_add; 0x%llx\n", atmeth_swap_add(atmeth));
+ printf("\t__be64 comp; 0x%llx\n", atmeth_comp(atmeth));
+ printf("} __packed;\n");
+}
+
+static inline u64 atmack_orig(struct rxe_atmack *atmack)
+{
+ return be64toh(atmack->orig);
+}
+
+static inline void print_atmack(struct rxe_atmack *atmack)
+{
+ printf("struct rxe_atmack {\n");
+ printf("\t__be64 orig; 0x%llx\n", atmack_orig(atmack));
+ printf("};\n");
+}
+
+static inline u32 ieth_rkey(struct rxe_ieth *ieth)
+{
+ return ntohl(ieth->rkey);
+}
+
+static inline void print_ieth(struct rxe_ieth *ieth)
+{
+ printf("struct rxe_ieth {\n");
+ printf("\t__be32 rkey; 0x%x\n", ieth_rkey(ieth));
+ printf("};\n");
+}
+
+#define RDETH_EEN_MASK (0x00ffffff)
+
+static inline u32 rdeth_een(struct rxe_rdeth *rdeth)
+{
+ return RDETH_EEN_MASK & ntohl(rdeth->een);
+}
+
+static inline void print_rdeth(struct rxe_rdeth *rdeth)
+{
+ printf("struct rxe_rdeth {\n");
+ printf("\t__be32 een; 0x%x\n", rdeth_een(rdeth));
+ printf("};\n");
+}
+
+#define DETH_SQP_MASK (0x00ffffff)
+
+static inline u32 deth_qkey(struct rxe_deth *deth)
+{
+ return ntohl(deth->qkey);
+}
+
+static inline u32 deth_sqp(struct rxe_deth *deth)
+{
+ return DETH_SQP_MASK & ntohl(deth->sqp);
+}
+
+static inline void print_deth(struct rxe_deth *deth)
+{
+ printf("struct rxe_deth {\n");
+ printf("\t__be32 qkey; 0x%x\n", deth_qkey(deth));
+ printf("\t__be32 sqp; 0x%x\n", deth_sqp(deth));
+ printf("};\n");
+}
+
+static inline __be32 immdt_imm(struct rxe_immdt *immdt)
+{
+ return ntohl(immdt->imm);
+}
+
+static inline void print_immdt(struct rxe_immdt *immdt)
+{
+ printf("struct rxe_immdt {\n");
+ printf("\t__be32 imm; 0x%x\n", immdt_imm(immdt));
+ printf("};\n");
+}
+
+static inline void print_ipaddr(const struct iphdr *iph)
+{
+ unsigned char *ip_addr = (unsigned char *)&iph->saddr;
+
+ printf("struct iphdr {\n");
+ printf("\t__be32 saddr; %d.%d.%d.%d\n",
+ ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
+ ip_addr = (unsigned char *)&iph->daddr;
+ printf("\t__be32 daddr; %d.%d.%d.%d\n",
+ ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
+ printf("};\n");
+}
+
+static inline void print_udphdr(const struct udphdr *udp_hdr)
+{
+ printf("struct udphdr {\n");
+ printf("\t__be16 source; %d\n", ntohs(udp_hdr->source));
+ printf("\t__be16 dest; %d\n", ntohs(udp_hdr->dest));
+ printf("\t__be16 len; %d\n", ntohs(udp_hdr->len));
+ printf("\t__sum16 check; %d\n", ntohs(udp_hdr->check));
+ printf("};\n");
+}
+
+#define ROCEV2_PORT 4791
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+static const size_t rxe_hdr_sizes[] = {
+ [RXE_RETH] = sizeof(struct rxe_reth),
+ [RXE_AETH] = sizeof(struct rxe_aeth),
+ [RXE_ATMETH] = sizeof(struct rxe_atmeth),
+ [RXE_ATMACK] = sizeof(struct rxe_atmack),
+ [RXE_IETH] = sizeof(struct rxe_ieth),
+ [RXE_RDETH] = sizeof(struct rxe_rdeth),
+ [RXE_DETH] = sizeof(struct rxe_deth),
+ [RXE_IMMDT] = sizeof(struct rxe_immdt),
+};
+
+static void print_bpf_output(const void *data, __u32 size)
+{
+ /* Validate size covers IP and UDP headers */
+ if (size < sizeof(struct iphdr) + sizeof(struct udphdr))
+ return;
+
+ const struct iphdr *iph = (const struct iphdr *)data;
+
+ if (iph->protocol != IPPROTO_UDP)
+ return;
+
+ const struct udphdr *udp_hdr = (const struct udphdr *)(data + sizeof(struct iphdr));
+
+ if (ntohs(udp_hdr->dest) != ROCEV2_PORT)
+ return;
+
+ print_ipaddr(iph);
+ printf("udp hdr:\n");
+ print_udphdr(udp_hdr);
+
+ size_t base_hdr_size = sizeof(struct iphdr) + sizeof(struct udphdr)
+ + sizeof(struct rxe_bth);
+
+ if (size < base_hdr_size)
+ return;
+
+ const struct rxe_bth *bth = (struct rxe_bth *)(data + sizeof(struct iphdr)
+ + sizeof(struct udphdr));
+
+ /* Validate opcode bounds and ensure name exists to prevent NULL dereference */
+ if (bth->opcode >= 256 || !rxe_opcode[bth->opcode].name) {
+ printf("Unknown or unpopulated opcode: 0x%x\n", bth->opcode);
+ return;
+ }
+
+ printf("%s\n", rxe_opcode[bth->opcode].name);
+ print_bth(bth);
+
+ for (int i = RXE_BTH + 1; i < NUM_HDR_TYPES; i++) {
+ int offset = rxe_opcode[bth->opcode].offset[i];
+
+ if (offset && (size >= sizeof(struct iphdr) + sizeof(struct udphdr) + offset)) {
+ const unsigned char *cur_p = (const unsigned char *)bth + offset;
+ size_t remaining_size = size - ((const void *)cur_p - data);
+
+ if (i < ARRAY_SIZE(rxe_hdr_sizes) && rxe_hdr_sizes[i] > 0) {
+ if (remaining_size < rxe_hdr_sizes[i])
+ break;
+ }
+
+ switch (i) {
+ case RXE_RETH:
+ if (remaining_size < sizeof(struct rxe_reth))
+ break;
+ printf("RXE_RETH\n");
+ print_reth((const struct rxe_reth *)cur_p);
+ break;
+
+ case RXE_AETH:
+ printf("RXE_AETH\n");
+ struct rxe_aeth *aeth = (struct rxe_aeth *)cur_p;
+
+ print_aeth(aeth);
+ break;
+
+ case RXE_ATMETH:
+ printf("RXE_ATMETH\n");
+ struct rxe_atmeth *atmeth = (struct rxe_atmeth *)cur_p;
+
+ print_atmeth(atmeth);
+ break;
+
+ case RXE_ATMACK:
+ printf("RXE_ATMACK\n");
+ struct rxe_atmack *atmack = (struct rxe_atmack *)cur_p;
+
+ print_atmack(atmack);
+ break;
+
+ case RXE_IETH:
+ printf("RXE_IETH\n");
+ struct rxe_ieth *ieth = (struct rxe_ieth *)cur_p;
+
+ print_ieth(ieth);
+ break;
+
+ case RXE_RDETH:
+ printf("RXE_RDETH\n");
+ struct rxe_rdeth *rdeth = (struct rxe_rdeth *)cur_p;
+
+ print_rdeth(rdeth);
+ break;
+
+ case RXE_DETH:
+ printf("RXE_DETH\n");
+ struct rxe_deth *deth = (struct rxe_deth *)cur_p;
+
+ print_deth(deth);
+ break;
+
+ case RXE_IMMDT:
+ printf("RXE_IMMDT\n");
+ struct rxe_immdt *immdt = (struct rxe_immdt *)cur_p;
+
+ print_immdt(immdt);
+ break;
+
+ case RXE_PAYLOAD:
+ printf("RXE_PAYLOAD\n");
+ int expected_len = (int)ntohs(udp_hdr->len)
+ - (int)sizeof(struct udphdr)
+ - rxe_opcode[bth->opcode].length;
+ int len = (expected_len < (int)remaining_size) ?
+ expected_len : (int)remaining_size;
+
+ if (len < 0)
+ len = 0;
+
+ for (int j = 0; j < len; j++) {
+ if (isprint(cur_p[j]))
+ printf("%c", cur_p[j]);
+ else
+ printf("%02x", cur_p[j]);
+ }
+ printf("\n");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ printf("\n");
+}
+
+static int handle_event(void *ctx, void *data, unsigned long data_sz)
+{
+ const struct pkg_event *e = data;
+
+ if (data_sz < sizeof(*e))
+ return -1;
+
+ if (e->len < sizeof(struct ethhdr) || e->len > sizeof(e->data))
+ return -1;
+
+ print_bpf_output(e->data + sizeof(struct ethhdr),
+ e->len - sizeof(struct ethhdr));
+
+ return 0;
+}
+
+static volatile sig_atomic_t exiting;
+
+static void sig_handler(int sig)
+{
+ exiting = 1;
+}
+
+int main(void)
+{
+ struct rxe_pkg_kernel_dump *skel;
+ int err = 1;
+ struct ring_buffer *ringbuf = NULL;
+ struct rlimit rlim = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY };
+
+ if (setrlimit(RLIMIT_MEMLOCK, &rlim)) {
+ perror("setrlimit failed");
+ return 1;
+ }
+
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+
+ skel = rxe_pkg_kernel_dump__open_and_load();
+ if (!skel) {
+ fprintf(stderr, "Failed to open and load BPF skeleton\n");
+ return 1;
+ }
+
+ int ifindex = if_nametoindex("nk0");
+
+ if (ifindex == 0) {
+ perror("if_nametoindex nk0 failed");
+ goto cleanup;
+ }
+
+ skel->links.handle_netkit_ingress = bpf_program__attach_netkit(
+ skel->progs.handle_netkit_ingress, ifindex, NULL);
+
+ if (!skel->links.handle_netkit_ingress) {
+ fprintf(stderr, "Failed to attach to netkit nk0\n");
+ goto cleanup;
+ }
+
+ ringbuf = ring_buffer__new(bpf_map__fd(skel->maps.rb),
+ handle_event, NULL, NULL);
+ if (!ringbuf) {
+ fprintf(stderr, "Failed to create ring buffer\n");
+ goto cleanup;
+ }
+
+ err = 0;
+
+ while (!exiting) {
+ err = ring_buffer__poll(ringbuf, 100 /* timeout */);
+ if (err < 0) {
+ if (err == -EINTR)
+ break;
+ fprintf(stderr, "poll error: %d\n", err);
+ break;
+ }
+ }
+
+ if (exiting)
+ err = 0;
+
+cleanup:
+ ring_buffer__free(ringbuf);
+ rxe_pkg_kernel_dump__destroy(skel);
+ return err;
+}
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v5 2/2] selftests/rdma: Add netkit-based automated test script for RXE
2026-10-31 17:30 [PATCH v5 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
2026-10-31 17:30 ` [PATCH v5 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
@ 2026-10-31 17:30 ` Zhu Yanjun
2026-10-31 17:45 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Zhu Yanjun @ 2026-10-31 17:30 UTC (permalink / raw)
To: jgg, leon, shuah, zyjzyj2000, linux-rdma, linux-kselftest,
yanjun.zhu
Add a shell script to automate functional and tracing tests for the
Soft-RoCE (RXE) driver over netkit network interfaces. The script creates
an isolated network namespace, sets up a netkit link pair (nk0/nk1),
attaches RXE links, and runs an rping client/server verification test
along with kernel tracing output collection.
Signed-off-by: Zhu Yanjun <yanjun.zhu@linux.dev>
---
.../testing/selftests/rdma/rxe_pkg_netkit.sh | 91 +++++++++++++++++++
1 file changed, 91 insertions(+)
create mode 100755 tools/testing/selftests/rdma/rxe_pkg_netkit.sh
diff --git a/tools/testing/selftests/rdma/rxe_pkg_netkit.sh b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
new file mode 100755
index 000000000000..937144cb91a6
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+# Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
+if [ "$(id -u)" -ne 0 ]; then
+ echo "SKIP: This test must be run as root"
+ exit 4
+fi
+
+for cmd in ip rdma rping; do
+ if ! command -v "$cmd" >/dev/null 2>&1; then
+ echo "SKIP: Required tool '$cmd' not found"
+ exit 4
+ fi
+done
+
+if ! modprobe -n netkit >/dev/null 2>&1 || ! modprobe -n rdma_rxe >/dev/null 2>&1; then
+ echo "SKIP: Required kernel modules (netkit or rdma_rxe) are not available"
+ exit 4
+fi
+
+exec > /dev/null
+
+set -e
+
+CUR_WD="$(dirname "$0")"
+[ "$CUR_WD" = "." ] && CUR_WD="$(pwd)"
+
+cleanup() {
+ local exit_code=$?
+
+ set +e
+ [ -n "$RPING_PID" ] && kill -9 "$RPING_PID" 2>/dev/null
+ unset RPING_PID
+
+ [ -n "$DUMP_PID" ] && kill -9 "$DUMP_PID" 2>/dev/null
+ unset DUMP_PID
+
+ ip netns exec ns0 rdma link del rxe0 2>/dev/null
+
+ rdma link del rxe1 2>/dev/null
+ ip link del nk0 2>/dev/null
+ ip netns delete ns0 2>/dev/null
+
+ exit "$exit_code"
+}
+
+trap cleanup EXIT INT TERM
+
+# Clear tracing buffer
+echo > /sys/kernel/debug/tracing/trace
+
+# Create network namespace and netkit peer links
+ip netns add ns0
+ip link add nk0 type netkit mode l2 peer name nk1
+ip link set nk0 up
+ip link set nk1 up
+
+# Configure namespace interface
+ip link set nk1 netns ns0
+ip netns exec ns0 ip addr add 10.0.0.1/24 dev nk1
+ip netns exec ns0 ip link set nk1 up
+
+# Configure host interface
+ip addr add 10.0.0.2/24 dev nk0
+
+# Start dump utility in background
+"$CUR_WD/rxe_netkit_dump" &
+DUMP_PID=$!
+
+# Add RXE links over netkit devices
+ip netns exec ns0 rdma link add rxe0 type rxe netdev nk1
+rdma link add rxe1 type rxe netdev nk0
+
+# Start rping server in namespace
+ip netns exec ns0 rping -s -a 10.0.0.1 -C 3 &
+RPING_PID=$!
+
+# Add wait time
+sleep 1
+
+# Run rping client on host
+rping -c -a 10.0.0.1 -C 3
+
+# Tear down RDMA links and netkit devices
+ip netns exec ns0 rdma link del rxe0
+rdma link del rxe1
+ip link del nk0
+ip netns delete ns0
+
+# Display kernel trace output
+cat /sys/kernel/debug/tracing/trace
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v5 2/2] selftests/rdma: Add netkit-based automated test script for RXE
2026-10-31 17:30 ` [PATCH v5 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
@ 2026-10-31 17:45 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-31 17:45 UTC (permalink / raw)
To: Zhu Yanjun, linux-rdma
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] selftests/rdma: stdout redirected to /dev/null and rxe_netkit_dump unverified
- [Medium] selftests/rdma: unchecked /sys/kernel/debug/tracing/trace access under set -e
- [Medium] selftests/rdma: double cleanup() and masked signal exit in rxe_pkg_netkit.sh
- [Medium] selftests/rdma: rxe_pkg_netkit.sh tears down links before stopping processes
--
commit f5c35de37b8753656d0f89ba368ca5c8470cec1f
Author: Zhu Yanjun <yanjun.zhu@linux.dev>
selftests/rdma: Add netkit-based automated test script for RXE
This commit adds the rxe_pkg_netkit.sh selftest script to automate functional
and tracing tests for Soft-RoCE (RXE) over netkit interfaces inside an
isolated network namespace.
> diff --git a/tools/testing/selftests/rdma/rxe_pkg_netkit.sh b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
> new file mode 100755
> index 0000000000000..937144cb91a6c
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
> @@ -0,0 +1,91 @@
[ ... ]
> +exec > /dev/null
> +
> +set -e
> +
> +CUR_WD="$(dirname "$0")"
> +[ "$CUR_WD" = "." ] && CUR_WD="$(pwd)"
> +
> +cleanup() {
> + local exit_code=$?
> +
> + set +e
> + [ -n "$RPING_PID" ] && kill -9 "$RPING_PID" 2>/dev/null
> + unset RPING_PID
> +
> + [ -n "$DUMP_PID" ] && kill -9 "$DUMP_PID" 2>/dev/null
> + unset DUMP_PID
> +
> + ip netns exec ns0 rdma link del rxe0 2>/dev/null
> +
> + rdma link del rxe1 2>/dev/null
> + ip link del nk0 2>/dev/null
> + ip netns delete ns0 2>/dev/null
> +
> + exit "$exit_code"
> +}
> +
> +trap cleanup EXIT INT TERM
[Severity: Medium]
As reported by Sashiko on v4 [1], when tools/testing/selftests/rdma/rxe_pkg_netkit.sh
receives SIGINT or SIGTERM (for example, on a kselftest timeout or user
interruption while waiting in sleep 1), cleanup() captures the status of the
previous completed command in exit_code=$?, which is 0 if that command
succeeded.
Because cleanup() then calls exit "$exit_code" without resetting the EXIT
trap via trap - EXIT, POSIX sh invokes the EXIT trap handler upon exit,
running cleanup() a second time and allowing an interrupted or timed-out test
to exit with status 0. In addition, the resource deletion commands at the end
of the script duplicate the teardown already performed by cleanup() on normal
EXIT.
Could cleanup() clear the EXIT trap before exiting and ensure signal-induced
termination exits with a non-zero status?
> +
> +# Clear tracing buffer
> +echo > /sys/kernel/debug/tracing/trace
[Severity: Medium]
As reported by Sashiko on v4 [2], tools/testing/selftests/rdma/rxe_pkg_netkit.sh
unconditionally writes to and reads from /sys/kernel/debug/tracing/trace
while set -e is active, and tools/testing/selftests/rdma/config does not
request CONFIG_TRACING or CONFIG_FTRACE.
If CONFIG_TRACING is disabled, debugfs is not mounted, or tracefs is mounted
standalone at /sys/kernel/tracing, this redirection fails under set -e and
aborts the test with exit status 1 instead of skipping with exit status 4 as
expected by kselftest when a kernel feature or mount is unavailable. Writing
to /sys/kernel/debug/tracing/trace also clears the host-wide global trace
buffer.
Should the script check that the trace file is available (or skip with exit
code 4 if unavailable) before accessing it?
> +
> +# Create network namespace and netkit peer links
> +ip netns add ns0
> +ip link add nk0 type netkit mode l2 peer name nk1
> +ip link set nk0 up
> +ip link set nk1 up
> +
> +# Configure namespace interface
> +ip link set nk1 netns ns0
> +ip netns exec ns0 ip addr add 10.0.0.1/24 dev nk1
> +ip netns exec ns0 ip link set nk1 up
> +
> +# Configure host interface
> +ip addr add 10.0.0.2/24 dev nk0
> +
> +# Start dump utility in background
> +"$CUR_WD/rxe_netkit_dump" &
> +DUMP_PID=$!
> +
> +# Add RXE links over netkit devices
> +ip netns exec ns0 rdma link add rxe0 type rxe netdev nk1
> +rdma link add rxe1 type rxe netdev nk0
> +
> +# Start rping server in namespace
> +ip netns exec ns0 rping -s -a 10.0.0.1 -C 3 &
> +RPING_PID=$!
> +
> +# Add wait time
> +sleep 1
> +
> +# Run rping client on host
> +rping -c -a 10.0.0.1 -C 3
> +
> +# Tear down RDMA links and netkit devices
> +ip netns exec ns0 rdma link del rxe0
> +rdma link del rxe1
> +ip link del nk0
> +ip netns delete ns0
[Severity: Medium]
As reported by Sashiko on v4 [3], right after rping -c returns,
tools/testing/selftests/rdma/rxe_pkg_netkit.sh deletes rxe0, rxe1, nk0, and
ns0 under set -e while the background rping -s server (RPING_PID) and
rxe_netkit_dump (DUMP_PID) processes may still be running and holding RDMA,
BPF netkit link, and network namespace resources.
When cleanup() subsequently runs, it sends SIGKILL (kill -9) to RPING_PID and
DUMP_PID without waiting for them to exit. Sending SIGKILL to DUMP_PID
bypasses sig_handler() in main() of tools/testing/selftests/rdma/rxe_pkg_user_dump.c,
preventing rxe_netkit_dump from cleanly draining ring buffer events and
calling ring_buffer__free() and rxe_pkg_kernel_dump__destroy().
Could the script wait for RPING_PID to finish and stop DUMP_PID with SIGINT
or SIGTERM (followed by wait) before deleting the RDMA links, netkit
devices, and namespace?
> +
> +# Display kernel trace output
> +cat /sys/kernel/debug/tracing/trace
[Severity: Medium]
As reported by Sashiko on v4 [4], tools/testing/selftests/rdma/rxe_pkg_netkit.sh
redirects stdout to /dev/null via exec > /dev/null at the top of the script.
Because all child processes inherit this file descriptor, all packet dump
output from rxe_netkit_dump and the output of cat /sys/kernel/debug/tracing/trace
here are discarded to /dev/null without verifying that any packets or trace
entries were captured.
In addition, "$CUR_WD/rxe_netkit_dump" & is launched in the background
without checking that the binary exists and without waiting for
rxe_pkg_kernel_dump__open_and_load() and bpf_program__attach_netkit() to
complete before starting RDMA traffic, and its exit status is never checked
via wait.
If rxe_netkit_dump is missing, fails BPF loading or netkit attachment, or
captures zero packets, won't the test still report success?
[1] https://lore.kernel.org/r/20261026215236.60054-3-yanjun.zhu@linux.dev
[2] https://lore.kernel.org/r/20261026215236.60054-3-yanjun.zhu@linux.dev
[3] https://lore.kernel.org/r/20261026215236.60054-3-yanjun.zhu@linux.dev
[4] https://lore.kernel.org/r/20261026215236.60054-3-yanjun.zhu@linux.dev
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031173017.20691-1-yanjun.zhu@linux.dev?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v5 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
2026-10-31 17:30 ` [PATCH v5 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
@ 2026-10-31 17:47 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-31 17:47 UTC (permalink / raw)
To: Zhu Yanjun, linux-rdma
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [Medium] selftests/rdma: broken BPF/UAPI include paths, arpa/inet.h, and skel rule
- [Medium] selftests/rdma: incompatible callback signature in handle_event() on 32-bit
- [Medium] selftests/rdma: print_bpf_output() includes ICRC and pad bytes in payload
- [Medium] selftests/rdma: fixed IPv4 header size and header loop order in print_bpf_output()
- [Medium] selftests/rdma: missing -mcpu=v3 in Makefile breaks BPF verification
- [Low] selftests/rdma: u64 printf format mismatch and __be32 return in immdt_imm()
- [Low] selftests/rdma: duplicated RXE tables/wrappers and hardcoded nk0 in dump tool
--
commit f654ac52160578ed80b6042ffb2064d0f8f8a7d7
Author: Zhu Yanjun <yanjun.zhu@linux.dev>
selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
This commit adds eBPF-based kernel and user-space packet dump utilities
for Soft-RoCE (RXE) traffic over netkit devices in the RDMA selftests.
> diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile
> index a91c14c45006e..f7783c20f40db 100644
> --- a/tools/testing/selftests/rdma/Makefile
> +++ b/tools/testing/selftests/rdma/Makefile
> @@ -4,6 +4,28 @@ TEST_PROGS := rxe_rping_between_netns.sh \
> rxe_socket_with_netns.sh \
> rxe_test_NETDEV_UNREGISTER.sh \
> rxe_sent_rcvd_bytes.sh \
> - rxe_netns_names.sh
> + rxe_netns_names.sh \
> + rxe_pkg_netkit.sh
>
> +TEST_GEN_PROGS_EXTENDED := rxe_netkit_dump
> include ../lib.mk
> +
> +CLANG ?= clang
> +LLC ?= llc
> +BPFTOOL ?= bpftool
> +ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/')
> +
> +INCLUDES := -I../../../../usr/include -I../../../../include/uapi -I../bpf -I$(OUTPUT)
> +CFLAGS += -g -Wall $(INCLUDES)
> +LDFLAGS += -lbpf -lelf -lz
> +
> +$(OUTPUT)/rxe_netkit_dump: rxe_pkg_user_dump.c $(OUTPUT)/rxe_pkg_kernel.skel.h
> + $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@
> +
> +$(OUTPUT)/rxe_pkg_kernel_dump.o: rxe_pkg_kernel_dump.c rxe_pkg_common.h
> + $(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) -c $< -o $@
> +
> +$(OUTPUT)/rxe_pkg_kernel.skel.h: $(OUTPUT)/rxe_pkg_kernel_dump.o
> + $(BPFTOOL) gen skeleton $< > $@
> +
> +EXTRA_CLEAN := $(OUTPUT)/rxe_pkg_kernel_dump.o $(OUTPUT)/rxe_pkg_kernel.skel.h $(OUTPUT)/rxe_pkg_kernel.skel.h.tmp
[Severity: Medium]
Could these Makefile rules and include flags cause build failures across
out-of-tree, multiarch, and -Werror environments, or leave a corrupted
skeleton header behind if bpftool fails?
In tools/testing/selftests/rdma/Makefile, INCLUDES hardcodes
-I../../../../usr/include and -I../../../../include/uapi instead of using
$(KHDR_INCLUDES) provided by ../lib.mk. Hardcoding ../../../../usr/include
breaks out-of-tree builds (O= or KBUILD_OUTPUT=), and passing the raw
unexported -I../../../../include/uapi path to userspace compilation triggers
the #warning in include/uapi/linux/types.h when __EXPORTED_HEADERS__ is not
defined (failing under USERCFLAGS=-Werror) and fails to resolve generated
<asm/types.h> and <asm/byteorder.h> headers when in-tree headers have not
been exported.
In addition, the recipe for $(OUTPUT)/rxe_pkg_kernel.skel.h redirects stdout
directly to $@:
$(BPFTOOL) gen skeleton $< > $@
If bpftool is missing or returns an error, the shell still creates an empty
$(OUTPUT)/rxe_pkg_kernel.skel.h file. Because that empty file satisfies
make's timestamp check on subsequent runs, compiling rxe_pkg_user_dump.c
then fails with undeclared skeleton symbols (even though EXTRA_CLEAN lists
$(OUTPUT)/rxe_pkg_kernel.skel.h.tmp, the rule does not write to $@.tmp and
mv it into place).
> diff --git a/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
> new file mode 100644
> index 0000000000000..2030529f94af7
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
> @@ -0,0 +1,78 @@
> +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
> +/*
> + * Copyright (c) 2026 Zhu Yanjun <yanjun.zhu@linux.dev>
> + */
> +
> +#include <linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_endian.h>
> +#include <linux/if_ether.h>
> +#include <linux/ip.h>
> +#include <arpa/inet.h>
[Severity: Medium]
Is <arpa/inet.h> needed in tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c?
Because handle_netkit_ingress() uses bpf_htons() from <bpf/bpf_endian.h>,
<arpa/inet.h> is unused. Including a userspace libc header in a translation
unit compiled with clang -target bpf drops host architecture macros (such as
__x86_64__) and multiarch include paths (/usr/include/<arch>-linux-gnu),
which causes glibc headers (<features.h>, <sys/cdefs.h>, <gnu/stubs.h>) to
fail to locate <sys/cdefs.h> on multiarch distributions or fall back to
<gnu/stubs-32.h> and fail on 64-bit hosts without 32-bit glibc development
headers installed.
[ ... ]
> +SEC("netkit/primary")
> +int handle_netkit_ingress(struct __sk_buff *skb)
> +{
> + /* Get the header pointer of skb */
> + void *data_end = (void *)(long)skb->data_end;
> + void *data = (void *)(long)skb->data;
> +
> + struct ethhdr *eth = data;
> +
> + /* Verify the data */
> + if (data + sizeof(*eth) > data_end)
> + return BPF_OK;
> +
> + /* Check if it is IP skb */
> + if (eth->h_proto == bpf_htons(ETH_P_IP)) {
> + struct iphdr *iph = data + sizeof(*eth);
> +
> + if ((void *)iph + sizeof(*iph) <= data_end) {
> + bpf_printk("Got IP packet from netkit ingress, %d\n", skb->len);
> +
> + __u32 len = skb->len;
> +
> + /* It is a rping test, the size of packets should be
> + * less than MAX_PKT_LEN
> + */
> + if (len == 0 || len > MAX_PKT_LEN) {
> + bpf_printk("len is not in 0..MAX_PKT_LEN\n");
> + return BPF_OK;
> + }
> +
> + struct pkg_event *e;
> +
> + /* The len is fixed to MAX_PKT_LEN */
> + e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
> +
> + if (!e) {
> + bpf_printk("bpf_ringbuf_reserve fails\n");
> + return BPF_OK;
> + }
> +
> + e->len = len;
> +
> + if (bpf_skb_load_bytes(skb, 0, e->data, len) < 0) {
[Severity: Medium]
Will handle_netkit_ingress() be rejected by the BPF verifier when compiled
without -mcpu=v3?
In tools/testing/selftests/rdma/Makefile, $(OUTPUT)/rxe_pkg_kernel_dump.o is
built with:
$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) -c $< -o $@
Without -mcpu=v3, clang defaults to the BPF v1 ISA, which lacks 32-bit ALU
and JMP32 instructions. At -O2, clang lowers the 32-bit check
(if (len == 0 || len > MAX_PKT_LEN)) into a 64-bit addition followed by a
64-bit shift pair to zero-extend the lower 32 bits before the conditional
jump:
r1 = r6
r1 += -1
r1 <<= 32
r1 >>= 32
if r1 > 511 goto ...
In the kernel BPF verifier, adjust_reg_min_max_vals() preserves dst_reg->id
with BPF_ADD_CONST64 across BPF_ADD and BPF_SUB, but clears dst_reg->id via
clear_scalar_id() on BPF_LSH (r1 <<= 32). Because r1->id is 0 at the
conditional branch, check_cond_jmp_op() cannot propagate the [1, 512] bounds
back to r6 (len), leaving len with its initial umin=0 and umax=U32_MAX
bounds. When bpf_skb_load_bytes(skb, 0, e->data, len) is verified,
check_mem_size_reg() sees reg_umin(size_reg) == 0 with zero_size_allowed ==
false (and reg_umax exceeding sizeof(e->data)) and rejects loading the
program with -EACCES.
Should the clang invocation in Makefile pass -mcpu=v3 (matching the other BPF
selftests)?
> diff --git a/tools/testing/selftests/rdma/rxe_pkg_user_dump.c b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
> new file mode 100644
> index 0000000000000..4d3fede105f73
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
[ ... ]
> +static inline void print_reth(const struct rxe_reth *reth)
> +{
> + printf("struct rxe_reth {\n");
> + printf("\t__be64 va; 0x%llx\n", reth_va(reth));
> + printf("\t__be32 rkey; 0x%x\n", reth_rkey(reth));
> + printf("\t__be32 len; 0x%x\n", reth_len(reth));
> +
> + printf("};\n");
> +}
[ ... ]
> +static inline void print_atmeth(struct rxe_atmeth *atmeth)
> +{
> + printf("struct rxe_atmeth {\n");
> + printf("\t__be64 va; 0x%llx\n", atmeth_va(atmeth));
> + printf("\t__be32 rkey; 0x%x\n", atmeth_rkey(atmeth));
> + printf("\t__be64 swap_add; 0x%llx\n", atmeth_swap_add(atmeth));
> + printf("\t__be64 comp; 0x%llx\n", atmeth_comp(atmeth));
> + printf("} __packed;\n");
> +}
> +
> +static inline u64 atmack_orig(struct rxe_atmack *atmack)
> +{
> + return be64toh(atmack->orig);
> +}
> +
> +static inline void print_atmack(struct rxe_atmack *atmack)
> +{
> + printf("struct rxe_atmack {\n");
> + printf("\t__be64 orig; 0x%llx\n", atmack_orig(atmack));
> + printf("};\n");
> +}
[ ... ]
> +static inline __be32 immdt_imm(struct rxe_immdt *immdt)
> +{
> + return ntohl(immdt->imm);
> +}
[Severity: Low]
Will printing u64 (__u64) values with %llx in print_reth(), print_atmeth(),
and print_atmack() produce -Wformat warnings on 64-bit architectures such as
ppc64le where __u64 in userspace is unsigned long rather than unsigned long
long? Also, should immdt_imm() return u32 instead of __be32 since it converts
immdt->imm to host byte order with ntohl()?
[ ... ]
> +static void print_bpf_output(const void *data, __u32 size)
> +{
> + /* Validate size covers IP and UDP headers */
> + if (size < sizeof(struct iphdr) + sizeof(struct udphdr))
> + return;
> +
> + const struct iphdr *iph = (const struct iphdr *)data;
> +
> + if (iph->protocol != IPPROTO_UDP)
> + return;
> +
> + const struct udphdr *udp_hdr = (const struct udphdr *)(data + sizeof(struct iphdr));
> +
> + if (ntohs(udp_hdr->dest) != ROCEV2_PORT)
> + return;
> +
> + print_ipaddr(iph);
> + printf("udp hdr:\n");
> + print_udphdr(udp_hdr);
> +
> + size_t base_hdr_size = sizeof(struct iphdr) + sizeof(struct udphdr)
> + + sizeof(struct rxe_bth);
> +
> + if (size < base_hdr_size)
> + return;
> +
> + const struct rxe_bth *bth = (struct rxe_bth *)(data + sizeof(struct iphdr)
> + + sizeof(struct udphdr));
> +
> + /* Validate opcode bounds and ensure name exists to prevent NULL dereference */
> + if (bth->opcode >= 256 || !rxe_opcode[bth->opcode].name) {
> + printf("Unknown or unpopulated opcode: 0x%x\n", bth->opcode);
> + return;
> + }
> +
> + printf("%s\n", rxe_opcode[bth->opcode].name);
> + print_bth(bth);
> +
> + for (int i = RXE_BTH + 1; i < NUM_HDR_TYPES; i++) {
> + int offset = rxe_opcode[bth->opcode].offset[i];
> +
> + if (offset && (size >= sizeof(struct iphdr) + sizeof(struct udphdr) + offset)) {
> + const unsigned char *cur_p = (const unsigned char *)bth + offset;
> + size_t remaining_size = size - ((const void *)cur_p - data);
> +
> + if (i < ARRAY_SIZE(rxe_hdr_sizes) && rxe_hdr_sizes[i] > 0) {
> + if (remaining_size < rxe_hdr_sizes[i])
> + break;
> + }
[Severity: Medium]
Does print_bpf_output() misparse packets when IPv4 options are present or
when headers appear in a different order than enum rxe_hdr_type?
First, print_bpf_output() computes the UDP and BTH offsets using
sizeof(struct iphdr) instead of validating iph->ihl >= 5 and advancing by
iph->ihl * 4.
Second, the loop iterates i from RXE_BTH + 1 to NUM_HDR_TYPES in enum
rxe_hdr_type order (where RXE_RETH = 3 precedes RXE_RDETH = 8 and
RXE_DETH = 9) rather than packet offset order. For RD opcodes such as
IB_OPCODE_RD_RDMA_WRITE_FIRST, RDETH and DETH are placed before RETH in the
packet, so breaking out of the loop when remaining_size < rxe_hdr_sizes[i]
skips headers with larger enum values that actually lie earlier in the
packet.
Third, bth->opcode is a __u8, so the bth->opcode >= 256 check is always
false.
[ ... ]
> + case RXE_PAYLOAD:
> + printf("RXE_PAYLOAD\n");
> + int expected_len = (int)ntohs(udp_hdr->len)
> + - (int)sizeof(struct udphdr)
> + - rxe_opcode[bth->opcode].length;
> + int len = (expected_len < (int)remaining_size) ?
> + expected_len : (int)remaining_size;
> +
> + if (len < 0)
> + len = 0;
> +
> + for (int j = 0; j < len; j++) {
> + if (isprint(cur_p[j]))
> + printf("%c", cur_p[j]);
> + else
> + printf("%02x", cur_p[j]);
> + }
> + printf("\n");
> + break;
[Severity: Medium]
Does the RXE_PAYLOAD calculation include the trailing BTH pad bytes and the
4-byte RoCEv2 ICRC as part of the payload?
Every RoCEv2 packet ends with 0..3 BTH pad bytes ((bth->flags & 0x30) >> 4)
and a 4-byte Invariant CRC (RXE_ICRC_SIZE, see payload_size() in
drivers/infiniband/sw/rxe/rxe_hdr.h). In addition, rxe_opcode[] sets a
non-zero .offset[RXE_PAYLOAD] even for opcodes that carry no payload, such
as IB_OPCODE_RC_ACKNOWLEDGE and IB_OPCODE_RC_RDMA_READ_REQUEST. Because
expected_len and remaining_size do not subtract RXE_ICRC_SIZE (4) and the BTH
padding length, print_bpf_output() prints 4 bytes of ICRC as RXE_PAYLOAD on
payload-less packets and appends padding and ICRC bytes to actual payloads.
> + default:
> + break;
> + }
> + }
> + }
> +
> + printf("\n");
> +}
> +
> +static int handle_event(void *ctx, void *data, unsigned long data_sz)
> +{
> + const struct pkg_event *e = data;
> +
> + if (data_sz < sizeof(*e))
> + return -1;
> +
> + if (e->len < sizeof(struct ethhdr) || e->len > sizeof(e->data))
> + return -1;
> +
> + print_bpf_output(e->data + sizeof(struct ethhdr),
> + e->len - sizeof(struct ethhdr));
> +
> + return 0;
> +}
[ ... ]
> + int ifindex = if_nametoindex("nk0");
> +
> + if (ifindex == 0) {
> + perror("if_nametoindex nk0 failed");
> + goto cleanup;
> + }
> +
> + skel->links.handle_netkit_ingress = bpf_program__attach_netkit(
> + skel->progs.handle_netkit_ingress, ifindex, NULL);
> +
> + if (!skel->links.handle_netkit_ingress) {
> + fprintf(stderr, "Failed to attach to netkit nk0\n");
> + goto cleanup;
> + }
> +
> + ringbuf = ring_buffer__new(bpf_map__fd(skel->maps.rb),
> + handle_event, NULL, NULL);
[Severity: Medium]
Will passing handle_event() to ring_buffer__new() fail to build on 32-bit
architectures?
In tools/lib/bpf/libbpf.h, ring_buffer_sample_fn is defined as:
typedef int (*ring_buffer_sample_fn)(void *ctx, void *data, size_t size);
However, handle_event() declares its third parameter as unsigned long
data_sz. On 32-bit architectures where size_t is unsigned int, passing
handle_event to ring_buffer__new() fails compilation with
-Wincompatible-pointer-types.
[Severity: Low]
This isn't a bug, but rxe_pkg_user_dump.c duplicates over 1,000 lines of
RXE header definitions, IB_OPCODE_* enums, rxe_opcode[] table entries, and
trivial wrapper functions (__bth_opcode()/bth_opcode(), etc.) from
drivers/infiniband/sw/rxe/, and rxe_opcode[] has already drifted from the
kernel table (omitting newer opcodes such as IB_OPCODE_RC_FLUSH and
IB_OPCODE_RC_ATOMIC_WRITE). In addition, main() hardcodes the interface name
"nk0" in if_nametoindex("nk0"). Could the interface name be passed via
command-line arguments (defaulting to "nk0"), and can the duplicated
definitions and wrappers in rxe_pkg_user_dump.c be trimmed or shared?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031173017.20691-1-yanjun.zhu@linux.dev?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-31 17:47 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-31 17:30 [PATCH v5 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
2026-10-31 17:30 ` [PATCH v5 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
2026-10-31 17:47 ` sashiko-bot
2026-10-31 17:30 ` [PATCH v5 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
2026-10-31 17:45 ` sashiko-bot
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.