* [PATCH v2 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE
@ 2026-10-19 21:38 Zhu Yanjun
2026-10-19 21:38 ` [PATCH v2 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
2026-10-19 21:38 ` [PATCH v2 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-19 21:38 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'
---
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 v2 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
2026-10-19 21:38 [PATCH v2 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
@ 2026-10-19 21:38 ` Zhu Yanjun
2026-10-19 21:53 ` sashiko-bot
2026-10-19 21:38 ` [PATCH v2 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-19 21:38 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 +
.../selftests/rdma/rxe_pkg_kernel_dump.c | 73 +
.../selftests/rdma/rxe_pkg_user_dump.c | 1334 +++++++++++++++++
4 files changed, 1433 insertions(+), 1 deletion(-)
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..ba225e580787 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
+ $(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
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_kernel_dump.c b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
new file mode 100644
index 000000000000..7227fc4a6f57
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
@@ -0,0 +1,73 @@
+// 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 <linux/if_ether.h>
+#include <linux/ip.h>
+#include <arpa/inet.h>
+
+#define MAX_PKT_LEN 512
+
+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 == 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;
+ }
+
+ /* The len is fixed to MAX_PKT_LEN */
+ void *reserve = bpf_ringbuf_reserve(&rb, MAX_PKT_LEN, 0);
+
+ if (!reserve) {
+ bpf_printk("bpf_ringbuf_reserve fails\n");
+ return BPF_OK;
+ }
+
+ if (bpf_skb_load_bytes(skb, 0, reserve, len) < 0) {
+ bpf_ringbuf_discard(reserve, 0);
+ bpf_printk("bpf_skb_load_bytes fails\n");
+ return BPF_OK;
+ }
+
+ bpf_ringbuf_submit(reserve, 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..b98d77b1dcf2
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
@@ -0,0 +1,1334 @@
+// 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>
+
+typedef unsigned char __u8;
+typedef unsigned short __u16;
+typedef unsigned int __u32;
+typedef unsigned long long __u64;
+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;
+};
+
+/******************************************************************************
+ * 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(void *arg)
+{
+ struct rxe_bth *bth = arg;
+
+ return bth->opcode;
+}
+
+static inline u8 bth_opcode(struct rxe_bth *bth)
+{
+ return __bth_opcode(bth);
+}
+
+static inline u8 bth_flags(struct rxe_bth *bth)
+{
+ return bth->flags;
+}
+
+static inline u16 __bth_pkey(void *arg)
+{
+ struct rxe_bth *bth = arg;
+
+ return ntohs(bth->pkey);
+}
+
+static inline u16 bth_pkey(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(void *arg)
+{
+ struct rxe_bth *bth = arg;
+
+ return BTH_QPN_MASK & ntohl(bth->qpn);
+}
+
+static inline u32 bth_qpn(struct rxe_bth *bth)
+{
+ return __bth_qpn(bth);
+}
+
+static inline u32 __bth_psn(void *arg)
+{
+ struct rxe_bth *bth = arg;
+
+ return BTH_PSN_MASK & ntohl(bth->apsn);
+}
+
+static inline u32 bth_psn(struct rxe_bth *bth)
+{
+ return __bth_psn(bth);
+}
+
+static inline void print_bth(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(struct rxe_reth *reth)
+{
+ return be64toh(reth->va);
+}
+
+static inline u32 reth_rkey(struct rxe_reth *reth)
+{
+ return ntohl(reth->rkey);
+}
+
+static inline u32 reth_len(struct rxe_reth *reth)
+{
+ return ntohl(reth->len);
+}
+
+static inline void print_reth(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(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(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
+
+static void print_bpf_output(void *data, __u32 size)
+{
+ /* Validate size covers IP and UDP headers */
+ if (size < sizeof(struct iphdr) + sizeof(struct udphdr))
+ return;
+
+ struct iphdr *iph = (struct iphdr *)data;
+
+ if (iph->protocol != IPPROTO_UDP)
+ return;
+
+ struct udphdr *udp_hdr = (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;
+
+ 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)) {
+ char *cur_p = (char *)bth + offset;
+
+ switch (i) {
+ case RXE_RETH:
+ printf("RXE_RETH\n");
+ struct rxe_reth *reth = (struct rxe_reth *)cur_p;
+
+ print_reth(reth);
+ 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 len = ntohs(udp_hdr->len) - rxe_opcode[bth->opcode].length;
+
+ 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)
+{
+ print_bpf_output(data+sizeof(struct ethhdr),
+ data_sz-sizeof(struct ethhdr));
+ return 0;
+}
+
+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;
+ }
+
+ 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 (1) {
+ err = ring_buffer__poll(ringbuf, 100 /* timeout */);
+ if (err < 0 && err != -EINTR) {
+ fprintf(stderr, "poll error\n");
+ break;
+ }
+ }
+
+cleanup:
+ ring_buffer__free(ringbuf);
+ rxe_pkg_kernel_dump__destroy(skel);
+ return 0;
+}
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] selftests/rdma: Add netkit-based automated test script for RXE
2026-10-19 21:38 [PATCH v2 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
2026-10-19 21:38 ` [PATCH v2 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
@ 2026-10-19 21:38 ` Zhu Yanjun
2026-10-19 21:53 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Zhu Yanjun @ 2026-10-19 21:38 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 | 83 +++++++++++++++++++
1 file changed, 83 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..a876bd9d57ef
--- /dev/null
+++ b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
@@ -0,0 +1,83 @@
+#!/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() {
+ set +e
+ [ -n "$DUMP_PID" ] && kill -9 "$DUMP_PID" 2>/dev/null
+ unset DUMP_PID
+
+ rdma link del rxe1 2>/dev/null
+ ip link del nk0 2>/dev/null
+ ip netns delete ns0 2>/dev/null
+
+ rm -f "$CUR_WD/rxe_netkit_dump" "$CUR_WD/rxe_pkg_kernel_dump.o" "$CUR_WD/rxe_pkg_kernel.skel.h"
+}
+
+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 &
+
+# 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 v2 2/2] selftests/rdma: Add netkit-based automated test script for RXE
2026-10-19 21:38 ` [PATCH v2 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
@ 2026-10-19 21:53 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-19 21:53 UTC (permalink / raw)
To: Zhu Yanjun, linux-rdma
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Medium] selftests/rdma: cleanup() in rxe_pkg_netkit.sh deletes compiled test binary
- [Medium] selftests/rdma: rxe_pkg_netkit.sh leaks background rping server and rxe0 link
- [Medium] selftests/rdma: rxe_pkg_netkit.sh fails if debugfs trace path is unavailable
- [Medium] selftests/rdma: rxe_pkg_netkit.sh ignores rxe_netkit_dump failure and output
- [Medium] selftests/rdma: cleanup() trap on INT/TERM resumes execution with set +e
- [Medium] selftests/rdma: overread in print_bpf_output() when dumping RXE_PAYLOAD
--
commit 6a324ce884c90ec9814616bc95190ffe879b8905
Author: Zhu Yanjun <yanjun.zhu@linux.dev>
selftests/rdma: Add netkit-based automated test script for RXE
This commit adds a shell script to automate functional and tracing tests
for the Soft-RoCE (RXE) driver over netkit network interfaces using rping
and rxe_netkit_dump.
> 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..a876bd9d57efb
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_netkit.sh
> @@ -0,0 +1,83 @@
[ ... ]
> +set -e
> +
> +CUR_WD="$(dirname "$0")"
> +[ "$CUR_WD" = "." ] && CUR_WD="$(pwd)"
> +
> +cleanup() {
> + set +e
> + [ -n "$DUMP_PID" ] && kill -9 "$DUMP_PID" 2>/dev/null
> + unset DUMP_PID
> +
> + rdma link del rxe1 2>/dev/null
> + ip link del nk0 2>/dev/null
> + ip netns delete ns0 2>/dev/null
> +
> + rm -f "$CUR_WD/rxe_netkit_dump" "$CUR_WD/rxe_pkg_kernel_dump.o" "$CUR_WD/rxe_pkg_kernel.skel.h"
[Severity: Medium]
Should cleanup() really remove rxe_netkit_dump, rxe_pkg_kernel_dump.o, and
rxe_pkg_kernel.skel.h here?
tools/testing/selftests/rdma/Makefile builds these files during compilation
and cleans them via TEST_GEN_PROGS_EXTENDED and EXTRA_CLEAN. Because
cleanup() is registered via trap on EXIT, running rxe_pkg_netkit.sh once
deletes the compiled rxe_netkit_dump binary from the test or installed
kselftest directory, causing all subsequent runs of the script to fail when
invoking "$CUR_WD/rxe_netkit_dump".
> +}
> +
> +trap cleanup EXIT INT TERM
[Severity: Medium]
When SIGINT or SIGTERM is delivered to rxe_pkg_netkit.sh (for example, on a
kselftest timeout), cleanup() runs set +e, tears down the test interfaces,
and returns without exiting the shell.
Won't the script then resume execution on the torn-down interfaces with
error checking disabled, run cleanup() a second time on EXIT, and
potentially exit with status 0 after being interrupted by a signal?
> +
> +# Clear tracing buffer
> +echo > /sys/kernel/debug/tracing/trace
[Severity: Medium]
What happens on systems where tracefs is mounted standalone at
/sys/kernel/tracing without debugfs at /sys/kernel/debug/tracing, or when
kernel tracing is not enabled in the config?
With set -e active, writing to /sys/kernel/debug/tracing/trace will abort
the script with a failure exit code rather than falling back to
/sys/kernel/tracing/trace or skipping with exit 4.
> +
> +# 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=$!
[Severity: Medium]
When rxe_pkg_netkit.sh starts rxe_netkit_dump and generates RoCEv2 rping
traffic, can print_bpf_output() in rxe_pkg_user_dump.c read past the end of
the captured packet into uninitialized ring buffer memory, or out-of-bounds
past the ring buffer record?
In tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c:
handle_netkit_ingress() reserves a fixed MAX_PKT_LEN (512) bytes in the BPF
ring buffer, but only copies len (skb->len) bytes:
void *reserve = bpf_ringbuf_reserve(&rb, MAX_PKT_LEN, 0);
...
if (bpf_skb_load_bytes(skb, 0, reserve, len) < 0) {
Then in tools/testing/selftests/rdma/rxe_pkg_user_dump.c, handle_event()
passes data_sz - sizeof(struct ethhdr) (which is always 512 - 14 = 498) to
print_bpf_output(), and the RXE_PAYLOAD handler computes:
case RXE_PAYLOAD:
printf("RXE_PAYLOAD\n");
int len = ntohs(udp_hdr->len) - rxe_opcode[bth->opcode].length;
for (int j = 0; j < len; j++) {
if (isprint(cur_p[j]))
printf("%c", cur_p[j]);
else
printf("%02x", cur_p[j]);
}
Because cur_p starts at (char *)bth + offset (after the UDP and RDMA
headers), and len does not subtract sizeof(struct udphdr) (8 bytes) or
check against the captured packet size, doesn't this loop read 8 bytes past
skb->len into uninitialized ring buffer memory on every RoCEv2 packet, and
read past the 512-byte ring buffer record when skb->len > 504?
> +
> +# 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 &
[Severity: Medium]
Does cleanup() leak the background rping server process and the rxe0 RDMA
link if the rping client fails or the script is interrupted?
Unlike DUMP_PID, the PID of this background rping -s process is not saved,
and cleanup() neither kills it nor runs:
ip netns exec ns0 rdma link del rxe0 2>/dev/null
If rping -c fails under set -e, ip netns delete ns0 in cleanup() only
unlinks the netns mount point while the orphaned rping -s process stays
alive holding ns0, nk1, rxe0, and the inherited stdout/stderr pipe file
descriptors open (which can also cause the kselftest runner to hang waiting
for pipe EOF).
> +
> +# 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
[Severity: Medium]
Will this test pass even if rxe_netkit_dump fails to start, fails to attach
its BPF program to nk0, or captures no packets?
Launching "$CUR_WD/rxe_netkit_dump" & in the background succeeds even if
the binary is missing or exits immediately (and main() in
rxe_pkg_user_dump.c returns 0 on its goto cleanup error paths). In
addition, cleanup() terminates DUMP_PID with kill -9, which prevents
rxe_netkit_dump from flushing block-buffered stdout when running under
non-interactive kselftest pipes or cleaning up its BPF resources, and the
final cat /sys/kernel/debug/tracing/trace does not check that any expected
trace events or packets were recorded.
Would it be better to verify that DUMP_PID is alive and ready before
running rping, signal it with SIGINT or SIGTERM instead of SIGKILL, and
check that the expected trace output was actually captured?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019213821.44842-1-yanjun.zhu@linux.dev?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities for RXE
2026-10-19 21:38 ` [PATCH v2 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
@ 2026-10-19 21:53 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-19 21:53 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: uninit ringbuf read and OOB payload dump in print_bpf_output()
- [Medium] selftests/rdma: missing packed attribute on struct rxe_atmeth
- [Medium] selftests/rdma: main() in rxe_pkg_user_dump.c masks errors by returning 0
- [Low] selftests/rdma: broken include paths and system bpf deps in Makefile
--
commit cad47e7d3ec5a1fbd0772ac7d820b7588ad1671d
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
(rxe_pkg_kernel_dump.c and rxe_pkg_user_dump.c) to tools/testing/selftests/rdma/
to capture and parse Soft-RoCE (RXE) traffic over netkit devices.
> diff --git a/tools/testing/selftests/rdma/Makefile b/tools/testing/selftests/rdma/Makefile
> index a91c14c45006e..ba225e580787d 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
> + $(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 $< > $@
[Severity: Low]
Will these build rules work for out-of-tree builds or on systems without a
recent system-wide libbpf and bpftool installed?
There appear to be a few issues with the build configuration here:
1. INCLUDES hardcodes -I../../../../usr/include and -I../../../../include/uapi
instead of using $(KHDR_INCLUDES) from ../lib.mk. In out-of-tree builds
(make O=...), exported headers are placed under $(O)/usr/include rather
than ../../../../usr/include. Falling back to raw unexported kernel headers
in ../../../../include/uapi lacks generated asm headers, triggers the
"Attempt to use kernel headers from user space" warning in
include/uapi/linux/types.h, and uses raw _UAPI_* header guards that
conflict with glibc headers such as net/ethernet.h. In addition, invoking
$(CLANG) -target bpf without host multiarch system include paths (such as
get_sys_includes in tools/testing/selftests/net/bpf.mk) can fail to locate
architecture headers like sys/cdefs.h, gnu/stubs.h, and asm/types.h.
2. The Makefile links against a system-installed -lbpf and invokes a system
bpftool instead of building the in-tree tools/lib/bpf and
tools/bpf/bpftool (as done in tools/testing/selftests/net/bpf.mk and
tools/testing/selftests/hid/Makefile). Note that -I../bpf does not provide
bpf/bpf_helpers.h or bpf/libbpf.h, and rxe_pkg_user_dump.c requires
recent libbpf support for bpf_program__attach_netkit() and
SEC("netkit/primary").
3. The rule for $(OUTPUT)/rxe_pkg_kernel.skel.h redirects stdout directly to
$@ without .DELETE_ON_ERROR or temporary file cleanup on failure, so a
missing or failing bpftool leaves behind an empty header file that
subsequent make invocations treat as up to date.
> +
> +EXTRA_CLEAN := $(OUTPUT)/rxe_pkg_kernel_dump.o $(OUTPUT)/rxe_pkg_kernel.skel.h
> 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..7227fc4a6f57e
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_kernel_dump.c
[ ... ]
> +SEC("netkit/primary")
> +int handle_netkit_ingress(struct __sk_buff *skb)
> +{
[ ... ]
> + __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;
> + }
> +
> + /* The len is fixed to MAX_PKT_LEN */
> + void *reserve = bpf_ringbuf_reserve(&rb, MAX_PKT_LEN, 0);
> +
> + if (!reserve) {
> + bpf_printk("bpf_ringbuf_reserve fails\n");
> + return BPF_OK;
> + }
> +
> + if (bpf_skb_load_bytes(skb, 0, reserve, len) < 0) {
> + bpf_ringbuf_discard(reserve, 0);
> + bpf_printk("bpf_skb_load_bytes fails\n");
> + return BPF_OK;
> + }
> +
> + bpf_ringbuf_submit(reserve, 0);
[Severity: Medium]
In handle_netkit_ingress(), bpf_ringbuf_reserve() always reserves MAX_PKT_LEN
(512) bytes, while bpf_skb_load_bytes() only writes len (skb->len) bytes into
reserve without zeroing the trailing MAX_PKT_LEN - len bytes or passing the
actual captured packet length to user space.
When handle_event() in rxe_pkg_user_dump.c receives this record, data_sz is
always 512 rather than skb->len, so print_bpf_output() cannot tell how many
bytes were actually initialized by bpf_skb_load_bytes() (see the comment on
print_bpf_output() below).
> + }
> + }
> +
> + /* 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 0000000000000..b98d77b1dcf2c
> --- /dev/null
> +++ b/tools/testing/selftests/rdma/rxe_pkg_user_dump.c
[ ... ]
> +/******************************************************************************
> + * Atomic Extended Transport Header
> + ******************************************************************************/
> +struct rxe_atmeth {
> + __be64 va;
> + __be32 rkey;
> + __be64 swap_add;
> + __be64 comp;
> +};
[Severity: Medium]
Should struct rxe_atmeth be marked with __attribute__((packed))?
In drivers/infiniband/sw/rxe/rxe_hdr.h, struct rxe_atmeth is defined with
__packed so that va (offset 0), rkey (offset 8), swap_add (offset 12), and
comp (offset 20) match the 28-byte RoCEv2 wire layout. Even print_atmeth()
below prints "} __packed;\n".
Without the packed attribute here, the compiler inserts 4 bytes of alignment
padding between rkey and swap_add, increasing RXE_ATMETH_BYTES from 28 to 32
bytes, shifting swap_add and comp by 4 bytes in print_atmeth(), and
miscalculating the header lengths and RXE_PAYLOAD offsets for
IB_OPCODE_RC_COMPARE_SWAP, IB_OPCODE_RC_FETCH_ADD, IB_OPCODE_RD_COMPARE_SWAP,
and IB_OPCODE_RD_FETCH_ADD.
[ ... ]
> +static void print_bpf_output(void *data, __u32 size)
> +{
[ ... ]
> + 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)) {
> + char *cur_p = (char *)bth + offset;
> +
> + switch (i) {
[ ... ]
> + case RXE_PAYLOAD:
> + printf("RXE_PAYLOAD\n");
> + int len = ntohs(udp_hdr->len) - rxe_opcode[bth->opcode].length;
> +
> + 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)
> +{
> + print_bpf_output(data+sizeof(struct ethhdr),
> + data_sz-sizeof(struct ethhdr));
> + return 0;
> +}
[Severity: Medium]
Can print_bpf_output() read uninitialized ring buffer memory or read out of
bounds past the end of data[] when dumping headers and RXE_PAYLOAD?
Looking at handle_event() -> print_bpf_output():
1. Because handle_netkit_ingress() reserves a fixed 512-byte ring buffer
entry without passing skb->len, size is always 498 (512 - sizeof(struct
ethhdr)) rather than the captured packet length. Furthermore, the loop
condition (size >= sizeof(struct iphdr) + sizeof(struct udphdr) + offset)
only checks that the start offset is within size, not that the full
extended header (offset + sizeof(header)) fits within the captured packet.
2. Under case RXE_PAYLOAD, len is computed as:
int len = ntohs(udp_hdr->len) - rxe_opcode[bth->opcode].length;
udp_hdr->len includes sizeof(struct udphdr) (8 bytes), the RoCE transport
headers, the payload, BTH padding (0..3 bytes), and the 4-byte RoCEv2
ICRC. Because sizeof(struct udphdr), padding, and the 4-byte ICRC are not
subtracted, len is at least 12 bytes larger than the actual payload (and
8 bytes larger than the entire UDP datagram). In addition,
rxe_opcode[bth->opcode].offset[RXE_PAYLOAD] is populated even for
header-only opcodes such as IB_OPCODE_RC_ACKNOWLEDGE and
IB_OPCODE_RC_RDMA_READ_REQUEST, so RXE_PAYLOAD is entered unconditionally.
3. Because len is never clamped to the remaining bytes in data[], cur_p[j]
reads at least 8 bytes of uninitialized ring buffer memory past the end of
every packet shorter than 512 bytes, and reads out of bounds past the end
of the 512-byte ring buffer record when a packet is 505..512 bytes long
(or if udp_hdr->len is larger than the buffer).
4. cur_p is a signed char *, so passing cur_p[j] directly to isprint() and
printf("%02x", cur_p[j]) without casting to unsigned char invokes
undefined behavior in isprint() for bytes >= 0x80 and sign-extends them to
8 hex digits.
> +
> +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;
> + }
> +
> + 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 (1) {
> + err = ring_buffer__poll(ringbuf, 100 /* timeout */);
> + if (err < 0 && err != -EINTR) {
> + fprintf(stderr, "poll error\n");
> + break;
> + }
> + }
> +
> +cleanup:
> + ring_buffer__free(ringbuf);
> + rxe_pkg_kernel_dump__destroy(skel);
> + return 0;
> +}
[Severity: Medium]
Should main() return a non-zero exit status on error, and handle termination
signals cleanly?
If if_nametoindex("nk0"), bpf_program__attach_netkit(), or ring_buffer__new()
fails, execution jumps to cleanup with err == 1; similarly, if
ring_buffer__poll() fails with an error other than -EINTR, the loop breaks to
cleanup with err < 0. However, the cleanup path ignores err and
unconditionally returns 0, masking failures from the calling test script.
Also, because the while (1) loop has no signal handler for SIGINT/SIGTERM and
print_bpf_output() never flushes stdout, running rxe_netkit_dump in the
background with stdout redirected to a file or pipe and killing it from a
test script will lose block-buffered stdout output and skip the cleanup
label.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019213821.44842-1-yanjun.zhu@linux.dev?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-19 21:53 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 21:38 [PATCH v2 0/2] selftests/rdma: Add an eBPF-based testcase using netkit for RXE Zhu Yanjun
2026-10-19 21:38 ` [PATCH v2 1/2] selftests/rdma: Introduce eBPF kernel and user dump utilities " Zhu Yanjun
2026-10-19 21:53 ` sashiko-bot
2026-10-19 21:38 ` [PATCH v2 2/2] selftests/rdma: Add netkit-based automated test script " Zhu Yanjun
2026-10-19 21:53 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox