Thread (9 messages) flat view 9 messages, 2 authors, 2026-08-17

Re: [PATCH net-next v2 4/5] selftests/xsk: add shared-UMEM callback framework and initial test cases

From: bot+bpf-ci@kernel.org
Date: 2026-08-17 21:38:43
Also in: bpf

quoted hunk ↗ jump to hunk
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
index 0bcc1aeaf4c6..f84f0edecebe 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
[ ... ]
quoted hunk ↗ jump to hunk
@@ -617,6 +624,86 @@ static int pkt_stream_even_odd_sequence(struct test_spec *test)
 	return 0;
 }

+static int pkt_stream_len_seq(struct test_spec *test, u32 short_len, u32 long_len)
+{
+	struct pkt_stream *tx_streams[MAX_SOCKETS] = {};
+	struct pkt_stream *rx_streams[MAX_SOCKETS] = {};
+	struct pkt_stream *pkt_stream;
+	u32 i;
+
+	for (i = 0; i < test->nb_sockets; i++) {
+		u32 pkt_len = i ? long_len : short_len;
+
+		pkt_stream = test->ifobj_tx->xsk_arr[i].pkt_stream;
+		tx_streams[i] = __pkt_stream_generate(pkt_stream->nb_pkts / 2, pkt_len, i, 2);
+		if (!tx_streams[i])
+			goto err;
+
+		pkt_stream = test->ifobj_rx->xsk_arr[i].pkt_stream;
+		rx_streams[i] = __pkt_stream_generate(pkt_stream->nb_pkts / 2, pkt_len, i, 2);
+		if (!rx_streams[i])
+			goto err;
+	}
+
+	for (i = 0; i < test->nb_sockets; i++) {
+		test->ifobj_tx->xsk_arr[i].pkt_stream = tx_streams[i];
+		test->ifobj_rx->xsk_arr[i].pkt_stream = rx_streams[i];
+	}
+
+	return 0;
+
+err:
+	for (i = 0; i < test->nb_sockets; i++) {
+		if (tx_streams[i])
+			pkt_stream_delete(tx_streams[i]);
+		if (rx_streams[i])
+			pkt_stream_delete(rx_streams[i]);
+	}
+
+	return -ENOMEM;
+}
+
+static int pkt_stream_uneven_dist_seq(struct test_spec *test, u32 total_pkts, u32 pkt_len)
+{
+	struct pkt_stream *tx_streams[MAX_SOCKETS] = {};
+	struct pkt_stream *rx_streams[MAX_SOCKETS] = {};
+	u32 i, pkts_sock0;
+
+	if (test->nb_sockets < 2 || total_pkts < 4)
+		return -EINVAL;
+
+	pkts_sock0 = total_pkts / 4;
+
+	for (i = 0; i < test->nb_sockets; i++) {
+		u32 nb_pkts = (i == 0) ? pkts_sock0 : (total_pkts - pkts_sock0);
+
+		tx_streams[i] = __pkt_stream_generate(nb_pkts, pkt_len, i, 2);
+		if (!tx_streams[i])
+			goto err;
+
+		rx_streams[i] = __pkt_stream_generate(nb_pkts, pkt_len, i, 2);
+		if (!rx_streams[i])
+			goto err;
+	}
+
+	for (i = 0; i < test->nb_sockets; i++) {
+		test->ifobj_tx->xsk_arr[i].pkt_stream = tx_streams[i];
+		test->ifobj_rx->xsk_arr[i].pkt_stream = rx_streams[i];
+	}
+
+	return 0;
+
+err:
+	for (i = 0; i < test->nb_sockets; i++) {
+		if (tx_streams[i])
+			pkt_stream_delete(tx_streams[i]);
+		if (rx_streams[i])
+			pkt_stream_delete(rx_streams[i]);
+	}
+
+	return -ENOMEM;
+}
+
[ ... ]
quoted hunk ↗ jump to hunk
@@ -2288,6 +2375,168 @@ int testapp_xdp_shared_umem(struct test_spec *test)
 	return ret;
 }

+static int shared_umem_test_prepare(struct test_spec *test)
+{
+	u32 i;
+
+	if (test->nb_sockets > MAX_SOCKETS) {
+		ksft_print_msg("ERROR: [%s] invalid socket count %u\n", __func__, test->nb_sockets);
+		return TEST_FAILURE;
+	}
+
+	for (i = 0; i < test->nb_sockets; i++) {
+		if (!test->ifobj_rx->xsk_arr[i].pkt_stream ||
+		    !test->ifobj_tx->xsk_arr[i].pkt_stream) {
+			ksft_print_msg("ERROR: [%s] missing stream for socket %u\n", __func__, i);
+			return TEST_FAILURE;
+		}
+	}
+
+	return TEST_PASS;
+}
+
+static int shared_umem_seq_even_odd(struct test_spec *test, const void *ctx)
+{
+	(void)ctx;
+
+	return pkt_stream_even_odd_sequence(test) ? TEST_FAILURE : TEST_PASS;
+}
+
+static int shared_umem_seq_len(struct test_spec *test, const void *ctx)
+{
+	const struct shared_umem_len_ctx *cfg = ctx;
+
+	return pkt_stream_len_seq(test, cfg->short_len, cfg->long_len) ? TEST_FAILURE : TEST_PASS;
+}
+
+static int shared_umem_seq_uneven_dist(struct test_spec *test, const void *ctx)
+{
+	const struct shared_umem_uneven_dist_ctx *cfg = ctx;
+
+	return pkt_stream_uneven_dist_seq(test, cfg->total_pkts,
+					 cfg->pkt_len) ? TEST_FAILURE : TEST_PASS;
+}
+
+static int shared_umem_post_uneven_dist(struct test_spec *test, int ret, const void *ctx)
+{
+	struct pkt_stream *tx_stream_0, *tx_stream_1;
+	struct pkt_stream *rx_stream_0, *rx_stream_1;
+
+	(void)ctx;
+
+	tx_stream_0 = test->ifobj_tx->xsk_arr[0].pkt_stream;
+	tx_stream_1 = test->ifobj_tx->xsk_arr[1].pkt_stream;
+	rx_stream_0 = test->ifobj_rx->xsk_arr[0].pkt_stream;
+	rx_stream_1 = test->ifobj_rx->xsk_arr[1].pkt_stream;
+
+	if (tx_stream_1->nb_valid_entries <= tx_stream_0->nb_valid_entries)
+		return TEST_FAILURE;
+
+	if (!ret && rx_stream_1->nb_rx_pkts <= rx_stream_0->nb_rx_pkts) {
+		ksft_print_msg("ERROR: socket1 rx_pkts (%u) not greater than socket0 (%u)\n",
+			       rx_stream_1->nb_rx_pkts, rx_stream_0->nb_rx_pkts);
+		ret = TEST_FAILURE;
+	}
+
+	return ret;
+}
Can these two checks validate anything the generic traffic path does
not already catch?

pkt_stream_uneven_dist_seq() builds socket 0 with total_pkts/4 packets
and socket 1 with the remainder, and every packet is marked valid:

    pkts_sock0 = total_pkts / 4;
    for (i = 0; i < test->nb_sockets; i++) {
        u32 nb_pkts = (i == 0) ? pkts_sock0 : (total_pkts - pkts_sock0);
        tx_streams[i] = __pkt_stream_generate(nb_pkts, pkt_len, i, 2);
        ...

With total_pkts = DEFAULT_PKT_CNT * 4 the values are fixed at 4096 vs
12288, so the first check always passes and the branch is unreachable.

For the second check, ret == TEST_PASS can only come from
testapp_validate_traffic() when the RX worker's receive_pkts() returned
0. receive_pkts() only exits the loop via all_packets_received()
returning true, which requires pkt_stream->nb_rx_pkts ==
pkt_stream->nb_valid_entries for every socket. Any other exit sets
test->fail and yields TEST_FAILURE.

So when ret == 0, rx_stream_1->nb_rx_pkts is necessarily 12288 and
rx_stream_0->nb_rx_pkts is 4096, and the comparison is always false.

The 1:3 distribution claim is asserted only by the generic per-socket
packet accounting, not by this hook. Would it be better to verify
distribution steering against something the kernel decides, such as
per-socket XDP_STATISTICS or a redirect count from the BPF program?
+
+static int run_shared_umem_test(struct test_spec *test, struct bpf_program *xdp_prog_rx,
+				struct bpf_program *xdp_prog_tx, struct bpf_map *xskmap_rx,
+				struct bpf_map *xskmap_tx, u32 nb_sockets,
+				shared_umem_seq_fn seq_fn, shared_umem_post_fn post_fn,
+				const void *ctx)
+{
+	int ret;
+
+	test->total_steps = 1;
+	test->nb_sockets = nb_sockets;
+
+	test_spec_set_xdp_prog(test, xdp_prog_rx, xdp_prog_tx, xskmap_rx, xskmap_tx);
+
+	ret = shared_umem_test_prepare(test);
+	if (ret)
+		return ret;
+
+	ret = seq_fn(test, ctx);
+	if (ret)
+		return ret;
Does this leak the streams when seq_fn() fails?

For the two callers that use shared_umem_seq_even_odd(),
testapp_shared_umem_4_sockets() and testapp_shared_umem_unaligned(), the
underlying pkt_stream_even_odd_sequence() publishes each replacement
stream into test->ifobj_{tx,rx}->xsk_arr[i].pkt_stream as soon as it is
allocated:

    for (i = 0; i < test->nb_sockets; i++) {
        tx_pkt_stream = test->ifobj_tx->xsk_arr[i].pkt_stream;
        tx_streams[i] = __pkt_stream_generate(...);
        if (!tx_streams[i]) {
            pkt_stream_delete(tx_pkt_stream);
            return -ENOMEM;
        }
        test->ifobj_tx->xsk_arr[i].pkt_stream = tx_streams[i];
        ...

If allocation fails mid-loop, the already-published streams for slots
0..(i-1) are never freed on the error path shown here, so
release_even_odd_sequence() is skipped and xsk_arr[] is left holding a
partially replaced stream set until the next __test_spec_init().

The two seq helpers added by this same commit, pkt_stream_len_seq() and
pkt_stream_uneven_dist_seq(), already use the staged temp-array + err:
rollback shape, so only the even/odd path is exposed.
+
+	ret = testapp_validate_traffic(test);
+	if (post_fn)
+		ret = post_fn(test, ret, ctx);
+
+	release_even_odd_sequence(test);
+
+	return ret;
+}
[ ... ]


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/32069138770
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help